UNPKG

hqchart

Version:

stock chart

52,905 lines 1.85 MB
//日志输出类
var JSConsole=
{ 
    Chart:{ Log:console.log },      //图形日志
    Complier:{ Log:console.log }    //编译器日志
};




/*
   Copyright (c) 2018 jones
 
    http://www.apache.org/licenses/LICENSE-2.0

   开源项目 https://github.com/jones2000/HQChart
 
   jones_2000@163.com

   系统指标 (H5版本)
*/


/*
    指标数据脚本 系统内置指标都写在这里
    Name:指标名字
    Description:指标描述信息
    IsMainIndex:是否是主图指标 true=主图指标 false=副图指标
    KLineType:K线设置 -1=主图不显示K线(只在主图有效) 0=在副图显示K线 1=在副图显示K线(收盘价线) 2=在副图显示K线(美国线)
    InstructionType: 1=专家指示  2=五彩K线
    FloatPrecision: 小数位数 缺省=2
    YSplitScale:  Y固定刻度 [1,8,10]
    YSpecificMaxMin: 固定Y轴最大最小值 { Max: 9, Min: 0, Count: 3 };
    StringFormat: 1=带单位万/亿 2=原始格式
    Condition: 限制条件 { Symbol:'Index'/'Stock'(只支持指数/股票),Period:[](支持的周期), }
*/

//周期条件枚举
var CONDITION_PERIOD=
{
    MINUTE_ID:101,            //分钟      走势图
    MULTIDAY_MINUTE_ID:102,   //多日分钟  走势图
    HISTORY_MINUTE_ID:103,    //历史分钟  走势图

    //K线周期
    KLINE_DAY_ID:0,
    KLINE_WEEK_ID:1,
    KLINE_MONTH_ID:2,
    KLINE_YEAR_ID:3,
    KLINE_MINUTE_ID:4,
    KLINE_5_MINUTE_ID:5,
    KLINE_15_MINUTE_ID:6,
    KLINE_30_MINUTE_ID:7,
    KLINE_60_MINUTE_ID:8

};

//自定义的指标脚本
function CustomIndexScript()
{
    this.DataMap=new Map(); //key=指标id, value=data {ID:, Name:指标名字, Description:指标描述信息 Args:参数 ......}

    this.Get=function(id)
    {
        if (!this.DataMap.has(id)) return null;
        return this.DataMap.get(id);
    }

    this.Add=function(data)
    {
        this.DataMap.set(data.ID, data);
    }
}

var g_CustomIndex=new CustomIndexScript();

function JSIndexScript()
{
    this.DataMap=new Map(
        [
            ['MA', this.MA],['均线', this.MA],['BOLL', this.BOLL],['BOLL副图', this.BOLL2],['BBI', this.BBI],
            ['DKX', this.DKX],['MIKE', this.MIKE],['PBX', this.PBX],
            ['ENE', this.ENE],['MACD', this.MACD],['KDJ', this.KDJ],
            ['VOL', this.VOL],['RSI', this.RSI],['BRAR', this.BRAR],
            ['WR', this.WR],['BIAS', this.BIAS],['OBV', this.OBV],
            ['DMI', this.DMI],['CR', this.CR],['PSY', this.PSY],
            ['CCI', this.CCI],['DMA', this.DMA],['TRIX', this.TRIX],
            ['VR', this.VR],['EMV', this.EMV],['ROC', this.ROC],
            ['MTM', this.MTM],['FSL', this.FSL],['CYR', this.CYR],
            ['MASS', this.MASS],['WAD', this.WAD],['CHO', this.CHO],
            ['ADTM', this.ADTM],['HSL', this.HSL],['BIAS36', this.BIAS36],
            ['BIAS_QL', this.BIAS_QL],['DPO', this.DPO],['OSC', this.OSC],
            ['ATR', this.ATR],['NVI', this.NVI],['PVI', this.PVI],
            ['UOS', this.UOS],['CYW', this.CYW],['LON', this.LON],
            ['NDB', this.NDB],['SKDJ',this.SKDJ],['KD',this.KD],['FKX',this.FKX],
            ['DKCOL',this.DKCOL],['UDL',this.UDL],['MFI',this.MFI],['LWR',this.LWR],
            ['MARSI',this.MARSI],['CYD',this.CYD],['CYF',this.CYF],['TAPI',this.TAPI],
            ['VMACD',this.VMACD],['QACD',this.QACD],['VPT',this.VPT],['WVAD',this.WVAD],
            ['DBQR',this.DBQR],['JS',this.JS],['CYE',this.CYE],['QR',this.QR],['GDX',this.GDX],
            ['JLHB',this.JLHB],['PCNT',this.PCNT],['BTX', this.BTX],['AMO',this.AMO],
            ['VRSI',this.VRSI],['HSCOL',this.HSCOL],['DBQRV',this.DBQRV],['DBLB',this.DBLB],
            ['ACD',this.ACD],['EXPMA',this.EXPMA],['EXPMA_S',this.EXPMA_S],['HMA',this.HMA],
            ['LMA',this.LMA],['VMA',this.VMA],['AMV',this.AMV],['BBIBOLL',this.BBIBOLL],
            ['ALLIGAT',this.ALLIGAT],['ZX',this.ZX],['XS',this.XS],['XS2',this.XS2],
            ['SG-XDT',this.SG_XDT],['SG-SMX',this.SG_SMX],['SG-LB',this.SG_LB],['SG-PF',this.SG_PF],
            ['RAD',this.RAD],['SHT',this.SHT],['ZLJC',this.ZLJC],['ZLMM',this.ZLMM],['SLZT',this.SLZT],
            ['ADVOL',this.ADVOL],['CYC',this.CYC],['CYS',this.CYS],['CYQKL',this.CYQKL],
            ['SCR',this.SCR],['ASR',this.ASR],['SAR',this.SAR],['TJCJL',this.TJCJL],['量比',this.VOLRate],
            ['平均K线',this.HeikinAshi], 
            ['EMPTY', this.EMPTY],  //什么都不显示的指标

            ['飞龙四式', this.Dragon4_Main],['飞龙四式-附图', this.Dragon4_Fig],
            ['资金分析', this.FundsAnalysis],['融资占比',this.MarginProportion],['负面新闻', this.NewsNegative],
            ['涨跌趋势', this.UpDownAnalyze],['北上资金', this.HK2SHSZ],['股东人数', this.ShareHolder],

            ['Zealink-资金吸筹', this.Zealink_Index1], ['Zealink-牛熊区间', this.Zealink_Index2],['Zealink-持仓信号', this.Zealink_Index3],
            ['Zealink-增减持',this.Zealink_Index4],['Zealink-大宗交易', this.Zealink_Index5], ['Zealink-信托持股', this.Zealink_Index6],
            ['Zealink-官网新闻', this.Zealink_Index7], ['Zealink-高管要闻', this.Zealink_Index8],['Zealink-股权质押', this.Zealink_Index9],
            ['Zealink-操盘BS点', this.Zealink_Index10],['Zealink-操盘BS点2', this.Zealink_Index11],
            
            //外包指标
            ['放心股-操盘BS点',this.FXG_BSPoint],
            ['放心股-涨停多空线',this.FXG_INDEX],
            ['放心股-涨停吸筹区',this.FXG_INDEX2],
            ['放心股-量能黄金点',this.FXG_INDEX3],

            //五彩K线(函数COLOR_开头)
            ['五彩K线-十字星',this.COLOR_KSTAR1],['五彩K线-早晨之星',this.COLOR_KSTAR2],['五彩K线-黄昏之星',this.COLOR_KSTAR3],['五彩K线-长十字',this.COLOR_SHI1],
            ['五彩K线-身怀六甲',this.COLOR_K220],['五彩K线-三个白武士',this.COLOR_K300],['五彩K线-三只乌鸦',this.COLOR_K310],['五彩K线-光头阳线',this.COLOR_K380],
            ['五彩K线-光脚阴线',this.COLOR_K390],['五彩K线-垂死十字',this.COLOR_K134],['五彩K线-早晨十字星',this.COLOR_K140],['五彩K线-黄昏十字星',this.COLOR_K150],
            ['五彩K线-射击之星',this.COLOR_K160],['五彩K线-倒转锤头',this.COLOR_K165],['五彩K线-锤头',this.COLOR_K170],['五彩K线-吊颈',this.COLOR_K180],
            ['五彩K线-穿头破脚',this.COLOR_K190],['五彩K线-出水芙蓉',this.COLOR_CSFR],['五彩K线-乌云盖顶',this.COLOR_WYGD],['五彩K线-曙光初现',this.COLOR_SGCJ],
            ['五彩K线-十字胎',this.COLOR_SZTAI],['五彩K线-剑',this.COLOR_SWORD],['五彩K线-平顶',this.COLOR_PINGDING],['五彩K线-平底',this.COLOR_PINGDI],
            ['五彩K线-大阳烛',this.COLOR_DAYANZHU],['五彩K线-大阴烛',this.COLOR_DAYINGZHU],
            
            ['五彩K线-好友反攻',this.COLOR_HYFG],['五彩K线-跳空缺口',this.COLOR_TKQK],
            ['五彩K线-双飞乌鸦',this.COLOR_SFWY],['五彩K线-上升三部曲',this.COLOR_SSSBQ],['五彩K线-下跌三部曲',this.COLOR_XDSBQ],['五彩K线-长下影',this.COLOR_CHXY],
            ['五彩K线-长上影',this.COLOR_CHSY],['五彩K线-分离',this.COLOR_FENLI],

            //交易系统
            ['交易系统-BIAS',this.TRADE_BIAS],['交易系统-CCI',this.TRADE_CCI],['交易系统-DMI',this.TRADE_DMI],['交易系统-KD',this.TRADE_KD],
            ['交易系统-BOLL',this.TRADE_BOLL],['交易系统-KDJ',this.TRADE_KDJ],['交易系统-MA',this.TRADE_MA],['交易系统-MACD',this.TRADE_MACD],
            ['交易系统-MTM',this.TRADE_MTM],['交易系统-PSY',this.TRADE_PSY],['交易系统-ROC',this.TRADE_ROC],['交易系统-RSI',this.TRADE_RSI],
            ['交易系统-VR',this.TRADE_VR],['交易系统-DPSJ',this.TRADE_DPSJ],

            ['TEST', this.TEST] //测试用
        ]);
}

JSIndexScript.AddIndex=function(aryIndex)  //添加自定义指标
{
    for(var i in aryIndex)
    {
        g_CustomIndex.Add(aryIndex[i]);
    }
}

JSIndexScript.prototype.Get=function(id)
{
    var data=g_CustomIndex.Get(id);
    if (data) return data;

    var func=this.DataMap.get(id);
    if (func) return func();

    return null;
}

JSIndexScript.prototype.Search=function(name)
{
    var result=[];
    var reg = new RegExp(name,'i');
    this.DataMap.forEach(function(value,key)
    {
        if (key.indexOf('交易系统-')!=0 && key.indexOf('五彩K线-')!=0  && key.search(reg)>=0) 
            result.push(key);
    });

    return result;
}

JSIndexScript.prototype.MA=function()
{
    let data=
    {
        Name:'MA', Description:'均线', IsMainIndex:true, StringFormat:2,
        Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10 }, { Name:'M3', Value:20} ],
        Script: //脚本
'MA1:MA(CLOSE,M1);\n\
MA2:MA(CLOSE,M2);\n\
MA3:MA(CLOSE,M3);'

    };

    return data;
}

JSIndexScript.prototype.BOLL=function()
{
    let data=
    {
        Name:'BOLL', Description:'布林线', IsMainIndex:true,
        Args:[ { Name:'M', Value:20} ],
        Script: //脚本
'BOLL:MA(CLOSE,M);\n\
UB:BOLL+2*STD(CLOSE,M);\n\
LB:BOLL-2*STD(CLOSE,M);'

    };

    return data;
}

JSIndexScript.prototype.BOLL2=function()
{
    let data=
    {
        Name:'BOLL2', Description:'布林线', IsMainIndex:false, KLineType:0,
        Args:[ { Name:'M', Value:20} ],
        Script: //脚本
'BOLL:MA(CLOSE,M);\n\
UB:BOLL+2*STD(CLOSE,M);\n\
LB:BOLL-2*STD(CLOSE,M);'

    };

    return data;
}


JSIndexScript.prototype.BBI=function()
{
    let data=
    {
        Name:'BBI', Description:'多空均线', IsMainIndex:true,
        Args:[ { Name:'M1', Value:3}, { Name:'M2', Value:6}, { Name:'M3', Value:12}, { Name:'M4', Value:24} ],
        Script: //脚本
        'BBI:(MA(CLOSE,M1)+MA(CLOSE,M2)+MA(CLOSE,M3)+MA(CLOSE,M4))/4;'

    };

    return data;
}


JSIndexScript.prototype.DKX=function()
{
    let data=
    {
        Name:'DKX', Description:'多空线', IsMainIndex:false,
        Args:[ { Name:'M', Value:10} ],
        Script: //脚本
'MID:=(3*CLOSE+LOW+OPEN+HIGH)/6;\n\
DKX:(20*MID+19*REF(MID,1)+18*REF(MID,2)+17*REF(MID,3)+\n\
16*REF(MID,4)+15*REF(MID,5)+14*REF(MID,6)+\n\
13*REF(MID,7)+12*REF(MID,8)+11*REF(MID,9)+\n\
10*REF(MID,10)+9*REF(MID,11)+8*REF(MID,12)+\n\
7*REF(MID,13)+6*REF(MID,14)+5*REF(MID,15)+\n\
4*REF(MID,16)+3*REF(MID,17)+2*REF(MID,18)+REF(MID,20))/210;\n\
MADKX:MA(DKX,M);'

    };

    return data;
}

JSIndexScript.prototype.MIKE=function()
{
    let data=
    {
        Name:'MIKE', Description:'麦克支撑压力', IsMainIndex:true,
        Args:[ { Name:'N', Value:10} ],
        Script: //脚本
'HLC:=REF(MA((HIGH+LOW+CLOSE)/3,N),1);\n\
HV:=EMA(HHV(HIGH,N),3);\n\
LV:=EMA(LLV(LOW,N),3);\n\
STOR:EMA(2*HV-LV,3);\n\
MIDR:EMA(HLC+HV-LV,3);\n\
WEKR:EMA(HLC*2-LV,3);\n\
WEKS:EMA(HLC*2-HV,3);\n\
MIDS:EMA(HLC-HV+LV,3);\n\
STOS:EMA(2*LV-HV,3);'

    };

    return data;
}

JSIndexScript.prototype.PBX=function()
{
    let data=
    {
        Name:'PBX', Description:'瀑布线', IsMainIndex:true,
        Args:[ { Name:'M1', Value:4}, { Name:'M2', Value:6}, { Name:'M3', Value:9}, { Name:'M4', Value:13},{ Name:'M5', Value:18},{ Name:'M6', Value:24} ],
        Script: //脚本
'PBX1:(EMA(CLOSE,M1)+MA(CLOSE,M1*2)+MA(CLOSE,M1*4))/3;\n\
PBX2:(EMA(CLOSE,M2)+MA(CLOSE,M2*2)+MA(CLOSE,M2*4))/3;\n\
PBX3:(EMA(CLOSE,M3)+MA(CLOSE,M3*2)+MA(CLOSE,M3*4))/3;\n\
PBX4:(EMA(CLOSE,M4)+MA(CLOSE,M4*2)+MA(CLOSE,M4*4))/3;\n\
PBX5:(EMA(CLOSE,M5)+MA(CLOSE,M5*2)+MA(CLOSE,M5*4))/3;\n\
PBX6:(EMA(CLOSE,M6)+MA(CLOSE,M6*2)+MA(CLOSE,M6*4))/3;'

    };

    return data;
}

JSIndexScript.prototype.ENE=function()
{
    let data=
    {
        Name:'ENE', Description:'轨道线', IsMainIndex:true,
        Args:[ { Name:'N', Value:25}, { Name:'M1', Value:6}, { Name:'M2', Value:6} ],
        Script: //脚本
'UPPER:(1+M1/100)*MA(CLOSE,N);\n\
LOWER:(1-M2/100)*MA(CLOSE,N);\n\
ENE:(UPPER+LOWER)/2;'

    };

    return data;
}

JSIndexScript.prototype.MACD=function()
{
    let data=
    {
        Name:'MACD', Description:'平滑异同平均', IsMainIndex:false,
        Args:[ { Name:'SHORT', Value:12}, { Name:'LONG', Value:26}, { Name:'MID', Value:9} ],
        Script: //脚本
'DIF:EMA(CLOSE,SHORT)-EMA(CLOSE,LONG);\n\
DEA:EMA(DIF,MID);\n\
MACD:(DIF-DEA)*2,COLORSTICK;'

    };

    return data;
}

JSIndexScript.prototype.KDJ=function()
{
    let data=
    {
        Name:'KDJ', Description:'随机指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:9}, { Name:'M1', Value:3}, { Name:'M2', Value:3} ],
        Script: //脚本
'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\
K:SMA(RSV,M1,1);\n\
D:SMA(K,M2,1);\n\
J:3*K-2*D;'

    };

    return data;
}

JSIndexScript.prototype.VOL=function()
{
    let data=
    {
        Name:'VOL', Description:'成交量', IsMainIndex:false,FloatPrecision:0,
        Args:[ { Name:'M1', Value:5}, { Name:'M2', Value:10} ],
        Script: //脚本
'VOL:VOL,VOLSTICK;\n\
MA1:MA(VOL,M1);\n\
MA2:MA(VOL,M2);'

    };

    return data;
}

JSIndexScript.prototype.RSI=function()
{
    let data=
    {
        Name:'RSI', Description:'相对强弱指标', IsMainIndex:false,
        Args:[ { Name:'N1', Value:6}, { Name:'N2', Value:12}, { Name:'N3', Value:24}  ],
        Script: //脚本
'LC:=REF(CLOSE,1);\n\
RSI1:SMA(MAX(CLOSE-LC,0),N1,1)/SMA(ABS(CLOSE-LC),N1,1)*100;\n\
RSI2:SMA(MAX(CLOSE-LC,0),N2,1)/SMA(ABS(CLOSE-LC),N2,1)*100;\n\
RSI3:SMA(MAX(CLOSE-LC,0),N3,1)/SMA(ABS(CLOSE-LC),N3,1)*100;'

    };

    return data;
}

JSIndexScript.prototype.BRAR=function()
{
    let data=
    {
        Name:'BRAR', Description:'情绪指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:26} ],
        Script: //脚本
'BR:SUM(MAX(0,HIGH-REF(CLOSE,1)),N)/SUM(MAX(0,REF(CLOSE,1)-LOW),N)*100;\n\
AR:SUM(HIGH-OPEN,N)/SUM(OPEN-LOW,N)*100;'

    };

    return data;
}

JSIndexScript.prototype.WR=function()
{
    let data=
    {
        Name:'WR', Description:'威廉指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:10}, { Name:'N1', Value:6} ],
        Script: //脚本
'WR1:100*(HHV(HIGH,N)-CLOSE)/(HHV(HIGH,N)-LLV(LOW,N));\n\
WR2:100*(HHV(HIGH,N1)-CLOSE)/(HHV(HIGH,N1)-LLV(LOW,N1));'

    };

    return data;
}

JSIndexScript.prototype.BIAS=function()
{
    let data=
    {
        Name:'BIAS', Description:'乖离率', IsMainIndex:false,
        Args:[ { Name:'N1', Value:6}, { Name:'N2', Value:12}, { Name:'N3', Value:24} ],
        Script: //脚本
'BIAS1 :(CLOSE-MA(CLOSE,N1))/MA(CLOSE,N1)*100;\n\
BIAS2 :(CLOSE-MA(CLOSE,N2))/MA(CLOSE,N2)*100;\n\
BIAS3 :(CLOSE-MA(CLOSE,N3))/MA(CLOSE,N3)*100;'

    };

    return data;
}

JSIndexScript.prototype.OBV=function()
{
    let data=
    {
        Name:'OBV', Description:'累积能量线', IsMainIndex:false,
        Args:[ { Name:'M', Value:30} ],
        Script: //脚本
'VA:=IF(CLOSE>REF(CLOSE,1),VOL,-VOL);\n\
OBV:SUM(IF(CLOSE==REF(CLOSE,1),0,VA),0);\n\
MAOBV:MA(OBV,M);'

    };

    return data;
}

JSIndexScript.prototype.DMI=function()
{
    let data=
    {
        Name:'DMI', Description:'趋向指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:14}, { Name:'MM', Value:6} ],
        Script: //脚本
'MTR:=EXPMEMA(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(REF(CLOSE,1)-LOW)),N);\n\
HD :=HIGH-REF(HIGH,1);\n\
LD :=REF(LOW,1)-LOW;\n\
DMP:=EXPMEMA(IF(HD>0&&HD>LD,HD,0),N);\n\
DMM:=EXPMEMA(IF(LD>0&&LD>HD,LD,0),N);\n\
PDI: DMP*100/MTR;\n\
MDI: DMM*100/MTR;\n\
ADX: EXPMEMA(ABS(MDI-PDI)/(MDI+PDI)*100,MM);\n\
ADXR:EXPMEMA(ADX,MM);'

    };

    return data;
}

JSIndexScript.prototype.CR=function()
{
    let data=
    {
        Name:'CR', Description:'带状能量线', IsMainIndex:false,
        Args:[ { Name:'N', Value:26}, { Name:'M1', Value:10},{ Name:'M2', Value:20},{ Name:'M3', Value:40},{ Name:'M4', Value:62} ],
        Script: //脚本
'MID:=REF(HIGH+LOW,1)/2;\n\
CR:SUM(MAX(0,HIGH-MID),N)/SUM(MAX(0,MID-LOW),N)*100;\n\
MA1:REF(MA(CR,M1),M1/2.5+1);\n\
MA2:REF(MA(CR,M2),M2/2.5+1);\n\
MA3:REF(MA(CR,M3),M3/2.5+1);\n\
MA4:REF(MA(CR,M4),M4/2.5+1);'

    };

    return data;
}

JSIndexScript.prototype.PSY=function()
{
    let data=
    {
        Name:'PSY', Description:'心理线', IsMainIndex:false,
        Args:[ { Name:'N', Value:12}, { Name:'M', Value:6} ],
        Script: //脚本
'PSY:COUNT(CLOSE>REF(CLOSE,1),N)/N*100;\r\
PSYMA:MA(PSY,M);'

    };

    return data;
}

JSIndexScript.prototype.CCI=function()
{
    let data=
    {
        Name:'CCI', Description:'商品路径指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:14} ],
        Script: //脚本
'TYP:=(HIGH+LOW+CLOSE)/3;\n\
CCI:(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));'

    };

    return data;
}

JSIndexScript.prototype.DMA=function()
{
    let data=
    {
        Name:'DMA', Description:'平均差', IsMainIndex:false,
        Args:[ { Name:'N1', Value:10},{ Name:'N2', Value:50},{ Name:'M', Value:10} ],
        Script: //脚本
'DIF:MA(CLOSE,N1)-MA(CLOSE,N2);\n\
DIFMA:MA(DIF,M);'

    };

    return data;
}

JSIndexScript.prototype.TRIX=function()
{
    let data=
    {
        Name:'TRIX', Description:'三重指数平均线', IsMainIndex:false,
        Args:[ { Name:'N', Value:12},{ Name:'M', Value:9} ],
        Script: //脚本
'MTR:=EMA(EMA(EMA(CLOSE,N),N),N);\n\
TRIX:(MTR-REF(MTR,1))/REF(MTR,1)*100;\n\
MATRIX:MA(TRIX,M) ;'

    };

    return data;
}

JSIndexScript.prototype.VR=function()
{
    let data=
    {
        Name:'VR', Description:'成交量变异率', IsMainIndex:false,
        Args:[ { Name:'N', Value:26},{ Name:'M', Value:6} ],
        Script: //脚本
'TH:=SUM(IF(CLOSE>REF(CLOSE,1),VOL,0),N);\n\
TL:=SUM(IF(CLOSE<REF(CLOSE,1),VOL,0),N);\n\
TQ:=SUM(IF(CLOSE==REF(CLOSE,1),VOL,0),N);\n\
VR:100*(TH*2+TQ)/(TL*2+TQ);\n\
MAVR:MA(VR,M);'

    };

    return data;
}

JSIndexScript.prototype.EMV=function()
{
    let data=
    {
        Name:'EMV', Description:'简易波动指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:14},{ Name:'M', Value:9} ],
        Script: //脚本
'VOLUME:=MA(VOL,N)/VOL;\n\
MID:=100*(HIGH+LOW-REF(HIGH+LOW,1))/(HIGH+LOW);\n\
EMV:MA(MID*VOLUME*(HIGH-LOW)/MA(HIGH-LOW,N),N);\n\
MAEMV:MA(EMV,M);'

    };

    return data;
}

JSIndexScript.prototype.ROC=function()
{
    let data=
    {
        Name:'ROC', Description:'变动率指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:12},{ Name:'M', Value:6} ],
        Script: //脚本
'ROC:100*(CLOSE-REF(CLOSE,N))/REF(CLOSE,N);\n\
MAROC:MA(ROC,M);'

    };

    return data;
}

JSIndexScript.prototype.MTM=function()
{
    let data=
    {
        Name:'MTM', Description:'动量线', IsMainIndex:false,
        Args:[ { Name:'N', Value:12},{ Name:'M', Value:6} ],
        Script: //脚本
'MTM:CLOSE-REF(CLOSE,N);\n\
MAMTM:MA(MTM,M);'

    };

    return data;
}

JSIndexScript.prototype.FSL=function()
{
    let data=
    {
        Name:'FSL', Description:'分水岭', IsMainIndex:false,
        Args:[ ],
        Script: //脚本
'SWL:(EMA(CLOSE,5)*7+EMA(CLOSE,10)*3)/10;\n\
SWS:DMA(EMA(CLOSE,12),MAX(1,100*(SUM(VOL,5)/(3*CAPITAL))));'

    };

    return data;
}

JSIndexScript.prototype.CYR=function()
{
    let data=
    {
        Name:'CYR', Description:'市场强弱', IsMainIndex:false,
        Args:[ { Name:'N', Value:13},{ Name:'M', Value:5}],
        Script: //脚本
'DIVE:=0.01*EMA(AMOUNT,N)/EMA(VOL,N);\n\
CYR:(DIVE/REF(DIVE,1)-1)*100;\n\
MACYR:MA(CYR,M);'

    };

    return data;
}

JSIndexScript.prototype.MASS=function()
{
    let data=
    {
        Name:'MASS', Description:'市场强弱', IsMainIndex:false,
        Args:[ { Name:'N1', Value:9},{ Name:'N2', Value:25}, { Name:'M', Value:6}],
        Script: //脚本
'MASS:SUM(MA(HIGH-LOW,N1)/MA(MA(HIGH-LOW,N1),N1),N2);\n\
MAMASS:MA(MASS,M);'

    };

    return data;
}

JSIndexScript.prototype.WAD=function()
{
    let data=
    {
        Name:'WAD', Description:'威廉多空力度线', IsMainIndex:false,
        Args:[ { Name:'M', Value:30}],
        Script: //脚本
'MIDA:=CLOSE-MIN(REF(CLOSE,1),LOW);\n\
MIDB:=IF(CLOSE<REF(CLOSE,1),CLOSE-MAX(REF(CLOSE,1),HIGH),0);\n\
WAD:SUM(IF(CLOSE>REF(CLOSE,1),MIDA,MIDB),0);\n\
MAWAD:MA(WAD,M);'

    };

    return data;
}

JSIndexScript.prototype.CHO=function()
{
    let data=
    {
        Name:'CHO', Description:'佳庆指标', IsMainIndex:false,
        Args:[ { Name:'N1', Value:10}, { Name:'N2', Value:20}, { Name:'M', Value:6}],
        Script: //脚本
'MID:=SUM(VOL*(2*CLOSE-HIGH-LOW)/(HIGH+LOW),0);\n\
CHO:MA(MID,N1)-MA(MID,N2);\n\
MACHO:MA(CHO,M);'

    };

    return data;
}

JSIndexScript.prototype.ADTM=function()
{
    let data=
    {
        Name:'ADTM', Description:'动态买卖气指标', IsMainIndex:false,
        Args:[ { Name:'N', Value:23}, { Name:'M', Value:8}],
        Script: //脚本
'DTM:=IF(OPEN<=REF(OPEN,1),0,MAX((HIGH-OPEN),(OPEN-REF(OPEN,1))));\n\
DBM:=IF(OPEN>=REF(OPEN,1),0,MAX((OPEN-LOW),(OPEN-REF(OPEN,1))));\n\
STM:=SUM(DTM,N);\n\
SBM:=SUM(DBM,N);\n\
ADTM:IF(STM>SBM,(STM-SBM)/STM,IF(STM==SBM,0,(STM-SBM)/SBM));\n\
MAADTM:MA(ADTM,M);'

    };

    return data;
}

JSIndexScript.prototype.HSL=function()
{
    let data=
    {
        Name:'HSL', Description:'换手线', IsMainIndex:false,
        Args:[ { Name:'N', Value:5} ],
        Script: //脚本
'HSL:IF((SETCODE==0||SETCODE==1),100*VOL,VOL)/(FINANCE(7)/100);\n\
MAHSL:MA(HSL,N);'

    };

    return data;
}

JSIndexScript.prototype.BIAS36=function()
{
    let data=
    {
        Name:'BIAS36', Description:'三六乖离', IsMainIndex:false,
        Args:[ { Name:'M', Value:6} ],
        Script: //脚本
'BIAS36:MA(CLOSE,3)-MA(CLOSE,6);\n\
BIAS612:MA(CLOSE,6)-MA(CLOSE,12);\n\
MABIAS:MA(BIAS36,M);'

    };

    return data;
}

JSIndexScript.prototype.BIAS_QL=function()
{
    let data=
    {
        Name:'BIAS_QL', Description:'乖离率-传统版', IsMainIndex:false,
        Args:[ { Name:'N', Value:6}, { Name:'M', Value:6} ],
        Script: //脚本
'BIAS :(CLOSE-MA(CLOSE,N))/MA(CLOSE,N)*100;\n\
BIASMA :MA(BIAS,M);'

    };

    return data;
}

JSIndexScript.prototype.DPO=function()
{
    let data=
    {
        Name:'DPO', Description:'区间震荡线', IsMainIndex:false,
        Args:[ { Name:'N', Value:20}, { Name:'M', Value:6} ],
        Script: //脚本
'DPO:CLOSE-REF(MA(CLOSE,N),N/2+1);\n\
MADPO:MA(DPO,M);'

    };

    return data;
}

JSIndexScript.prototype.OSC=function()
{
    let data=
    {
        Name:'OSC', Description:'变动速率线', IsMainIndex:false,
        Args:[ { Name:'N', Value:20}, { Name:'M', Value:6} ],
        Script: //脚本
'OSC:100*(CLOSE-MA(CLOSE,N));\n\
MAOSC:EXPMEMA(OSC,M);'

    };

    return data;
}

JSIndexScript.prototype.ATR=function()
{
    let data=
    {
        Name:'ATR', Description:'真实波幅', IsMainIndex:false,
        Args:[ { Name:'N', Value:14}],
        Script: //脚本
'MTR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));\n\
ATR:MA(MTR,N);'

    };

    return data;
}

JSIndexScript.prototype.NVI=function()
{
    let data=
    {
        Name:'ATR', Description:'负成交量', IsMainIndex:false,
        Args:[ { Name:'N', Value:72} ],
        Script: //脚本
'NVI:100*MULAR(IF(V<REF(V,1),C/REF(C,1),1),0);\n\
MANVI:MA(NVI,N);'

    };

    return data;
}

JSIndexScript.prototype.PVI=function()
{
    let data=
    {
        Name:'PVI', Description:'正成交量', IsMainIndex:false,
        Args:[ { Name:'N', Value:72} ],
        Script: //脚本
'NVI:100*MULAR(IF(V>REF(V,1),C/REF(C,1),1),0);\n\
MANVI:MA(NVI,N);'

    };

    return data;
}

JSIndexScript.prototype.UOS=function()
{
    let data=
    {
        Name:'UOS', Description:'终极指标', IsMainIndex:false,
        Args:[ { Name:'N1', Value:7} ,{ Name:'N2', Value:14},{ Name:'N3', Value:28},{ Name:'M', Value:6}],
        Script: //脚本
'TH:=MAX(HIGH,REF(CLOSE,1));\n\
TL:=MIN(LOW,REF(CLOSE,1));\n\
ACC1:=SUM(CLOSE-TL,N1)/SUM(TH-TL,N1);\n\
ACC2:=SUM(CLOSE-TL,N2)/SUM(TH-TL,N2);\n\
ACC3:=SUM(CLOSE-TL,N3)/SUM(TH-TL,N3);\n\
UOS:(ACC1*N2*N3+ACC2*N1*N3+ACC3*N1*N2)*100/(N1*N2+N1*N3+N2*N3);\n\
MAUOS:EXPMEMA(UOS,M);'

    };

    return data;
}

JSIndexScript.prototype.CYW=function()
{
    let data=
    {
        Name:'CYW', Description:'主力控盘', IsMainIndex:false,
        Args:[ ],
        Script: //脚本
'VAR1:=CLOSE-LOW;\n\
VAR2:=HIGH-LOW;\n\
VAR3:=CLOSE-HIGH;\n\
VAR4:=IF(HIGH>LOW,(VAR1/VAR2+VAR3/VAR2)*VOL,0);\n\
CYW: SUM(VAR4,10)/10000, COLORSTICK;'

    };

    return data;
}

JSIndexScript.prototype.LON=function()
{
    let data=
    {
        Name:'LON', Description:'龙系长线', IsMainIndex:false,
        Args:[ { Name:'N', Value:10} ],
        Script: //脚本
'LC := REF(CLOSE,1);\n\
VID := SUM(VOL,2)/(((HHV(HIGH,2)-LLV(LOW,2)))*100);\n\
RC := (CLOSE-LC)*VID;\n\
LONG := SUM(RC,0);\n\
DIFF := SMA(LONG,10,1);\n\
DEA := SMA(LONG,20,1);\n\
LON : DIFF-DEA;\n\
LONMA : MA(LON,10);\n\
LONT : LON, COLORSTICK;'

    };

    return data;
}

JSIndexScript.prototype.NDB = function () 
{
    let data =
    {
        Name: 'NDB', Description: '脑电波', IsMainIndex: false,
        Args: [{ Name: 'P1', Value: 5 }, { Name: 'P2', Value: 10 }],
        Script: //脚本
'HH:=IF(C/REF(C,1)>1.098 AND L>REF(H,1),2*C-REF(C,1)-H,2*C-H-L);\n\
V1:= BARSCOUNT(C) - 1;\n\
V2:= 2 * REF(C, V1) - REF(H, V1) - REF(L, V1);\n\
DK: SUM(HH, 0) + V2;\n\
MDK5: MA(DK, P1);\n\
MDK10: MA(DK, P2);'

    };

    return data;
}

JSIndexScript.prototype.SKDJ = function () 
{
    let data =
    {
        Name: 'SKDJ', Description: '慢速随机指标', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 9 }, { Name: 'M', Value: 3 }],
        Script: //脚本
'LOWV:=LLV(LOW,N);\n\
HIGHV:=HHV(HIGH,N);\n\
RSV:=EMA((CLOSE-LOWV)/(HIGHV-LOWV)*100,M);\n\
K:EMA(RSV,M);\n\
D:MA(K,M);'

    };

    return data;
}

JSIndexScript.prototype.KD = function () 
{
    let data =
    {
        Name: 'KD', Description: '随机指标KD', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 9 }, { Name: 'M1', Value: 3 },{ Name: 'M2', Value: 3 }],
        Script: //脚本
'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\
K:SMA(RSV,M1,1);\n\
D:SMA(K,M2,1);'

    };

    return data;
}

JSIndexScript.prototype.FKX=function()
{
    let data=
    {
        Name:'FKX', Description:'反K线', IsMainIndex:true,
        Args:[  ],
        Script: //脚本
            'DRAWKLINE(-LOW, -OPEN, -HIGH, -CLOSE);'
    };

    return data;
}

JSIndexScript.prototype.DKCOL = function () 
{
    let data =
    {
        Name: 'DKCOL', Description: '多空能量柱(适用于分时主图)', IsMainIndex: true,
        Args: [{ Name: 'N', Value: 5 }],
        Script: //脚本
'FF:=(C-REF(C,N))/REF(C,N);\n\
STICKLINE(FF>0,DYNAINFO(3),DYNAINFO(3)*(1+FF),0.5,0),COLORRED;\n\
STICKLINE(FF<0,DYNAINFO(3),DYNAINFO(3)*(1+FF),0.5,0),COLORGREEN;'

    };

    return data;
}

JSIndexScript.prototype.UDL = function () 
{
    let data =
    {
        Name: 'UDL', Description: '引力线', IsMainIndex: false,
        Args: [{ Name: 'N1', Value: 3 },{ Name: 'N2', Value: 5 },{ Name: 'N3', Value: 10 },{ Name: 'N4', Value: 20 },{ Name: 'M', Value: 6 }],
        Script: //脚本
'UDL:(MA(CLOSE,N1)+MA(CLOSE,N2)+MA(CLOSE,N3)+MA(CLOSE,N4))/4;\n\
MAUDL:MA(UDL,M);'

    };

    return data;
}

JSIndexScript.prototype.MFI = function () 
{
    let data =
    {
        Name: 'MFI', Description: '资金流量指标', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 14 },{ Name: 'N2', Value: 6 }],
        Script: //脚本
'TYP := (HIGH + LOW + CLOSE)/3;\n\
V1:=SUM(IF(TYP>REF(TYP,1),TYP*VOL,0),N)/SUM(IF(TYP<REF(TYP,1),TYP*VOL,0),N);\n\
MFI:100-(100/(1+V1));'

    };

    return data;
}


JSIndexScript.prototype.LWR = function () 
{
    let data =
    {
        Name: 'LWR', Description: 'LWR威廉指标', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 9 },{ Name: 'M1', Value: 3 },{ Name: 'M2', Value: 3 }],
        Script: //脚本
'RSV:= (HHV(HIGH,N)-CLOSE)/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\
LWR1:SMA(RSV,M1,1);\n\
LWR2:SMA(LWR1,M2,1);'

    };

    return data;
}

JSIndexScript.prototype.MARSI = function () 
{
    let data =
    {
        Name: 'MARSI', Description: '相对强弱平均线', IsMainIndex: false,
        Args: [{ Name: 'M1', Value: 10 },{ Name: 'M2', Value: 6 }],
        Script: //脚本
'DIF:=CLOSE-REF(CLOSE,1);\n\
VU:=IF(DIF>=0,DIF,0);\n\
VD:=IF(DIF<0,-DIF,0);\n\
MAU1:=MEMA(VU,M1);\n\
MAD1:=MEMA(VD,M1);\n\
MAU2:=MEMA(VU,M2);\n\
MAD2:=MEMA(VD,M2);\n\
RSI10:MA(100*MAU1/(MAU1+MAD1),M1);\n\
RSI6:MA(100*MAU2/(MAU2+MAD2),M2);'

    };

    return data;
}

JSIndexScript.prototype.CYD = function () 
{
    let data =
    {
        Name: 'CYD', Description: '承接因子', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 21 }],
        Script: //脚本
'CYDS:WINNER(CLOSE)/(VOL/CAPITAL);\n\
CYDN:WINNER(CLOSE)/MA(VOL/CAPITAL,N);'

    };

    return data;
}

JSIndexScript.prototype.CYF = function () 
{
    let data =
    {
        Name: 'CYF', Description: '市场能量', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 21 }],
        Script: //脚本
'CYF:100-100/(1+EMA(HSL,N));'

    };

    return data;
}

JSIndexScript.prototype.TAPI = function () 
{
    let data =
    {
        Name: 'TAPI', Description: '加权指数成交值', IsMainIndex: false,
        Args: [{ Name: 'M', Value: 6 }],
        Script: //脚本
'TAPI:AMOUNT/INDEXC;\n\
MATAIP:MA(TAPI,M);'

    };

    return data;
}

JSIndexScript.prototype.VMACD = function () 
{
    let data =
    {
        Name: 'VMACD', Description: '量平滑异同平均', IsMainIndex: false,
        Args: [{ Name: 'SHORT', Value: 12 },{ Name: 'LONG', Value: 26 },{ Name: 'MID', Value: 9 }],
        Script: //脚本
'DIF:EMA(VOL,SHORT)-EMA(VOL,LONG);\n\
DEA:EMA(DIF,MID);\n\
MACD:DIF-DEA,COLORSTICK;'

    };

    return data;
}

JSIndexScript.prototype.QACD = function () 
{
    let data =
    {
        Name: 'QACD', Description: '快速异同平均', IsMainIndex: false,
        Args: [{ Name: 'N1', Value: 12 },{ Name: 'N2', Value: 26 },{ Name: 'M', Value: 9 }],
        Script: //脚本
'DIF:EMA(CLOSE,N1)-EMA(CLOSE,N2);\n\
MACD:EMA(DIF,M);\n\
DDIF:DIF-MACD;'

    };

    return data;
}

JSIndexScript.prototype.VPT = function () 
{
    let data =
    {
        Name: 'VPT', Description: '量价曲线', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 51 },{ Name: 'M', Value: 6 }],
        Script: //脚本
'VPT:SUM(VOL*(CLOSE-REF(CLOSE,1))/REF(CLOSE,1),N);\n\
MAVPT:MA(VPT,M);'

    };

    return data;
}

JSIndexScript.prototype.WVAD = function () 
{
    let data =
    {
        Name: 'WVAD', Description: '威廉变异离散量', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 24 },{ Name: 'M', Value: 6 }],
        Script: //脚本
'WVAD:SUM((CLOSE-OPEN)/(HIGH-LOW)*VOL,N)/10000;\n\
MAWVAD:MA(WVAD,M);'

    };

    return data;
}

JSIndexScript.prototype.DBQR = function () 
{
    let data =
    {
        Name: 'WVAD', Description: '对比强弱', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 5 },{ Name: 'M1', Value: 10 },{ Name: 'M2', Value: 20 },{ Name: 'M3', Value: 60 }],
        Script: //脚本
'ZS:(INDEXC-REF(INDEXC,N))/REF(INDEXC,N);\n\
GG:(CLOSE-REF(CLOSE,N))/REF(CLOSE,N);\n\
MADBQR1:MA(GG,M1);\n\
MADBQR2:MA(GG,M2);\n\
MADBQR3:MA(GG,M3);'

    };

    return data;
}

JSIndexScript.prototype.JS = function () 
{
    let data =
    {
        Name: 'JS', Description: '加速线', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 5 },{ Name: 'M1', Value: 5 },{ Name: 'M2', Value: 10 },{ Name: 'M3', Value: 20 }],
        Script: //脚本
'JS:100*(CLOSE-REF(CLOSE,N))/(N*REF(CLOSE,N));\n\
MAJS1:MA(JS,M1);\n\
MAJS2:MA(JS,M2);\n\
MAJS3:MA(JS,M3);'

    };

    return data;
}

JSIndexScript.prototype.CYE = function () 
{
    let data =
    {
        Name: 'CYE', Description: '市场趋势', IsMainIndex: false,
        Args: [ ],
        Script: //脚本
'MAL:=MA(CLOSE,5);\n\
MAS:=MA(MA(CLOSE,20),5);\n\
CYEL:(MAL-REF(MAL,1))/REF(MAL,1)*100;\n\
CYES:(MAS-REF(MAS,1))/REF(MAS,1)*100;'

    };

    return data;
}

JSIndexScript.prototype.QR = function () 
{
    let data =
    {
        Name: 'QR', Description: '强弱指标', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 21 } ],
        Script: //脚本
'个股: (CLOSE-REF(CLOSE,N))/REF(CLOSE,N)*100; \n\
大盘: (INDEXC-REF(INDEXC,N))/REF(INDEXC,N)*100; \n\
强弱值:EMA(个股-大盘,2),COLORSTICK;'

    };

    return data;
}

JSIndexScript.prototype.GDX = function () 
{
    let data =
    {
        Name: 'GDX', Description: '轨道线', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 30 } ,{ Name: 'M', Value: 9 }],
        Script: //脚本
'AA:=ABS((2*CLOSE+HIGH+LOW)/4-MA(CLOSE,N))/MA(CLOSE,N); \n\
轨道:DMA(CLOSE,AA);\n\
压力线:(1+M/100)*轨道; \n\
支撑线:(1-M/100)*轨道;'

    };

    return data;
}

JSIndexScript.prototype.JLHB = function () 
{
    let data =
    {
        Name: 'JLHB', Description: '绝路航标', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 7 } ,{ Name: 'M', Value: 5 }],
        Script: //脚本
'VAR1:=(CLOSE-LLV(LOW,60))/(HHV(HIGH,60)-LLV(LOW,60))*80; \n\
B:SMA(VAR1,N,1); \n\
VAR2:SMA(B,M,1); \n\
绝路航标:IF(CROSS(B,VAR2) AND B<40,50,0);'

    };

    return data;
}

JSIndexScript.prototype.PCNT = function () 
{
    let data =
    {
        Name: 'PCNT', Description: '幅度比', IsMainIndex: false,
        Args: [{ Name: 'M', Value: 5 }],
        Script: //脚本
'PCNT:(CLOSE-REF(CLOSE,1))/CLOSE*100;\n\
MAPCNT:EXPMEMA(PCNT,M);'

    };

    return data;
}

JSIndexScript.prototype.BTX = function () 
{
    let data =
    {
        Name: 'BTX', Description: '宝塔线', IsMainIndex: false,
        Args: [],
        Script: //脚本
            'B1:=REF(C,1);\n\
B2:= REF(C, 2);\n\
SS:= IF(C > REF(C, 1) AND REF(C, 1) >= REF(C, 2), 1, IF(C < REF(C, 1) AND REF(C, 1) <= REF(C, 2), -1, IF(C > REF(C, 2) AND REF(C, 2) > REF(C, 1), 2, IF(C < REF(C, 2) AND REF(C, 2) < REF(C, 1), -2, 0))));\n\
SM:= IF(REF(SS, 1) <> 0, REF(SS, 1), IF(REF(SS, 2) <> 0, REF(SS, 2), IF(REF(SS, 3) <> 0, REF(SS, 3), IF(REF(SS, 5) <> 0, REF(SS, 5), IF(REF(SS, 6) <> 0, REF(SS, 6), IF(REF(SS, 7) <> 0, REF(SS, 7), 0))))));\n\
MC:= IF(REF(SS, 1) <> 0, B2, IF(SM > 0, MIN(B1, B2), MAX(B1, B2)));\n\
TOW1:= IF(C > REF(C, 1), C, REF(C, 1));\n\
TOW2:= IF((SS == -1 OR SS == -2) AND SM > 0, B2, TOW1);\n\
TOWER:= IF(TOW1 > TOW2, TOW1, TOW2);\n\
STICKLINE(SS == 1 OR SM >= 1 AND SS == 0, B1, C, 10, 1), COLORRED;\n\
STICKLINE(SS == -1 OR SM <= -1 AND SS == 0, B1, C, 10, 0), COLORCYAN;\n\
STICKLINE(SS == 2, B2, C, 10, 1), COLORRED;\n\
STICKLINE(SS == -2, B2, C, 10, 0), COLORCYAN;\n\
STICKLINE((SS == -1 OR SS == -2) AND SM > 0, B2, B1, 10, 1), COLORRED;\n\
STICKLINE((SS == 1 OR SS == 2) AND SM < 0, B2, B1, 10, 0), COLORCYAN;'
        };

    return data;
}

JSIndexScript.prototype.AMO = function () 
{
    let data =
    {
        Name: 'AMO', Description: '成交金额', IsMainIndex: false,
        Args: [{ Name: 'M1', Value: 5 },{ Name: 'M2', Value: 10 }],
        Script: //脚本
'AMOW:AMOUNT/10000.0,VOLSTICK;\n\
AMO1:MA(AMOW,M1);\n\
AMO2:MA(AMOW,M2);'

    };

    return data;
}

JSIndexScript.prototype.VRSI = function () 
{
    let data =
    {
        Name: 'VRSI', Description: '相对强弱量', IsMainIndex: false,
        Args: [{ Name: 'N1', Value: 6 },{ Name: 'N2', Value: 12 },{ Name: 'N3', Value: 24 }],
        Script: //脚本
'LC:=REF(VOL,1);\n\
RSI1:SMA(MAX(VOL-LC,0),N1,1)/SMA(ABS(VOL-LC),N1,1)*100;\n\
RSI2:SMA(MAX(VOL-LC,0),N2,1)/SMA(ABS(VOL-LC),N2,1)*100;\n\
RSI3:SMA(MAX(VOL-LC,0),N3,1)/SMA(ABS(VOL-LC),N3,1)*100;'

    };

    return data;
}

JSIndexScript.prototype.HSCOL = function () 
{
    let data =
    {
        Name: 'HSCOL', Description: '换手柱', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 5 }],
        Script: //脚本
'HSCOL:IF((SETCODE==0||SETCODE==1),100*VOL,VOL)/(FINANCE(7)/100),VOLSTICK;\n\
MAHSL:MA(HSCOL,N);'

    };

    return data;
}

JSIndexScript.prototype.DBQRV = function () 
{
    let data =
    {
        Name: 'DBQRV', Description: '对比强弱量(需下载日线)', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 5 }],
        Script: //脚本
'ZS:(INDEXV-REF(INDEXV,N))/REF(INDEXV,N);\n\
GG:(VOL-REF(VOL,N))/REF(VOL,N);'

    };

    return data;
}

JSIndexScript.prototype.DBLB = function () 
{
    let data =
    {
        Name: 'DBLB', Description: '对比量比(需下载日线)', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 5 },{ Name: 'M', Value: 5 }],
        Script: //脚本
'GG:=VOL/SUM(REF(VOL,1),N);\n\
ZS:=INDEXV/SUM(REF(INDEXV,1),N);\n\
DBLB:GG/ZS;\n\
MADBLB:MA(DBLB,M);'

    };

    return data;
}

JSIndexScript.prototype.ACD = function () 
{
    let data =
    {
        Name: 'ACD', Description: '升降线', IsMainIndex: false,
        Args: [{ Name: 'M', Value: 20 }],
        Script: //脚本
'LC:=REF(CLOSE,1);\n\
DIF:=CLOSE-IF(CLOSE>LC,MIN(LOW,LC),MAX(HIGH,LC));\n\
ACD:SUM(IF(CLOSE==LC,0,DIF),0);\n\
MAACD:EXPMEMA(ACD,M);'

    };

    return data;
}

JSIndexScript.prototype.EXPMA = function () 
{
    let data =
    {
        Name: 'EXPMA', Description: '指数平均线', IsMainIndex: true,
        Args: [{ Name: 'M1', Value: 12 },{ Name: 'M2', Value: 50 }],
        Script: //脚本
'EXP1:EMA(CLOSE,M1);\n\
EXP2:EMA(CLOSE,M2);'

    };

    return data;
}

JSIndexScript.prototype.EXPMA_S = function () 
{
    let data =
    {
        Name: 'EXPMA_S', Description: '指数平均线-副图', IsMainIndex: false,
        Args: [{ Name: 'M1', Value: 12 },{ Name: 'M2', Value: 50 }],
        Script: //脚本
'EXP1:EMA(CLOSE,M1);\n\
EXP2:EMA(CLOSE,M2);'

    };

    return data;
}

JSIndexScript.prototype.HMA = function () 
{
    let data =
    {
        Name: 'HMA', Description: '高价平均线', IsMainIndex: true,
        Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }],
        Script: //脚本
'HMA1:MA(HIGH,M1);\n\
HMA2:MA(HIGH,M2);\n\
HMA3:MA(HIGH,M3);\n\
HMA4:MA(HIGH,M4);\n\
HMA5:MA(HIGH,M5);'

    };

    return data;
}

JSIndexScript.prototype.LMA = function () 
{
    let data =
    {
        Name: 'LMA', Description: '低价平均线', IsMainIndex: true,
        Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }],
        Script: //脚本
'LMA1:MA(LOW,M1);\n\
LMA2:MA(LOW,M2);\n\
LMA3:MA(LOW,M3);\n\
LMA4:MA(LOW,M4);\n\
LMA5:MA(LOW,M5);'

    };

    return data;
}

JSIndexScript.prototype.VMA = function () 
{
    let data =
    {
        Name: 'VMA', Description: '变异平均线', IsMainIndex: true,
        Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }],
        Script: //脚本
'VV:=(HIGH+OPEN+LOW+CLOSE)/4;\n\
VMA1:MA(VV,M1);\n\
VMA2:MA(VV,M2);\n\
VMA3:MA(VV,M3);\n\
VMA4:MA(VV,M4);\n\
VMA5:MA(VV,M5);'

    };

    return data;
}


JSIndexScript.prototype.AMV = function () 
{
    let data =
    {
        Name: 'AMV', Description: '成本价均线', IsMainIndex: false,
        Args: [{ Name: 'M1', Value: 6 },{ Name: 'M2', Value: 12 },{ Name: 'M3', Value: 30 },{ Name: 'M4', Value: 72 },{ Name: 'M5', Value: 144 }],
        Script: //脚本
'AMOV:=VOL*(OPEN+CLOSE)/2;\n\
AMV1:SUM(AMOV,M1)/SUM(VOL,M1);\n\
AMV2:SUM(AMOV,M2)/SUM(VOL,M2);\n\
AMV3:SUM(AMOV,M3)/SUM(VOL,M3);\n\
AMV4:SUM(AMOV,M4)/SUM(VOL,M4);'

    };

    return data;
}

JSIndexScript.prototype.BBIBOLL = function () 
{
    let data =
    {
        Name: 'BBIBOLL', Description: '多空布林线', IsMainIndex: true,
        Args: [{ Name: 'N', Value: 11 },{ Name: 'M', Value: 6 }],
        Script: //脚本
'CV:=CLOSE;\n\
BBIBOLL:(MA(CV,3)+MA(CV,6)+MA(CV,12)+MA(CV,24))/4;\n\
UPR:BBIBOLL+M*STD(BBIBOLL,N);\n\
DWN:BBIBOLL-M*STD(BBIBOLL,N);'

    };

    return data;
}

JSIndexScript.prototype.ALLIGAT = function () 
{
    let data =
    {
        Name: 'ALLIGAT', Description: '鳄鱼线', IsMainIndex: true,
        Args: [],
        Script: //脚本
'NN:=(H+L)/2;\n\
上唇:REF(MA(NN,5),3),COLOR40FF40;\n\
牙齿:REF(MA(NN,8),5),COLOR0000C0;\n\
下颚:REF(MA(NN,13),8),COLORFF4040;'

    };

    return data;
}

JSIndexScript.prototype.ZX = function () 
{
    let data =
    {
        Name: 'ZX', Description: '重心线', IsMainIndex: false,
        Args: [],
        Script: //脚本
'AV:0.01*AMOUNT/VOL;'

    };

    return data;
}

JSIndexScript.prototype.XS = function () 
{
    let data =
    {
        Name: 'XS', Description: '薛斯通道', IsMainIndex: true,
        Args: [{ Name: 'N', Value: 13 }],
        Script: //脚本
'VAR2:=CLOSE*VOL;\n\
VAR3:=EMA((EMA(VAR2,3)/EMA(VOL,3)+EMA(VAR2,6)/EMA(VOL,6)+EMA(VAR2,12)/EMA(VOL,12)+EMA(VAR2,24)/EMA(VOL,24))/4,N);\n\
SUP:1.06*VAR3;\n\
SDN:VAR3*0.94;\n\
VAR4:=EMA(CLOSE,9);\n\
LUP:EMA(VAR4*1.14,5);\n\
LDN:EMA(VAR4*0.86,5);'

    };

    return data;
}

JSIndexScript.prototype.XS2 = function () 
{
    let data =
    {
        Name: 'XS2', Description: '薛斯通道II', IsMainIndex: true,
        Args: [{ Name: 'N', Value: 102 },{ Name: 'M', Value: 7 }],
        Script: //脚本
'AA:=MA((2*CLOSE+HIGH+LOW)/4,5); \n\
通道1:AA*N/100; \n\
通道2:AA*(200-N)/100; \n\
CC:=ABS((2*CLOSE+HIGH+LOW)/4-MA(CLOSE,20))/MA(CLOSE,20); \n\
DD:=DMA(CLOSE,CC); \n\
通道3:(1+M/100)*DD; \n\
通道4:(1-M/100)*DD;'

    };

    return data;
}

JSIndexScript.prototype.SG_XDT = function () 
{
    let data =
    {
        Name: 'SG-XDT', Description: '心电图(需下载日线)', IsMainIndex: false,
        Args: [{ Name: 'P1', Value: 5 },{ Name: 'P2', Value: 10 }],
        Script: //脚本
'QR:CLOSE/INDEXC*1000;\n\
MQR1:MA(QR,5);\n\
MQR2:MA(QR,10);'

    };

    return data;
}

JSIndexScript.prototype.SG_SMX = function () 
{
    let data =
    {
        Name: 'SG-SMX', Description: '生命线(需下载日线)', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 50 }],
        Script: //脚本
'H1:=HHV(HIGH,N);\n\
L1:=LLV(LOW,N);\n\
H2:=HHV(INDEXH,N);\n\
L2:=LLV(INDEXL,N);\n\
ZY:=CLOSE/INDEXC*2000;\n\
ZY1:EMA(ZY,3);\n\
ZY2:EMA(ZY,17);\n\
ZY3:EMA(ZY,34);'

    };

    return data;
}

JSIndexScript.prototype.SG_LB = function () 
{
    let data =
    {
        Name: 'SG-LB', Description: '量比(需下载日线)', IsMainIndex: false,
        Args: [],
        Script: //脚本
'ZY2:=VOL/INDEXV*1000;\n\
量比:ZY2;\n\
MA5:MA(ZY2,5);\n\
MA10:MA(ZY2,10);'

    };

    return data;
}

JSIndexScript.prototype.SG_PF = function () 
{
    let data =
    {
        Name: 'SG-PF', Description: '强势股评分(需下载日线)', IsMainIndex: false,
        Args: [],
        Script: //脚本
'ZY1:=CLOSE/INDEXC*1000;\n\
A1:=IF(ZY1>HHV(ZY1,3),10,0);\n\
A2:=IF(ZY1>HHV(ZY1,5),15,0);\n\
A3:=IF(ZY1>HHV(ZY1,10),20,0);\n\
A4:=IF(ZY1>HHV(ZY1,2),10,0);\n\
A5:=COUNT(ZY1>REF(ZY1,1) ,9)*5;\n\
强势股评分:A1+A2+A3+A4+A5;'

    };

    return data;
}

JSIndexScript.prototype.RAD = function () 
{
    let data =
    {
        Name: 'RAD', Description: '威力雷达(需下载日线)', IsMainIndex: false,
        Args: [{ Name: 'D', Value: 3 },{ Name: 'S', Value: 30 },{ Name: 'M', Value: 30 }],
        Script: //脚本
'SM:=(OPEN+HIGH+CLOSE+LOW)/4;\n\
SMID:=MA(SM,D);\n\
IM:=(INDEXO+INDEXH+INDEXL+INDEXC)/4;\n\
IMID:=MA(IM,D);\n\
SI1:=(SMID-REF(SMID,1))/SMID;\n\
II:=(IMID-REF(IMID,1))/IMID;\n\
RADER1:SUM((SI1-II)*2,S)*1000;\n\
RADERMA:SMA(RADER1,M,1);'

    };

    return data;
}

JSIndexScript.prototype.SHT = function () 
{
    let data =
    {
        Name: 'SHT', Description: '龙系短线', IsMainIndex: false,
        Args: [{ Name: 'N', Value: 5 }],
        Script: //脚本
'VAR1:=MA((VOL-REF(VOL,1))/REF(VOL,1),5);\n\
VAR2:=(CLOSE-MA(CLOSE,24))/MA(CLOSE,24)*100;\n\
MY: VAR2*(1+VAR1);\n\
SHT: MY, COLORSTICK;\n\
SHTMA: MA(SHT,N);'

    };

    return data;
}

JSIndexScript.prototype.ZLJC = function () 
{
    let data =
    {
        Name: 'ZLJC', Description: '主力进出', IsMainIndex: false,
        Args: [],
        Script: //脚本
'VAR1:=(CLOSE+LOW+HIGH)/3; \n\
VAR2:=SUM(((VAR1-REF(LOW,1))-(HIGH-VAR1))*VOL/100000/(HIGH-LOW),0); \n\
VAR3:=EMA(VAR2,1); \n\
JCS:VAR3; \n\
JCM:MA(VAR3,12); \n\
JCL:MA(VAR3,26);'

    };

    return data;
}

JSIndexScript.prototype.ZLMM = function () 
{
    let data =
    {
        Name: 'ZLMM', Description: '主力买卖', IsMainIndex: false,
        Args: [],
        Script: //脚本
'LC :=REF(CLOSE,1);\n\
RSI2:=SMA(MAX(CLOSE-LC,0),12,1)/SMA(ABS(CLOSE-LC),12,1)*100;\n\
RSI3:=SMA(MAX(CLOSE-LC,0),18,1)/SMA(ABS(CLOSE-LC),18,1)*100;\n\
MMS:MA(3*RSI2-2*SMA(MAX(CLOSE-LC,0),16,1)/SMA(ABS(CLOSE-LC),16,1)*100,3);\n\
MMM:EMA(MMS,8);\n\
MML:MA(3*RSI3-2*SMA(MAX(CLOSE-LC,0),12,1)/SMA(ABS(CLOSE-LC),12,1)*100,5);'

    };

    return data;
}

JSIndexScript.prototype.SLZT = function () 
{
    let data =
    {
        Name: 'SLZT', Description: '神龙在天', IsMainIndex: false,
        Args: [],
        Script: //脚本
'白龙: MA(CLOSE,125);\n\
黄龙: 白龙+2*STD(CLOSE,170);\n\
紫龙: 白龙-2*STD(CLOSE,145);\n\
青龙: SAR(125,1,7), LINESTICK;\n\
VAR2:=HHV(HIGH,70);\n\
VAR3:=HHV(HIGH,20);\n\
红龙: VAR2*0.83;\n\
蓝龙: VAR3*0.91;'

    };

    return data;
}

JSIndexScript.prototype.ADVOL = function () 
{
    let data =
    {
        Name: 'ADVOL', Description: '龙系离散量', IsMainIndex: false,
        Args: [],
        Script: //脚本
'A:=SUM(((CLOSE-LOW)-(HIGH-CLOSE))*VOL/10000/(HIGH-LOW),0);\n\
ADVOL:A;\n\
MA1:MA(A,30);\n\
MA2:MA(MA1,100);'

    };

    return data;
}

JSIndexScript.prototype.CYC = function () 
{
    let data =
    {
        Name: 'CYC', Description: '成本均线', IsMainIndex: true,
        Args: [{ Name: 'P1', Value: 5 },{ Name: 'P2', Value: 13 },{ Name: 'P3', Value: 34 }],
        Script: //脚本
'JJJ:=IF(DYNAINFO(8)>0.01,0.01*DYNAINFO(10)/DYNAINFO(8),DYNAINFO(3));\n\
DDD:=(DYNAINFO(5)<0.01 || DYNAINFO(6)<0.01);\n\
JJJT:=IF(DDD,1,(JJJ<(DYNAINFO(5)+0.01) && JJJ>(DYNAINFO(6)-0.01)));\n\
CYC1:IF(JJJT,0.01*EMA(AMOUNT,P1)/EMA(VOL,P1),EMA((HIGH+LOW+CLOSE)/3,P1));\n\
CYC2:IF(JJJT,0.01*EMA(AMOUNT,P2)/EMA(VOL,P2),EMA((HIGH+LOW+CLOSE)/3,P2));\n\
CYC3:IF(JJJT,0.01*EMA(AMOUNT,P3)/EMA(VOL,P3),EMA((HIGH+LOW+CLOSE)/3,P3));\n\
CYC4:IF(JJJT,DMA(AMOUNT/(100*VOL),100*VOL/FINANCE(7)),EMA((HIGH+LOW+CLOSE)/3,120));'

    };

    return data;
}

JSIndexScript.prototype.CYS = function () 
{
    let data =
    {
        Name: 'CYS', Description: '市场盈亏', IsMainIndex: false,
        Args: [],
        Script: //脚本
'CYC13:EMA(AMOUNT,13)/EMA(VOL,13);\n\
CYS:(CLOSE-CYC13)/CYC13*100;'

    };

    return data;
}

JSIndexScript.prototype.CYQKL = function () 
{
    let data =
    {
        Name: 'CYQKL', Description: '博弈K线长度', IsMainIndex: false,
        Args: [],
        Script: //脚本
'KL:100*(WINNER(CLOSE)-WINNER(OPEN));'

    };

    return data;
}

JSIndexScript.prototype.SCR = function () 
{
    let data =
    {
        Name: 'SCR', Description: '筹码集中度', IsMainIndex: false,
        Args: [{ Name: 'P1', Value: 90 }],
        Script: //脚本
'A:=P1+(100-P1)/2;\n\
B:=(100-P1)/2;\n\
CC:=COST(A);\n\
DD:=COST(B);\n\
SCR:(CC-DD)/(CC+DD)*100/2;'

    };

    return data;
}


JSIndexScript.prototype.ASR = function () 
{
    let data =
    {
        Name: 'ASR', Description: '浮筹比例', IsMainIndex: false,
        Args: [],
        Script: //脚本
'ASR:(WINNER(C*1.1)-WINNER(C*0.9))/WINNER(HHV(H,0))*100;'

    };

    return data;
}

JSIndexScript.prototype.SAR = function () 
{
    let data =
    {
        Name: 'SAR', Description: '抛物转向', IsMainIndex: true,
        Args: [{ Name: 'P', Value: 10 },{ Name: 'STEP', Value: 2 },{ Name: 'MAXP', Value: 20 }],
        Script: //脚本
'S:SAR(P,STEP,MAXP),CIRCLEDOT;'

    };

    return data;
}

JSIndexScript.prototype.TJCJL = function () 
{
    let data =
    {
        Name: '太极成交量', Description: '太极成交量', IsMainIndex: true,
        Args: [],
        Script: //脚本
'总手:VOL,NODRAW;\n\
ZZ:=IF(REF(C,1)>REF(O,1) AND O>REF(C,1)*1.014 AND C<O*1.02,1,3);\n\
V5:=MA(V,5);\n\
V12:=MA(V,12);\n\
V34:=MA(V,34);\n\
C6:=MA(C,6);\n\
STICKLINE(VOL,0,VOL,10,0),COLORLIGRAY;\n\
STICKLINE(CROSS(C,C6) AND V>V5*1.2 AND V>V12*1.2 AND ZZ>2 AND C>H*0.975,0,VOL,10,0),COLORRED;\n\
STICKLINE(CROSS(C6,C) AND V>V5*1.2 AND V>V12*1.2,0,VOL,10,0),COLORGREEN;\n\
STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND C<REF(C,1)*1.05,0,VOL,10,0),COLORYELLOW;\n\
STICKLINE(VOL<MA(VOL,5)/2 AND V<V12/2,0,VOL,10,0),COLORBLUE;\n\
STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND C<REF(C,1)*1.05 AND CROSS(C,C6) AND V>V5*1.2 AND V>V12*1.2 AND ZZ>2 AND C>H*0.975,VOL*0.5,0,10,0),COLORRED;\n\
STICKLINE(VOL>MA(VOL,5)*2 AND V>V34*3 AND C<REF(C,1)*1.05 AND CROSS(C6,C) AND V>V5*1.2 AND V>V12*1.2,VOL*0.5,0,10,0),COLORRED;'

    };

    return data;
}

/*
    飞龙四式-主图
*/
JSIndexScript.prototype.Dragon4_Main = function () 
{
    let data =
    {
        Name: '飞龙四式', Description: '飞龙四式', IsMainIndex: true,
        Args: [{ Name: 'N1', Value: 5 }, { Name: 'N2', Value: 10 }, { Name: 'N3', Value: 50 }, { Name: 'N4', Value: 60 }],
        Script: //脚本
'蜻蜓点水:=EMA(CLOSE,N1),COLORGRAY;\n\
魔界:=EMA(CLOSE,N2),COLORGREEN;\n\
水:=EMA(CLOSE,N3),COLORRED;\n\
DRAWKLINE(HIGH,OPEN,LOW,CLOSE);\n\
生命线:MA(CLOSE,N4),COLORBLUE,LINETHICK2;\n\
DRAWBAND(魔界,\'RGB(186,225,250)\',水,\'RGB(253,194,124)\');\n\
DRAWBAND(蜻蜓点水,\'RGB(128,138,135)\',魔界,\'RGB(0,0,255)\');'

    };

    return data;
}

JSIndexScript.prototype.Dragon4_Fig=function()
{
    let data =
    {
        Name: '飞龙四式', Description: '飞龙四式', IsMainIndex: false,
        Args: [],
        Script: //脚本
'倍:VOL>=REF(V,1)*1.90 AND C>REF(C,1),COLORYELLOW;\n\
低:VOL<REF(LLV(VOL,13),1),COLORGREEN;\n\
地:VOL<REF(LLV(VOL,100),1),COLORMAGENTA; \n\
平:=ABS(VOL-HHV(REF(VOL,1),5))/HHV(REF(VOL,1),5)<=0.03 OR ABS(VOL-REF(VOL,1))/REF(VOL,1)<=0.03,NODRAW,COLORWHITE;\n\
倍缩:VOL<=REF(V,1)*0.5,COLORFF8000;\n\
梯量:COUNT(V>REF(V,1),3)==3 AND COUNT(C>O,3)==3,COLORBROWN;\n\
缩量涨:COUNT(C>REF(C,1),2)==2 AND COUNT(V<REF(V,1),2)==2,COLORBLUE;\n\
STICKLINE(C>=REF(C,1),V,0,2,0),COLORRED;\n\
STICKLINE(C<REF(C,1),V,0,2,0),COLORGREEN;\n\
STICKLINE(倍,0,V,2,0),COLORYELLOW;\n\
STICKLINE(低,0,V,2,0),COLORGREEN;\n\
STICKLINE(地,0,V,2,0),COLORLIMAGENTA;\n\
STICKLINE(平,0,V,2,0),COLORGRAY;\n\
STICKLINE(倍缩,0,V,2,0),COLORFF8000;\n\
STICKLINE(梯量,0,V,2,0),COLORBROWN;\n\
STICKLINE(缩量涨,0,V,2,0),COLORBLUE;'

    };

    return data;
}


/*
能图-资金分析
M:=55;
N:=34;
LC:=REF(CLOSE,1);
RSI:=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);
FF:=EMA(CLOSE,3);
MA15:=EMA(CLOSE,21); DRAWTEXT(CROSS(85,RSI),75,'▼'),COLORGREEN;
VAR1:=IF(YEAR>=2038 AND MONTH>=1,0,1);
VAR2:=REF(LOW,1)*VAR1;
VAR3:=SMA(ABS(LOW-VAR2),3,1)/SMA(MAX(LOW-VAR2,0),3,1)*100*VAR1;
VAR4:=EMA(IF(CLOSE*1.3,VAR3*10,VAR3/10),3)*VAR1;
VAR5:=LLV(LOW,30)*VAR1;
VAR6:=HHV(VAR4,30)*VAR1;
VAR7:=IF(MA(CLOSE,58),1,0)*VAR1;
VAR8:=EMA(IF(LOW<=VAR5,(VAR4+VAR6*2)/2,0),3)/618*VAR7*VAR1;
吸筹A:IF(VAR8>100,100,VAR8)*VAR1,COLORRED;
吸筹B:STICKLINE(吸筹A>-150,0,吸筹A,8,0),COLORRED;

散户线: 100*(HHV(HIGH,M)-CLOSE)/(HHV(HIGH,M)-LLV(LOW,M)),COLORFFFF00,LINETHICK2;
RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;
K:=SMA(RSV,3,1);
D:=SMA(K,3,1);
J:=3*K-2*D;
主力线:EMA(J,5),COLORFF00FF,LINETHICK2;
DRAWICON(CROSS(主力线,散户线),主力线,1);
DRAWICON(CROSS(散户线,主力线),主力线,2);
*/

JSIndexScript.prototype.FundsAnalysis=function()
{
    let data =
    {
        Name: '资金分析', Description: '资金分析', IsMainIndex: false,
        Args: [{ Name: 'M', Value: 55 }, { Name: 'N', Value: 34 }],
        Script: //脚本
'LC:=REF(CLOSE,1);\n\
RSI:=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);\n\
FF:=EMA(CLOSE,3);\n\
MA15:=EMA(CLOSE,21); DRAWTEXT(CROSS(85,RSI),75,\'▼\'),COLORGREEN;\n\
VAR1:=IF(YEAR>=2038 AND MONTH>=1,0,1);\n\
VAR2:=REF(LOW,1)*VAR1;\n\
VAR3:=SMA(ABS(LOW-VAR2),3,1)/SMA(MAX(LOW-VAR2,0),3,1)*100*VAR1;\n\
VAR4:=EMA(IF(CLOSE*1.3,VAR3*10,VAR3/10),3)*VAR1;\n\
VAR5:=LLV(LOW,30)*VAR1;\n\
VAR6:=HHV(VAR4,30)*VAR1;\n\
VAR7:=IF(MA(CLOSE,58),1,0)*VAR1;\n\
VAR8:=EMA(IF(LOW<=VAR5,(VAR4+VAR6*2)/2,0),3)/618*VAR7*VAR1;\n\
吸筹A:IF(VAR8>100,100,VAR8)*VAR1,COLORFB2F3B;\n\
{吸筹B}STICKLINE(吸筹A>-150,0,吸筹A,8,0),COLORFB2F3B;\n\
\n\
散户线: 100*(HHV(HIGH,M)-CLOSE)/(HHV(HIGH,M)-LLV(LOW,M)),COLORAA89BD,LINETHICK2;\n\
RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\
K:=SMA(RSV,3,1);\n\
D:=SMA(K,3,1);\n\
J:=3*K-2*D;\n\
主力线:EMA(J,5),COLORF39800,LINETHICK2;\n\
DRAWICON(CROSS(主力线,散户线),主力线,1);\n\
DRAWICON(CROSS(散户线,主力线),主力线,2);'
    };

    return data; 
}

JSIndexScript.prototype.MarginProportion=function()
{
    let data =
    {
        Name: '融资占比(%)', Description: '融资占比', IsMainIndex: false, 
        Condition: { Period:[CONDITION_PERIOD.KLINE_DAY_ID] },
        Args: [],
        Script: //脚本
        '占比:MARGIN(2);'
    };

    return data; 
}

JSIndexScript.prototype.FXG_BSPoint=function()
{
    let data =
    {
        Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true,
        Args: [],
        Script: //脚本
        'MA5:MA(CLOSE,5);\n\
        MA13:MA(CLOSE,13);\n\
        MA21:MA(CLOSE,21);\n\
        MA34:MA(CLOSE,34);\n\
        {MA55:MA(CLOSE,55),COLOR0000FF;}\n\
        {MA120:=MA(CLOSE,120),COLORFFFF00;}\n\
        天使:=EMA(C,2),COLOR000000;\n\
        魔鬼:=EMA(SLOPE(C,21)*20+C,42),COLOR000000;\n\
        买:=CROSS(天使,魔鬼);\n\
        卖:=CROSS(魔鬼,天使);\n\
        DRAWICON(买,L*0.97,13);\n\
        DRAWICON(卖,H*1.03,14);\n\
        DRAWKLINE_IF(天使>=魔鬼,HIGH,CLOSE,LOW,OPEN),COLORRED;\n\
        DRAWKLINE_IF(天使<魔鬼,HIGH,CLOSE,LOW,OPEN),COLORBLUE;\n\
        DRAWKLINE_IF(CROSS(天使,魔鬼),HIGH,CLOSE,LOW,OPEN),COLORYELLOW;\n\
        DRAWKLINE_IF(CROSS(魔鬼,天使),HIGH,CLOSE,LOW,OPEN),COLORBLACK;'
    };

    return data; 
}

JSIndexScript.prototype.FXG_INDEX=function()
{
    let data =
    {
        Name: '涨停多空线', Description: '涨停多空线', IsMainIndex: false,
        Args: [],
        Script: //脚本
'做多能量线: SMA((CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100,5,1)-8,COLORRED,LINETHICK3;\n\
做空能量线: SMA((HHV(HIGH,36)-CLOSE)/(HHV(HIGH,36)-LLV(LOW,36))*100,2,1),COLORGREEN,LINETHICK3;\n\
20,POINTDOT,COLORF00FF0;\n\
50,POINTDOT,COLORGREEN;\n\
80,POINTDOT,COLORLIBLUE;'
    };

    return data; 
}

JSIndexScript.prototype.FXG_INDEX2=function()
{
    let data =
    {
        Name: '涨停吸筹区', Description: '涨停吸筹区', IsMainIndex: false,
        Args: [],
        Script: //脚本
'VAR0:=EMA(HHV(HIGH,500),21); \n\
VAR1:=EMA(HHV(HIGH,250),21);\n\
VAR2:=EMA(HHV(HIGH,90),21); \n\
VAR3:=EMA(LLV(LOW,500),21); \n\
VAR4:=EMA(LLV(LOW,250),21); \n\
VAR5:=EMA(LLV(LOW,90),21);\n\
\n\
VAR6:=EMA((VAR3*0.96+VAR4*0.96+VAR5*0.96+VAR0*0.558+VAR1*0.558+VAR2*0.558)/6,21); \n\
VAR7:=EMA((VAR3*1.25+VAR4*1.23+VAR5*1.2+VAR0*0.55+VAR1*0.55+VAR2*0.65)/6,21); \n\
VAR8:=EMA((VAR3*1.3+VAR4*1.3+VAR5*1.3+VAR0*0.68+VAR1*0.68+VAR2*0.68)/6,21); \n\
VAR9:=EMA((VAR6*3+VAR7*2+VAR8)/6*1.738,21); \n\
VAR10:=REF(LOW,1); \n\
VAR11:=SMA(ABS(LOW-VAR10),3,1)/SMA(MAX(LOW-VAR10,0),3,1)*100; \n\
VAR12:=EMA(IFF(CLOSE*1.35<=VAR9,VAR11*10,VAR11/10),3); \n\
VAR13:=LLV(LOW,30); \n\
VAR14:=HHV(VAR12,30); \n\
VAR15:=IFF(MA(CLOSE,58),1,0); \n\
VAR16:=EMA(IFF(LOW<=VAR13,(VAR12+VAR14*2)/2,0),3)/618*VAR15;\n\
\n\
资金入场:IFF(VAR16>0,VAR16,0),LINETHICK,LINETHICK2, COLORFF0000; \n\
\n\
A1:IFF(资金入场>0,资金入场*1.2,0),STICK,LINETHICK5, COLORFF0000;\n\
A2:IFF(资金入场>0,资金入场*0.8,0),STICK,LINETHICK5, COLORFF6600;\n\
A3:IFF(资金入场>0,资金入场*0.6,0),STICK,LINETHICK5, COLORFF9900;\n\
A4:IFF(资金入场>0,资金入场*0.4,0) ,STICK,LINETHICK5,COLORFFCC00;\n\
A5:IFF(资金入场>0,资金入场*0.2,0) ,STICK,LINETHICK5,COLORFFFF00;'
    };

    return data; 
}

JSIndexScript.prototype.FXG_INDEX3=function()
{
    let data =
    {
        Name: '量能黄金点', Description: '量能黄金点', IsMainIndex: false,FloatPrecision:0,
        Args: [],
        Script: //脚本
'A:=IFF((CLOSE>126.32),VOL,VOL); \n\
主力:=MA(A,4),COLORRED;\n\
游资:=MA(A,8),COLORYELLOW;\n\
大户:=MA(A,16),COLORF0F000;\n\
散户:=MA(A,32),COLOR00FF00;\n\
主比:=ABS(((主力)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORRED;\n\
游比:=ABS(((游资)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORYELLOW;\n\
大比:=ABS(((大户)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLORF0F000;\n\
散比:=ABS(((散户)/(主力 + 游资 + 大户 + 散户))*(100)),LINESTICK,COLOR00FF00;\n\
警戒线:MA(A,180),COLORFF66FF;\n\
STICKLINE((主力 > 0),0,主力,2.5,0),COLOR1020BB;\n\
STICKLINE((主力 > 0),0,主力,0.7,0),COLORRED;\n\
STICKLINE((游资 > 0),0,游资,2.5,0),COLOR009CFF;\n\
STICKLINE((游资 > 0),0,游资,0.7,0),COLORYELLOW;\n\
STICKLINE((大户 > 0),0,大户,2.5,0),COLORFF8800;\n\
STICKLINE((大户 > 0),0,大户,0.7,0),COLORLIBLUE;\n\
STICKLINE((散户 > 0),0,散户,2.5,0),COLOR00CA00;\n\
STICKLINE((散户 > 0),0,散户,0.7,0),COLORGREEN;'
    };

    return data; 
}


JSIndexScript.prototype.NewsNegative=function()
{
    let data=
        {
            Name: '负面新闻', Description: '负面新闻统计', IsMainIndex: false,FloatPrecision:0,
            Args: [{ Name: 'N', Value: 5 }, { Name: 'N2', Value: 10 }],
            Script: //脚本
'负面:NEWS(1);\n\
MA1:MA(负面,N);\n\
MA2:MA(负面,N2);'
        };

    return data;
}

JSIndexScript.prototype.UpDownAnalyze=function()
{
    let data=
    {
        Name: '涨跌趋势', Description: '涨跌趋势', IsMainIndex: false,FloatPrecision:0, 
        Condition: { Period:[CONDITION_PERIOD.MINUTE_ID, CONDITION_PERIOD.MULTIDAY_MINUTE_ID, CONDITION_PERIOD.KLINE_DAY_ID] },
        Args: [],
        Script: //脚本
"上涨家数:UPCOUNT('CNA.CI'),COLORRED;\n\
下跌家数:DOWNCOUNT('CNA.CI'),COLORGREEN;"
    };

    return data;
}

JSIndexScript.prototype.HK2SHSZ=function()
{
    let data=
    {
        Name: '北上资金', Description: '北上资金', IsMainIndex: false,FloatPrecision:0, 
        Condition: { Period:[CONDITION_PERIOD.MINUTE_ID,CONDITION_PERIOD.MULTIDAY_MINUTE_ID,CONDITION_PERIOD.KLINE_DAY_ID] },
        Args: [],
        Script: //脚本
            "净流入:HK2SHSZ(1),COLORSTICK;"
    };

    return data;
}

JSIndexScript.prototype.ShareHolder=function()
{
    let data=
    {
        Name: '股东人数', Description: '股东人数', IsMainIndex: false,FloatPrecision:0, 
        Condition: { Period:[ 
                                CONDITION_PERIOD.KLINE_DAY_ID,
                                CONDITION_PERIOD.KLINE_MONTH_ID,
                                CONDITION_PERIOD.KLINE_WEEK_ID,
                                CONDITION_PERIOD.KLINE_YEAR_ID] },
        Args: [],
        Script: //脚本
            "人数:FINANCE(100);"
    };

    return data;
}

JSIndexScript.prototype.VOLRate=function()
{
    let data=
    {
        Name: '量比', Description: '量比', IsMainIndex: false,  Condition: { Period:[CONDITION_PERIOD.MINUTE_ID, CONDITION_PERIOD.MULTIDAY_MINUTE_ID ] },
        Args: [],
        Script: //脚本
            "LIANGBI:VOLR;"
    };

    return data;
}

////////////////////////////////////////////////////////////////////////////////////////////////
//五彩K线

JSIndexScript.prototype.COLOR_KSTAR1=function()
{
    let data=
    {
        Name: '十字星', Description: '十字星', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:CLOSE==OPEN&&HIGH>LOW;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_KSTAR2=function()
{
    let data=
    {
        Name: '早晨之星', Description: '早晨之星', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:(REF(CLOSE,2)/REF(OPEN,2)<0.95) && (REF(OPEN,1) < REF(CLOSE,2)) && (ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03) && CLOSE/OPEN>1.05 && CLOSE>REF(CLOSE,2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_KSTAR3=function()
{
    let data=
    {
        Name: '黄昏之星', Description: '黄昏之星', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:REF(CLOSE,2)/REF(OPEN,2)>1.05 && REF(OPEN,1)>REF(CLOSE,2) && ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03 && CLOSE/OPEN<0.95 && CLOSE<REF(CLOSE,2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_SHI1=function()
{
    let data=
    {
        Name: '长十字', Description: '长十字', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:CLOSE==OPEN&&HIGH/LOW>1.03;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K220=function()
{
    let data=
    {
        Name: '身怀六甲', Description: '身怀六甲', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:ABS(REF(CLOSE,1)-REF(OPEN,1))/REF(CLOSE,1)>0.04&&\n\
            ABS(CLOSE-OPEN)/CLOSE<0.005&&\n\
            MAX(CLOSE,OPEN)<MAX(REF(CLOSE,1),REF(OPEN,1))&&\n\
            MIN(CLOSE,OPEN)>MIN(REF(CLOSE,1),REF(OPEN,1));'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K300=function()
{
    let data=
    {
        Name: '三个白武士', Description: '三个白武士', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:UPNDAY(CLOSE,3)&&NDAY(CLOSE,OPEN,3);'
    };

    return data;
}


JSIndexScript.prototype.COLOR_K310=function()
{
    let data=
    {
        Name: '三只乌鸦', Description: '三只乌鸦', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:DOWNNDAY(CLOSE,3)&&NDAY(OPEN,CLOSE,3);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K380=function()
{
    let data=
    {
        Name: '光头阳线', Description: '光头阳线', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:HIGH==CLOSE&&HIGH>LOW;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K390=function()
{
    let data=
    {
        Name: '光脚阴线', Description: '光脚阴线', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:LOW==CLOSE&&HIGH>LOW;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K134=function()
{
    let data=
    {
        Name: '垂死十字', Description: '垂死十字', IsMainIndex: true, InstructionType:2,
        Script: //脚本
            'KSTAR:CLOSE==OPEN&&CLOSE==LOW&&CLOSE<HIGH;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K140=function()
{
    let data=
    {
        Name: '早晨十字星', Description: '早晨十字星', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'KSTAR:REF(CLOSE,2)/REF(OPEN,2)<0.95&&\n\
REF(OPEN,1)<REF(CLOSE,2)&&\n\
REF(OPEN,1)==REF(CLOSE,1)&&\n\
CLOSE/OPEN>1.05&&CLOSE>REF(CLOSE,2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K150=function()
{
    let data=
    {
        Name: '黄昏十字星', Description: '黄昏十字星', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'KSTAR:REF(CLOSE,2)/REF(OPEN,2)>1.05&&\n\
REF(OPEN,1)>REF(CLOSE,2)&&\n\
REF(OPEN,1)=REF(CLOSE,1)&&\n\
CLOSE/OPEN<0.95&&CLOSE<REF(CLOSE,2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K160=function()
{
    let data=
    {
        Name: '射击之星', Description: '射击之星', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'KSTAR:MIN(OPEN,CLOSE)==LOW&&\n\
HIGH-LOW>3*(MAX(OPEN,CLOSE)-LOW)&&\n\
CLOSE>MA(CLOSE,5);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K165=function()
{
    let data=
    {
        Name: '倒转锤头', Description: '倒转锤头', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'KSTAR:MIN(OPEN,CLOSE)==LOW&&\n\
HIGH-LOW>3*(MAX(OPEN,CLOSE)-LOW)&&\n\
CLOSE<MA(CLOSE,5);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K170=function()
{
    let data=
    {
        Name: '锤头', Description: '锤头', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'OUT:HIGH==MAX(OPEN,CLOSE)&&\n\
HIGH-LOW>3*(HIGH-MIN(OPEN,CLOSE))&&\n\
CLOSE<MA(CLOSE,5);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K180=function()
{
    let data=
    {
        Name: '吊颈', Description: '吊颈', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'OUT:HIGH==MAX(OPEN,CLOSE)&&\n\
HIGH-LOW>3*(HIGH-MIN(OPEN,CLOSE))&&\n\
CLOSE>MA(CLOSE,5);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_K190=function()
{
    let data=
    {
        Name: '穿头破脚', Description: '穿头破脚', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'OUT:(REF(CLOSE,1)/REF(OPEN,1)>1.03&&\n\
CLOSE/OPEN<0.96&&\n\
CLOSE<REF(OPEN,1)&&OPEN>REF(CLOSE,1))||\n\
(REF(CLOSE,1)/REF(OPEN,1)<0.97&&\n\
CLOSE/OPEN>1.04&&\n\
CLOSE>REF(OPEN,1)&&OPEN<REF(CLOSE,1));'
    };

    return data;
}

JSIndexScript.prototype.COLOR_SWORD=function()
{
    let data=
    {
        Name: '剑', Description: '剑', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'AA:=VOL>REF(VOL,1)||VOL>(CAPITAL*0.1);\n\
BB:=OPEN>=(REF(HIGH,1))&&REF(HIGH,1)>(REF(HIGH,2)*1.06);\n\
CC:=CLOSE>(REF(CLOSE,1))-(REF(CLOSE,1)*0.01);\n\
DD:=CLOSE<(HIGH*0.965) && HIGH>(OPEN*1.05);\n\
EE:=LOW<OPEN && LOW<CLOSE&&HIGH>(REF(CLOSE,1)*1.06);\n\
FF:=(HIGH-(MAX(OPEN,CLOSE)))/2>(MIN(OPEN,CLOSE))-LOW;\n\
GG:=(ABS(OPEN-CLOSE))/2<(MIN(OPEN,CLOSE)-LOW);\n\
SWORDO:AA&&BB&&CC&&DD&&EE&&FF&&GG;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_CSFR=function()
{
    let data=
    {
        Name: '出水芙蓉', Description: '出水芙蓉', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'A:=CLOSE>OPEN;\n\
B:=A&&CLOSE>MA(CLOSE,S)&&CLOSE>MA(CLOSE,M)&&CLOSE>MA(CLOSE,LL);\n\
CC:=B&&OPEN<MA(CLOSE,M)&&OPEN<MA(CLOSE,LL);\n\
CSFRO:CC&&(CLOSE-OPEN)>0.0618*CLOSE;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_WYGD=function()
{
    let data=
    {
        Name: '乌云盖顶', Description: '乌云盖顶', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( \n\
REF(CLOSE,1)/REF(OPEN,1)>1.03 AND \n\
CLOSE/OPEN<0.97 AND \n\
OPEN>REF(CLOSE,1) AND CLOSE<REF(CLOSE,1), 3);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_SGCJ=function()
{
    let data=
    {
        Name: '曙光初现', Description: '曙光初现', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( \n\
REF(CLOSE,1)/REF(OPEN,1)<0.97 AND \n\
CLOSE/OPEN>1.03 AND \n\
OPEN<REF(CLOSE,1) AND CLOSE>REF(CLOSE,1), 3);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_SZTAI=function()
{
    let data=
    {
        Name: '十字胎', Description: '十字胎', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( ABS(REF(CLOSE,1)-REF(OPEN,1))/REF(CLOSE,1) > 0.04 AND \n\
CLOSE==OPEN AND CLOSE < MAX(REF(CLOSE,1),REF(OPEN,1)) AND \n\
CLOSE > MIN(REF(CLOSE,1),REF(OPEN,1)), 2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_PINGDING=function()
{
    let data=
    {
        Name: '平顶', Description: '平顶', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET(ABS(HIGH-REF(HIGH,1))/HIGH<0.001,2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_PINGDI=function()
{
    let data=
    {
        Name: '平底', Description: '平底', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET((ABS(LOW-REF(LOW,1))/LOW<0.001 AND \n\
ABS(REF(LOW,1)-REF(LOW,2))/REF(LOW,1)<=0.001),2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_DAYANZHU=function()
{
    let data=
    {
        Name: '大阳烛', Description: '大阳烛', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:CLOSE/OPEN>1.05 AND HIGH/LOW < CLOSE/OPEN+0.018;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_DAYINGZHU=function()
{
    let data=
    {
        Name: '大阴烛', Description: '大阴烛', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:OPEN/CLOSE > 1.05 AND HIGH/LOW < OPEN/CLOSE+0.018;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_HYFG=function()
{
    let data=
    {
        Name: '好友反攻', Description: '好友反攻', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( (REF(CLOSE,1)<REF(OPEN,1) AND \n\
CLOSE>OPEN AND ABS(CLOSE-REF(CLOSE,1))/CLOSE<0.002),2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_TKQK=function()
{
    let data=
    {
        Name: '跳空缺口', Description: '跳空缺口', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( HIGH<REF(LOW,1) OR LOW>REF(HIGH,1),2);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_SFWY=function()
{
    let data=
    {
        Name: '双飞乌鸦', Description: '双飞乌鸦', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( REF(CLOSE,1)<REF(OPEN,1) AND CLOSE<OPEN AND CLOSE/OPEN<0.98,1);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_SSSBQ=function()
{
    let data=
    {
        Name: '上升三部曲', Description: '上升三部曲', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( \n\
REF(CLOSE,4)/REF(OPEN,4)>1.03 AND \n\
REF(CLOSE,3)<REF(OPEN,3) AND \n\
REF(CLOSE,2)<REF(OPEN,2) AND \n\
REF(CLOSE,1)<REF(OPEN,1) AND \n\
REF(LOW,4)<REF(LOW,3) AND \n\
REF(LOW,4)<REF(LOW,2) AND \n\
REF(LOW,4)<REF(LOW,1) AND \n\
REF(HIGH,4)>REF(HIGH,3) AND \n\
REF(HIGH,4)>REF(HIGH,2) AND \n\
REF(HIGH,4)>REF(HIGH,1) AND \n\
CLOSE/OPEN>1.03 AND \n\
CLOSE>REF(CLOSE,4), 5);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_XDSBQ=function()
{
    let data=
    {
        Name: '下跌三部曲', Description: '下跌三部曲', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( \n\
REF(CLOSE,4)/REF(OPEN,4)<0.97 AND \n\
REF(CLOSE,3)>REF(OPEN,3) AND \n\
REF(CLOSE,2)>REF(OPEN,2) AND \n\
REF(CLOSE,1)>REF(OPEN,1) AND \n\
REF(LOW,4)<REF(LOW,3) AND \n\
REF(LOW,4)<REF(LOW,2) AND \n\
REF(LOW,4)<REF(LOW,1) AND \n\
REF(HIGH,4)>REF(HIGH,3) AND \n\
REF(HIGH,4)>REF(HIGH,2) AND \n\
REF(HIGH,4)>REF(HIGH,1) AND \n\
CLOSE/OPEN<0.97 AND \n\
CLOSE<REF(CLOSE,4), 5);'
    };

    return data;
}

JSIndexScript.prototype.COLOR_CHXY=function()
{
    let data=
    {
        Name: '长下影', Description: '长下影', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR2:(MIN(CLOSE,OPEN)-LOW)/(HIGH-LOW)>0.667;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_CHSY=function()
{
    let data=
    {
        Name: '长上影', Description: '长上影', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR2:(HIGH-MAX(CLOSE,OPEN))/(HIGH-LOW)>0.667,COLORBLUE;'
    };

    return data;
}

JSIndexScript.prototype.COLOR_FENLI=function()
{
    let data=
    {
        Name: '分离', Description: '分离', IsMainIndex: true, InstructionType:2,
        Script: //脚本
'VAR1:BACKSET( OPEN==REF(OPEN,1) AND (CLOSE-OPEN)*(REF(CLOSE,1)-REF(OPEN,1))<0,2);'
    };

    return data;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//交易系统

JSIndexScript.prototype.TRADE_BIAS = function () 
{
    let data =
    {
        Name: 'BIAS', Description: '乖离率专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 12 }, { Name: 'LL', Value: 6 },{ Name: 'LH', Value: 6 }],
        Script: //脚本
'BIAS:=(CLOSE-MA(CLOSE,N))/MA(CLOSE,N)*100;\n\
ENTERLONG:CROSS(-LL,BIAS);\n\
EXITLONG:CROSS(BIAS,LH);'

    };

    return data;
}

JSIndexScript.prototype.TRADE_CCI = function () 
{
    let data =
    {
        Name: 'CCI', Description: 'CCI专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 14 }],
        Script: //脚本
'TYP:=(HIGH+LOW+CLOSE)/3;\n\
CCI:=(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));\n\
INDEX:=CCI;\n\
ENTERLONG:CROSS(INDEX,-100);\n\
EXITLONG:CROSS(100,INDEX);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_DMI = function () 
{
    let data =
    {
        Name: 'DMI', Description: '趋向专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 14 }],
        Script: //脚本
'MTR:=SUM(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(LOW-REF(CLOSE,1))),N);\n\
HD :=HIGH-REF(HIGH,1);\n\
LD :=REF(LOW,1)-LOW;\n\
PDM:=SUM(IF(HD>0&&HD>LD,HD,0),N);\n\
MDM:=SUM(IF(LD>0&&LD>HD,LD,0),N);\n\
PDI:=PDM*100/MTR;\n\
MDI:=MDM*100/MTR;\n\
ENTERLONG:CROSS(PDI,MDI);\n\
EXITLONG:CROSS(MDI,PDI);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_KD = function () 
{
    let data =
    {
        Name: 'KD', Description: 'KD指标专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 9 },{ Name: 'M1', Value: 3 },{ Name: 'M2', Value: 3 }],
        Script: //脚本
'WRSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\
WK:=SMA(WRSV,M1,1);\n\
D:=SMA(WK,M2,1);\n\
ENTERLONG:CROSS(WK,D)&&WK<20;\n\
EXITLONG:CROSS(D,WK)&&WK>80;'
    };

    return data;
}

JSIndexScript.prototype.TRADE_BOLL = function () 
{
    let data =
    {
        Name: 'BOLL', Description: '布林带专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 20 }],
        Script: //脚本
'MID :=MA(CLOSE,N);\n\
UPPER:=MID+2*STD(CLOSE,N);\n\
LOWER:=MID-2*STD(CLOSE,N);\n\
ENTERLONG:CROSS(CLOSE,LOWER);\n\
EXITLONG:CROSS(CLOSE,UPPER);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_KDJ = function () 
{
    let data =
    {
        Name: 'KDJ', Description: 'KDJ专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 9 },{ Name: 'M1', Value: 3 }],
        Script: //脚本
'RSV:=(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N))*100;\n\
K:=SMA(RSV,M1,1);\n\
D:=SMA(K,M1,1);\n\
J:=3*K-2*D;\n\
ENTERLONG:CROSS(J,0);\n\
EXITLONG:CROSS(100,J);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_MA = function () 
{
    let data =
    {
        Name: 'MA', Description: '均线专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'SHORT', Value: 5 },{ Name: 'LONG', Value: 20 }],
        Script: //脚本
'ENTERLONG:CROSS(MA(CLOSE,SHORT),MA(CLOSE,LONG));\n\
EXITLONG:CROSS(MA(CLOSE,LONG),MA(CLOSE,SHORT));'
    };

    return data;
}

JSIndexScript.prototype.TRADE_MACD = function () 
{
    let data =
    {
        Name: 'MACD', Description: 'MACD专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'LONG', Value: 26 }, { Name: 'SHORT', Value: 12 }, { Name: 'M', Value: 9 }],
        Script: //脚本
'DIFF:=EMA(CLOSE,SHORT) - EMA(CLOSE,LONG);\n\
DEA  := EMA(DIFF,M);\n\
MACD := 2*(DIFF-DEA);\n\
ENTERLONG:CROSS(MACD,0);\n\
EXITLONG:CROSS(0,MACD);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_MTM = function () 
{
    let data =
    {
        Name: 'MTM', Description: '动力指标专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 6 }],
        Script: //脚本
'WMTM:=C-REF(C,N);\n\
ENTERLONG:CROSS(WMTM,0);\n\
EXITLONG:CROSS(0,WMTM);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_PSY = function () 
{
    let data =
    {
        Name: 'PSY', Description: 'PSY心理线专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 12 },{ Name: 'LL', Value: 10 },{ Name: 'LH', Value: 85 }],
        Script: //脚本
'MYPSY:=COUNT(CLOSE>REF(CLOSE,1),N)/N*100;\n\
ENTERLONG:CROSS(LL,MYPSY);\n\
EXITLONG:CROSS(MYPSY,LH);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_ROC = function () 
{
    let data =
    {
        Name: 'ROC', Description: '变动速率专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 12 },{ Name: 'M', Value: 6 }],
        Script: //脚本
'WROC:=MA(100*(CLOSE-REF(CLOSE,N))/REF(CLOSE,N),M);\n\
ENTERLONG:CROSS(WROC,0);\n\
EXITLONG:CROSS(0,WROC);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_RSI = function () 
{
    let data =
    {
        Name: 'RSI', Description: '相对强弱专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 6 },{ Name: 'LL', Value: 20 },{ Name: 'LH', Value: 80 }],
        Script: //脚本
'LC:=REF(CLOSE,1);\n\
WRSI:=SMA(MAX(CLOSE-LC,0),N,1)/SMA(ABS(CLOSE-LC),N,1)*100;\n\
ENTERLONG:CROSS(WRSI,LL);\n\
EXITLONG:CROSS(LH,WRSI);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_VR = function () 
{
    let data =
    {
        Name: 'VR', Description: 'VR容量比率专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N', Value: 26 },{ Name: 'LL', Value: 70 },{ Name: 'LH', Value: 250 }],
        Script: //脚本
'WVR := SUM((IF(CLOSE>OPEN,VOL,0)+IF(CLOSE=OPEN,VOL/2,0)),N)/SUM((IF(CLOSE<OPEN,VOL,0)+IF(CLOSE=OPEN,VOL/2,0)),N)*100;\n\
ENTERLONG:CROSS(LL,WVR);\n\
EXITLONG:CROSS(WVR,LH);'
    };

    return data;
}

JSIndexScript.prototype.TRADE_DPSJ = function () 
{
    let data =
    {
        Name: 'DPSJ', Description: '大盘随机专家系统', IsMainIndex: true, InstructionType:1,
        Args: [{ Name: 'N1', Value: 18 },{ Name: 'N2', Value: 12 }],
        Script: //脚本
'RSV:=(INDEXC-LLV(INDEXL,N1))/(HHV(INDEXH,N1)-LLV(INDEXL,N1))*100;\n\
K:=SMA(RSV,N2,1);\n\
HSL:VOL/100/(FINANCE(7));\n\
ENTERLONG: CROSS(K,20);\n\
EXITLONG: (CROSS(HSL,5) OR CROSS(K,80));'
    };

    return data;
}


JSIndexScript.prototype.Zealink_Index1 = function () 
{
    let data =
    {
        Name: '资金吸筹', Description: '资金吸筹', IsMainIndex: false,
        Args: [],
        Script: //脚本
'VAR1:=REF((LOW+OPEN+CLOSE+HIGH)/4,1);  \n\
VAR2:= SMA(ABS(LOW - VAR1), 13, 1) / SMA(MAX(LOW - VAR1, 0), 10, 1);\n\
VAR3:= EMA(VAR2, 10);\n\
VAR4:= LLV(LOW, 33);\n\
VAR5:= EMA(IF(LOW <= VAR4, VAR3, 0), 3) * 0.2;\n\
主力进场: IF(VAR5 > REF(VAR5, 1), VAR5,0), COLORRED, NODRAW;\n\
洗盘: IF(VAR5 < REF(VAR5, 1),  VAR5,0), COLORYELLOW, NODRAW;\n\
STICKLINE(VAR5> REF(VAR5, 1),0, VAR5, 20, 0), COLORRED;\n\
STICKLINE(VAR5 < REF(VAR5, 1), 0, VAR5, 20, 0), COLORYELLOW;'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index2 = function () 
{
    let data =
    {
        Name: '牛熊区间', Description: '牛熊区间', IsMainIndex: false,YSpecificMaxMin:{Max:100,Min:1,Count:4},YSplitScale:[1,50,100],
        Args: [],
        Script: //脚本
'短高H:=(20*H+19*REF(H,1)+18*REF(H,2)+17*REF(H,3)+16*REF(H,4)+15*REF(H,5)+14*REF(H,6)\n\
+ 13 * REF(H, 7) + 12 * REF(H, 8) + 11 * REF(H, 9) + 10 * REF(H, 10) + 9 * REF(H, 11) + 8 * REF(H, 12)\n\
+ 7 * REF(H, 13) + 6 * REF(H, 14) + 5 * REF(H, 15) + 4 * REF(H, 16) + 3 * REF(H, 17) + 2 * REF(H, 18) +\n\
REF(H, 20))/ 210, COLORBLUE, LINETHICK1;\n\
短低L:= (20 * L + 19 * REF(L, 1) + 18 * REF(L, 2) + 17 * REF(L, 3) + 16 * REF(L, 4) + 15 * REF(L, 5) + 14 * REF(L, 6)\n\
+ 13 * REF(L, 7) + 12 * REF(L, 8) + 11 * REF(L, 9) + 10 * REF(L, 10) + 9 * REF(L, 11) + 8 * REF(L, 12)\n\
+ 7 * REF(L, 13) + 6 * REF(L, 14) + 5 * REF(L, 15) + 4 * REF(L, 16) + 3 * REF(L, 17) + 2 * REF(L, 18) +\n\
REF(L, 20)) / 210, COLORBLUE, LINETHICK1;\n\
D90H:= EMA(短高H, 90), COLORRED, LINETHICK1;\n\
D90L:= EMA(短低L, 90), COLORRED, LINETHICK1;\n\
D90差:= D90H - D90L;\n\
D90顶:= D90H + D90差 * 2, COLORRED, LINETHICK1;\n\
D90底:= D90L - D90差 * 2, COLORRED, LINETHICK1;\n\
高0:= (EMA(EMA(H, 25), 25) - EMA(EMA(L, 25), 25)) * 1 + EMA(EMA(H, 25), 25), LINETHICK1, COLORWHITE;\n\
低0:= EMA(EMA(L, 25), 25) - (EMA(EMA(H, 25), 25) - EMA(EMA(L, 25), 25)) * 1, LINETHICK1, COLORWHITE;\n\
多头定位:= 低0 >= D90底 AND 高0 >= D90顶;\n\
空头定位:= 高0 <= D90顶 AND 低0 <= D90底;\n\
震荡定位:= 低0 >= D90底 AND 高0 <= D90顶;\n\
牛市: IF(多头定位 == 1, 100, 1), COLORRED, NODRAW;\n\
熊市: IF(空头定位 == 1, 100, 1), COLORGREEN, NODRAW;\n\
震荡: IF(震荡定位 == 1, 100, 1), COLORGRAY, NODRAW;\n\
STICKLINE(多头定位 == 1, 100, 1, 100, 0), COLORRED;\n\
STICKLINE(空头定位 == 1, 100, 1, 100, 0), COLORGREEN;\n\
STICKLINE(震荡定位 == 1, 100, 1, 100, 0), COLORGRAY;'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index3 = function () 
{
    let data =
    {
        Name: '持仓信号', Description: '持仓信号', IsMainIndex: true,
        Args: [],
        Script: //脚本
'买线:=EMA(C,2);\n\
卖线:=EMA(SLOPE(C,30)*5+C,20); \n\
BU:=CROSS(买线,卖线);\n\
SEL:=CROSS(卖线,买线);\n\
\n\
STICKLINE(买线>=卖线 AND CLOSE>OPEN,LOW,HIGH,0,1),COLORRED;\n\
STICKLINE(买线>=卖线 AND CLOSE<OPEN,LOW,HIGH,0,1),COLORRED;\n\
STICKLINE(买线>=卖线 AND CLOSE>OPEN,CLOSE,OPEN,50,1),COLORRED;\n\
STICKLINE(买线>=卖线 AND CLOSE<OPEN,CLOSE,OPEN,50,0),COLORRED;\n\
STICKLINE(买线<卖线 AND CLOSE<OPEN,LOW,HIGH,0,1),COLORGREEN;\n\
STICKLINE(买线<卖线 AND CLOSE>OPEN,LOW,HIGH,0,1),COLORGREEN;\n\
STICKLINE(买线<卖线 AND CLOSE<OPEN,CLOSE,OPEN,50,0),COLORGREEN;\n\
STICKLINE(买线<卖线 AND CLOSE>OPEN,CLOSE,OPEN,50,1),COLORGREEN;\n\
\n\
HHJSJDA:=(3*CLOSE+OPEN+LOW+HIGH)/6;\n\
HHJSJDB:(19*HHJSJDA+19*REF(HHJSJDA,1)+18*REF(HHJSJDA,2)+17*REF(HHJSJDA,3)+16*REF(HHJSJDA,4)+15*REF(HHJSJDA,5)+14*REF(HHJSJDA,6)\n\
+13*REF(HHJSJDA,7)+12*REF(HHJSJDA,8)+11*REF(HHJSJDA,9)+10*REF(HHJSJDA,10)+9*REF(HHJSJDA,11)+8*REF(HHJSJDA,12)+7*REF(HHJSJDA,13)+6*REF(HHJSJDA,14)+5*REF(HHJSJDA,15)+4*REF(HHJSJDA,16)+3*REF(HHJSJDA,17)+2*REF\n\
(HHJSJDA,20)+REF(HHJSJDA,19))/210,COLORYELLOW;\n\
HHJSJDC:MA(HHJSJDB,5),COLORRED;\n\
\n\
SVAR11:=HHV(HIGH,34);\n\
SVAR14:=CLOSE-REF(CLOSE,1);\n\
SVAR15:=MAX(SVAR14,0);\n\
SVAR16:=ABS(SVAR14);\n\
SVAR17:=SMA(SVAR15,7,1)/SMA(SVAR16,7,1)*100;\n\
SVAR18:=SMA(SVAR15,13,1)/SMA(SVAR16,13,1)*100;\n\
SVAR19:=BARSCOUNT(CLOSE);\n\
SVAR20:=SMA(MAX(SVAR14,0),6,1)/SMA(ABS(SVAR14),6,1)*100;\n\
SVAR21:=(-200)*(HHV(HIGH,60)-CLOSE)/(HHV(HIGH,60)-LLV(LOW,60))+100;\n\
SVAR1A:=(CLOSE-LLV(LOW,15))/(HHV(HIGH,15)-LLV(LOW,15))*100;\n\
SVAR1B:=SMA((SMA(SVAR1A,4,1)-50)*2,3,1);\n\
SVAR1C:=(INDEXC-LLV(INDEXL,14))/(HHV(INDEXH,14)-LLV(INDEXL,14))*100;\n\
SVAR1D:=SMA(SVAR1C,4,1);\n\
SVAR1E:=SMA(SVAR1D,3,1);\n\
SVAR1F:=(HHV(HIGH,30)-CLOSE)/CLOSE*100;\n\
SVAR22:=SVAR20<=25 AND SVAR21<-95 AND SVAR1F>20 AND SVAR1B<-30 AND SVAR1E<30 AND SVAR11-CLOSE>=-0.25 AND SVAR17<22 AND SVAR18<28 AND SVAR19>50;\n\
BUY3:=CROSS(SVAR22,0.5) AND COUNT(SVAR22==1,10)==1;\n\
\n\
SVARF:=LOW*0.9;\n\
SVAR10X:=100-3*SMA((OPEN-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1)+2*SMA(SMA((OPEN-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1),15,1);\n\
SVAR11X:=SVARF<REF(SVAR10X,1) AND VOL>REF(VOL,1) AND CLOSE>REF(CLOSE,1);\n\
BUY2:=SVAR11X AND COUNT(SVAR11X,30)==1;\n\
\n\
VAR1:=(CLOSE+HIGH+LOW+OPEN)/4;\n\
VAR2:=SUMBARS(VOL,CAPITAL);\n\
VAR3:=HHV(VAR1,VAR2);\n\
VAR4:=LLV(VAR1,VAR2);\n\
VAR5:=(2*VAR1-VAR4-REF(VAR4,1))/(VAR3-VAR4);\n\
VAR6:=(VAR1-VAR4)/(VAR3-VAR4);\n\
VAR7:=IF(VAR1<=VAR4,VAR5*60,VAR6*60);\n\
VAR8:=600*(EMA(CLOSE,3)-EMA(LOW,30))/EMA(LOW,30);\n\
VAR9:=EMA(VAR8,7);\n\
VARC:=HHV(HIGH,9)-LLV(LOW,9);\n\
VARD:=HHV(HIGH,9)-CLOSE;\n\
VARE:=CLOSE-LLV(LOW,9);\n\
VARF:=VARD/VARC*100-70;\n\
VAR10:=(CLOSE-LLV(LOW,60))/(HHV(HIGH,60)-LLV(LOW,60))*100;\n\
VAR11:=(2*CLOSE+HIGH+LOW)/4;\n\
VAR12:=SMA(VARE/VARC*100,3,1);\n\
VAR13:=LLV(LOW,34);\n\
VAR14:=SMA(VAR12,3,1)-SMA(VARF,9,1);\n\
VAR15:=IF(VAR14>100,VAR14-100,0);\n\
VAR16:=HHV(HIGH,34);\n\
VAR17:=EMA((VAR11-VAR13)/(VAR16-VAR13)*100,8);\n\
VAR18:=EMA(VAR17,5);\n\
BUY:=STICKLINE(VAR17-VAR18>0,VAR17,VAR18,8,1),COLORRED;\n\
SELL:=STICKLINE(VAR17-VAR18<0,VAR17,VAR18,8,1),COLORGREEN;\n\
BUY1:=VAR17>VAR18 AND REF(VAR17,1)<REF(VAR18,1);\n\
SELL1:=VAR17<VAR18 AND REF(VAR17,1)>REF(VAR18,1);\n\
\n\
短高H:=(20*H+19*REF(H,1)+18*REF(H,2)+17*REF(H,3)+16*REF(H,4)+15*REF(H,5)+14*REF(H,6)\n\
+13*REF(H,7)+12*REF(H,8)+11*REF(H,9)+10*REF(H,10)+9*REF(H,11)+8*REF(H,12)\n\
+7*REF(H,13)+6*REF(H,14)+5*REF(H,15)+4*REF(H,16)+3*REF(H,17)+2*REF(H,18)+\n\
REF(H,20))/210,COLORBLUE,LINETHICK1;\n\
短低L:=(20*L+19*REF(L,1)+18*REF(L,2)+17*REF(L,3)+16*REF(L,4)+15*REF(L,5)+14*REF(L,6)\n\
+13*REF(L,7)+12*REF(L,8)+11*REF(L,9)+10*REF(L,10)+9*REF(L,11)+8*REF(L,12)\n\
+7*REF(L,13)+6*REF(L,14)+5*REF(L,15)+4*REF(L,16)+3*REF(L,17)+2*REF(L,18)+\n\
REF(L,20))/210,COLORBLUE,LINETHICK1;\n\
D90H:=EMA(短高H,90),COLORRED,LINETHICK1;\n\
D90L:=EMA(短低L,90),COLORRED,LINETHICK1;\n\
D90差:=D90H-D90L;\n\
D90顶:=D90H+D90差*2,COLORRED,LINETHICK1;\n\
D90底:=D90L-D90差*2,COLORRED,LINETHICK1;\n\
高0:=(EMA(EMA(H,25),25)-EMA(EMA(L,25),25))*1+EMA(EMA(H,25),25),LINETHICK1,COLORWHITE;\n\
低0:=EMA(EMA(L,25),25)-(EMA(EMA(H,25),25)-EMA(EMA(L,25),25))*1,LINETHICK1,COLORWHITE;\n\
多头定位:=低0>=D90底 AND 高0>=D90顶;\n\
空头定位:=高0<=D90顶 AND 低0<=D90底;\n\
震荡定位:=低0>=D90底 AND 高0<=D90顶;\n\
\n\
牛市:=多头定位==1;\n\
熊市:=空头定位==1;\n\
震荡:=震荡定位==1;\n\
\n\
非牛市:=熊市 OR 震荡;\n\
非熊市:=牛市 OR 震荡;\n\
\n\
BUY11:=BUY1 AND 非熊市;\n\
SELL11:=SELL1 AND 震荡定位==0;\n\
\n\
BUY111:=BUY11 AND COUNT(BUY11,10)<2;\n\
BUY0:=BUY111 AND COUNT(BUY111,21)==1;\n\
SELL111:=SELL11 AND COUNT(SELL11,10)<2;\n\
SELL0:=SELL111 AND COUNT(SELL111,10)==1;\n\
\n\
XK1:=EMA(100*(CLOSE-LLV(LOW,34))/(HHV(HIGH,34)-LLV(LOW,34)),3)/4;\n\
上穿:=REF(XK1,1)<5 AND XK1>=5;\n\
BUY4:=上穿 AND COUNT(XK1<2,12)<1;\n\
\n\
SELL2:=REF(XK1,1)<=22.5 AND XK1>22.5 AND COUNT(REF(XK1,1)>=22.5 AND XK1<22.5,5)>0;\n\
SELL3:=REF(XK1,1)>=21.5 AND XK1<21.5 AND COUNT(REF(XK1,1)>=22.5 AND XK1<22.5,12)>1;\n\
SELL4:=SELL2 OR SELL3 AND COUNT((SELL2 OR SELL3)==1,5)==1;\n\
\n\
SUPERDRAWTEXT(BUY0,L,"机会",2,10),COLORRED;\n\
SUPERDRAWTEXT(SELL0,H,"风险",1,10),COLORGREEN;\n\
SUPERDRAWTEXT(BUY2,L,"机会",2,10),COLORRED;\n\
SUPERDRAWTEXT(BUY4,L,"机会",2,10),COLORRED;\n\
SUPERDRAWTEXT(SELL4,H,"风险",1,10),COLORGREEN;'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index4 = function () 
{
    let data =
    {
        Name: '股东实际增减持', Description: '股东实际增减持', IsMainIndex: false, FloatPrecision:0,
        Args: [],
        Script: //脚本
'增持:NEWS(4),NODRAW,COLORRED;\n\
减持:NEWS(5),NODRAW,COLORGREEN;\n\
STICKLINE(增持>0,0,增持,1,0),COLORRED;\n\
STICKLINE(减持<0,0,减持,1,0),COLORGREEN;'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index5 = function () 
{
    let data =
    {
        Name: '大宗交易', Description: '大宗交易', IsMainIndex: false, FloatPrecision: 0,
        Args: [],
        Script: //脚本
            '交易次数:NEWS(7);'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index6 = function () 
{
    let data =
    {
        Name: '信托持股', Description: '信托持股', IsMainIndex: false, FloatPrecision: 0,
        Args: [],
        Script: //脚本
            '家数:NEWS(6);'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index7 = function () 
{
    let data =
    {
        Name: '官网新闻', Description: '官网新闻', IsMainIndex: false, FloatPrecision: 0,
        Args: [],
        Script: //脚本
            '个数:NEWS(8);'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index8 = function () 
{
    let data =
    {
        Name: '高管要闻', Description: '高管要闻', IsMainIndex: false, FloatPrecision: 0,
        Args: [],
        Script: //脚本
            '个数:NEWS(9);'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index9 = function () 
{
    let data =
    {
        Name: '股权质押', Description: '股权质押', IsMainIndex: false, FloatPrecision: 0,
        Args: [],
        Script: //脚本
            '次数:NEWS(10);'
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index10 = function () 
{
    let data =
    {
        Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, FloatPrecision: 0,
        Args: [],
        Script: //脚本
"JJ:=(CLOSE+HIGH+LOW)/3; \n\
A:=EMA(JJ,10); \n\
B:=REF(A,1);\n\
M1:=EMA(CLOSE,3);\n\
M2:=EMA(CLOSE,8); \n\
M3:=EMA(M2,13); \n\
M4:=EMA(M2,55); \n\
B1:=A>B AND REF(A,1)<REF(B,1);\n\
S1:=A<B AND REF(A,1)>REF(B,1);\n\
INDEXCLOSE:INDEXC,EXDATA;\n\
INDEXOPEN:INDEXO,EXDATA;\n\
DRAWICON(REF(B1,1),L*0.97,13);\n\
DRAWICON(REF(S1,1),H*1.03,14);"
    };

    return data;
}

JSIndexScript.prototype.Zealink_Index11 = function () 
{
    let data =
    {
        Name: '操盘BS点', Description: '操盘BS点', IsMainIndex: true, FloatPrecision: 0,
        Args: [],
        Script: //脚本
"JJ:=(CLOSE+HIGH+LOW)/3;\n\
A:=EMA(JJ,10);\n\
B:=REF(A,1);\n\
M1:=EMA(CLOSE,3);\n\
M2:=EMA(CLOSE,8); \n\
M3:=EMA(M2,13);\n\
M4:=EMA(M2,55);\n\
持股区域:=STICKLINE(A>B,A,B,2,0),COLORYELLOW; \n\
持币区域:=STICKLINE(A<B,A,B,2,0),COLORBLUE;\n\
\n\
VR1:=(C+O+REF(C,1))/3;\n\
VR3:=HHV(VR1,21);\n\
VR4:=LLV(VR1,21);\n\
VR5:=(2*VR1-VR4-REF(VR4,1))/(VR3-VR4);\n\
VR6:=(VR1-VR4)/(VR3-VR4);\n\
VR7:=IF(VR1<=VR4,VR5*60,VR6*60);\n\
VR8:=600*(EMA(C,3)-EMA(L,30))/EMA(L,30);\n\
VR9:=EMA(VR8,7);\n\
VRA:=(WINNER(120*C/100)-WINNER(100*C/100))*100;\n\
VRB:=(-100)*(WINNER(120*C/100)-WINNER(100*C/100))+5;\n\
VRC:=HHV(H,9)-LLV(L,9);\n\
VRD:=HHV(H,9)-C;\n\
VRE:=C-LLV(L,9);\n\
VRF:=VRD/VRC*100-70;\n\
VR10:=(C-LLV(L,60))/(HHV(H,60)-LLV(L,60))*100;\n\
VR11:=(2*C+H+L)/4;\n\
VR12:=SMA(VRE/VRC*100,3,1);\n\
VR13:=LLV(L,34);\n\
VR14:=SMA(VR12,3,1)-SMA(VRF,9,1);\n\
VR15:=IF(VR14>100,VR14-100,0);\n\
VR16:=HHV(H,34);\n\
VR17:=EMA((VR11-VR13)/(VR16-VR13)*100,8);\n\
VR18:=EMA(VR17,5);\n\
\n\
B1:=A>B AND REF(A,1)<REF(B,1);\n\
S1:=A<B AND REF(A,1)>REF(B,1);\n\
\n\
B9:=BARSLAST(REF(B1,1) AND (REF(VR17>VR18,1) OR MIN(VR17,VR18)>REF(MAX(VR17,VR18),1)));\n\
S9:=BARSLAST(REF(S1,1) AND (REF(VR17<VR18,1) OR MAX(VR17,VR18)<REF(MIN(VR17,VR18),1)));\n\
\n\
DRAWICON(REF(B1,1) AND (REF(VR17>VR18,1) OR MIN(VR17,VR18)>REF(MAX(VR17,VR18),1)) AND REF(B9,2)>=S9,L*0.97,13);\n\
DRAWICON(REF(S1,1) AND (REF(VR17<VR18,1) OR MAX(VR17,VR18)<REF(MIN(VR17,VR18),1)) AND REF(S9,1)>=B9,H*1.03,14);\n\
\n\
DRAWKLINE_IF(VR17>VR18,HIGH,CLOSE,LOW,OPEN),COLORRED;\n\
DRAWKLINE_IF(VR17<VR18,HIGH,CLOSE,LOW,OPEN),COLORBLUE;\n\
\n\
INDEXCLOSE:INDEXC,EXDATA;		//取指数的收盘价 回测的时候计算BATE系数用 "
    };

    return data;
}


JSIndexScript.prototype.HeikinAshi=function()
{
    let data =
    {
        Name: '平均K线', Description: 'Heikin-Ashi 平均K线', IsMainIndex: true, KLineType:-1,
        Args: [],
        Script: //脚本
"HCLOSE:(OPEN+HIGH+LOW+CLOSE)/4, NODRAW;\n\
HOPEN:(REF(OPEN,1)+REF(CLOSE,1))/2,NODRAW;\n\
HHIGH:MAX(HIGH,MAX(HOPEN,HCLOSE)),NODRAW;\n\
HLOW:MIN(LOW,MIN(HOPEN,HCLOSE)),NODRAW;\n\
DRAWKLINE(HHIGH,HOPEN,HLOW,HCLOSE);"
    }

    return data;
}

JSIndexScript.prototype.EMPTY = function () 
{
    let data =
    {
        Name: '', Description: '空指标', IsMainIndex: true,
        Args: [],
        Script: //脚本
            'VAR2:=C;'
    };

    return data;
}


JSIndexScript.prototype.TEST = function () 
{
    let data =
        {
            Name: 'TEST', Description: '测试脚本', IsMainIndex: false,
            Args: [{ Name: 'N', Value: 10 }],
            Script: //脚本
                "DRAWCHANNEL(OPEN>C,H,L, 'RGB(255,94,102)', 2 ,'5,5','RGBA(58,20,62,0.3)' );"
        };

    return data;
}



/*
   Copyright (c) 2018 jones
 
    http://www.apache.org/licenses/LICENSE-2.0

   开源项目 https://github.com/jones2000/HQChart
 
   jones_2000@163.com

   封装行情类图形控件 (H5版本)
*/


//日志输出类
if (!JSConsole)
{
    var JSConsole=
    { 
        Chart:{ Log:console.log },      //图形日志
        Complier:{ Log:console.log }    //编译器日志
    };
}


function JSChart(divElement)
{
    this.DivElement=divElement;
    this.DivToolElement=null;           //工具条
    this.JSChartContainer;              //画图控件

    //h5 canvas
    this.CanvasElement=document.createElement("canvas");
    this.CanvasElement.className='jschart-drawing';
    this.CanvasElement.id=Guid();
    this.CanvasElement.setAttribute("tabindex",0);
    if (this.CanvasElement.style) this.CanvasElement.style.outline='none';
    if(!divElement.hasChildNodes("canvas")){
        divElement.appendChild(this.CanvasElement);
    }

    //改参数div
    this.ModifyIndexDialog=new ModifyIndexDialog(divElement);
    this.ChangeIndexDialog=new ChangeIndexDialog(divElement);
    this.MinuteDialog=new MinuteDialog(divElement);

    this.OnSize=function()
    {
        //画布大小通过div获取
        var height=parseInt(this.DivElement.style.height.replace("px",""));
        if (this.ToolElement)
        {
            //TODO调整工具条大小
            height-=this.ToolElement.style.height.replace("px","");   //减去工具条的高度
        }

        this.CanvasElement.height=height;
        this.CanvasElement.width=parseInt(this.DivElement.style.width.replace("px",""));
        this.CanvasElement.style.width=this.CanvasElement.width+'px';
        this.CanvasElement.style.height=this.CanvasElement.height+'px';

        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        this.CanvasElement.height*=pixelTatio;
        this.CanvasElement.width*=pixelTatio;

        JSConsole.Chart.Log(`[JSChart::OnSize] devicePixelRatio=${window.devicePixelRatio}, height=${this.CanvasElement.height}, width=${this.CanvasElement.width}`);

        if (this.JSChartContainer && this.JSChartContainer.Frame)
            this.JSChartContainer.Frame.SetSizeChage(true);

        if (this.JSChartContainer) this.JSChartContainer.Draw();
    }

    //手机屏需要调整 间距
    this.AdjustChartBorder=function(chart)
    {
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率

        chart.Frame.ChartBorder.Left*=pixelTatio;
        chart.Frame.ChartBorder.Right*=pixelTatio;
        chart.Frame.ChartBorder.Top*=pixelTatio;
        chart.Frame.ChartBorder.Bottom*=pixelTatio;
    }

    this.AdjustTitleHeight=function(chart)
    {
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率

        for(let i in chart.Frame.SubFrame)
        {
            chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight*=pixelTatio;
        }

        chart.ChartCorssCursor.TextHeight*=pixelTatio;  //十字光标文本信息高度
    }

    //历史K线图
    this.CreateKLineChartContainer=function(option)
    {
        var chart=null;
        if (option.Type==="历史K线图横屏") chart=new KLineChartHScreenContainer(this.CanvasElement);
        else chart=new KLineChartContainer(this.CanvasElement);

        if (option.NetworkFilter) chart.NetworkFilter=option.NetworkFilter;

        //创建改参数div
        chart.ModifyIndexDialog=this.ModifyIndexDialog;
        chart.ChangeIndexDialog=this.ChangeIndexDialog;
        chart.MinuteDialog=this.MinuteDialog;
        
        //右键菜单
        if (option.IsShowRightMenu==true) chart.RightMenu=new KLineRightMenu(this.DivElement);
        if (option.ScriptError) chart.ScriptErrorCallback=option.ScriptError;
        chart.SelectRectRightMenu=new KLineSelectRightMenu(this.DivElement);
        if (option.EnableScrollUpDown==true) chart.EnableScrollUpDown=option.EnableScrollUpDown;
        if (option.DisableMouse==true) chart.DisableMouse=option.DisableMouse;
        if (option.TouchMoveMinAngle) chart.TouchMoveMinAngle=option.TouchMoveMinAngle;

        if (option.KLine)   //k线图的属性设置
        {
            if (option.KLine.DragMode>=0) chart.DragMode=option.KLine.DragMode;
            if (option.KLine.Right>=0) chart.Right=option.KLine.Right;
            if (option.KLine.Period>=0) chart.Period=option.KLine.Period;
            if (option.KLine.MaxReqeustDataCount>0) chart.MaxReqeustDataCount=option.KLine.MaxReqeustDataCount;
            if (option.KLine.Info && option.KLine.Info.length>0) chart.SetKLineInfo(option.KLine.Info,false);
            if (option.KLine.IndexTreeApiUrl) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl;
            if (option.KLine.KLineDoubleClick==false) chart.MinuteDialog=this.MinuteDialog=null;
            if (option.KLine.IndexTreeApiUrl!=null) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl;
            if (option.KLine.IsShowTooltip==false) chart.IsShowTooltip=false;
            if (option.KLine.MaxRequestMinuteDayCount>0) chart.MaxRequestMinuteDayCount=option.KLine.MaxRequestMinuteDayCount;
            if (option.KLine.DrawType) chart.KLineDrawType=option.KLine.DrawType;
            if (option.KLine.FirstShowDate>20000101) chart.CustomShow={ Date:option.KLine.FirstShowDate };
            if (option.KLine.RightSpaceCount>0) chart.RightSpaceCount=option.KLine.RightSpaceCount;
        }

        if (option.Page)
        {
            if (option.Page.Day && option.Page.Day.Enable==true) chart.Page.Day.Enable=true;
            if (option.Page.Minute && option.Page.Minute.Enable==true) chart.Page.Minute.Enable=true;
        }

        if (option.DragDownload)
        {
            if (option.DragDownload.Day && option.DragDownload.Day.Enable==true) chart.DragDownload.Day.Enable=true;
            if (option.DragDownload.Minute && option.DragDownload.Minute.Enable==true) chart.DragDownload.Minute.Enable=true;
        }

        if (option.Language)
        {
            if (option.Language==='CN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
            else if(option.Language==='EN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID;
        }

        if (option.SourceDatatLimit) chart.SetSourceDatatLimit(option.SourceDatatLimit);

        if (option.DrawPicture) //画图工具
        {
            if (option.DrawPicture.StorageKey && chart.ChartDrawStorage) chart.ChartDrawStorage.Load(option.DrawPicture.StorageKey);
        }

        if (option.StepPixel>0) chart.StepPixel=option.StepPixel;
        if (option.ZoomStepPixel>0) chart.ZoomStepPixel=option.ZoomStepPixel;
        if (option.IsApiPeriod==true) chart.IsApiPeriod=option.IsApiPeriod;

        if (!option.Windows || option.Windows.length<=0) return null;

        //创建子窗口
        chart.Create(option.Windows.length);

        if (option.Border)
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
            if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom;
        }

        this.AdjustChartBorder(chart);

        if (option.KLine)
        {
            if (option.KLine.PageSize > 0)  //一屏显示的数据个数
            {
                let pageSize = chart.GetMaxMinPageSize();
                if (pageSize.Max>10 && pageSize.Max < option.KLine.PageSize) chart.PageSize = pageSize.Max;
                else if (pageSize.Min>10 && pageSize.Min> option.KLine.PageSize) chart.PageSize=pageSize.Min;
                else chart.PageSize = option.KLine.PageSize;
            }
        }

         //取消显示十字光标刻度信息
        if (option.IsCorssOnlyDrawKLine===true) chart.ChartCorssCursor.IsOnlyDrawKLine=option.IsCorssOnlyDrawKLine;
        if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd;
        if (option.IsClickShowCorssCursor==true) chart.IsClickShowCorssCursor=option.IsClickShowCorssCursor;
        if (option.CorssCursorInfo)
        {
            if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left=option.CorssCursorInfo.Left;
            if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right=option.CorssCursorInfo.Right;
            if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom=option.CorssCursorInfo.Bottom;
            if (option.CorssCursorInfo.IsShowCorss===false) chart.ChartCorssCursor.IsShowCorss=option.CorssCursorInfo.IsShowCorss;
            if (option.CorssCursorInfo.IsShowClose == true) chart.ChartCorssCursor.IsShowClose = option.CorssCursorInfo.IsShowClose;    //Y轴显示收盘价
            if (option.CorssCursorInfo.PressTime) chart.PressTime=option.CorssCursorInfo.PressTime; //长按显示十字光标的时间
            if (option.CorssCursorInfo.HPenType>0) chart.ChartCorssCursor.HPenType=option.CorssCursorInfo.HPenType;
            if (option.CorssCursorInfo.VPenType>0) chart.ChartCorssCursor.VPenType=option.CorssCursorInfo.VPenType;
        }

        if (option.SplashTitle) chart.ChartSplashPaint.SplashTitle=option.SplashTitle;

        if (option.Frame)
        {
            for(var i in option.Frame)
            {
                var item=option.Frame[i];
                if (!chart.Frame.SubFrame[i]) continue;
                if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount=item.SplitCount;
                if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat=item.StringFormat;
                if (IFrameSplitOperator.IsNumber(item.FloatPrecision)) chart.Frame.SubFrame[i].Frame.YSplitOperator.FloatPrecision=item.FloatPrecision;
                if (item.Custom) chart.Frame.SubFrame[i].Frame.YSplitOperator.Custom=item.Custom;
                if (IFrameSplitOperator.IsNumber(item.SplitType)) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitType=item.SplitType;
                if (!isNaN(item.Height)) chart.Frame.SubFrame[i].Height = item.Height;
                if (item.IsShowLeftText===false || item.IsShowLeftText===true) 
                {
                    chart.Frame.SubFrame[i].Frame.IsShowYText[0]=item.IsShowLeftText;
                    chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText;            //显示左边刻度
                }
                if (item.IsShowRightText===false || item.IsShowRightText===true) 
                {
                    chart.Frame.SubFrame[i].Frame.IsShowYText[1]=item.IsShowRightText;
                    chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowRightText=item.IsShowRightText;         //显示右边刻度
                }
                if (item.TopSpace>=0) chart.Frame.SubFrame[i].Frame.ChartBorder.TopSpace=item.TopSpace;
                if (item.BottomSpace>=0) chart.Frame.SubFrame[i].Frame.ChartBorder.BottomSpace=item.BottomSpace;
            }
        }

        if (option.KLine) 
        {
            if (option.KLine.ShowKLine == false) chart.ChartPaint[0].IsShow = false;
            if (option.KLine.InfoPosition>0) chart.ChartPaint[0].InfoPosition=option.KLine.InfoPosition;
            if (option.KLine.IsShowMaxMinPrice == false) chart.ChartPaint[0].IsShowMaxMinPrice=option.KLine.IsShowMaxMinPrice;
        }

        if(option.KLineTitle)
        {
            if(option.KLineTitle.IsShowName==false) chart.TitlePaint[0].IsShowName=false;
            if(option.KLineTitle.IsShowSettingInfo==false) chart.TitlePaint[0].IsShowSettingInfo=false;
            if(option.KLineTitle.IsShow == false) chart.TitlePaint[0].IsShow = false;
        }

        //叠加股票
        
        if (option.Overlay)
        {
            for(var i in option.Overlay)
            {
                var item=option.Overlay[i];
                chart.OverlaySymbol(item.Symbol,item);
            }
        }

        if (option.ExtendChart)
        {
            for(var i in option.ExtendChart)
            {
                var item=option.ExtendChart[i];
                chart.CreateExtendChart(item.Name, item);
            }
        }

        //创建子窗口的指标
        let scriptData = new JSIndexScript();

        if (option.ColorIndex)    //五彩K线
        {
            var item=option.ColorIndex;
            let indexInfo=scriptData.Get(item.Index);
            if (indexInfo)
            {
                indexInfo.ID=item.Index;
                chart.ColorIndex=new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args,indexInfo);    //脚本执行
            }
        }

        if (option.TradeIndex)  //交易指标
        {
            var item=option.TradeIndex;
            let indexInfo=scriptData.Get(item.Index);
            if (indexInfo)
            {
                indexInfo.ID=item.Index;
                chart.TradeIndex=new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args,indexInfo);    //脚本执行
            }
        }

        for(var i in option.Windows)
        {
            var item=option.Windows[i];
            if (item.Script)
            {
                chart.WindowIndex[i]=new ScriptIndex(item.Name,item.Script,item.Args,item);    //脚本执行
            }
            else if (item.JsonData)
            {
                chart.WindowIndex[i]=new JsonDataIndex(item.Name,item.Script,item.Args,item);    //脚本执行
            }
            else if (item.Local && item.Local.Data)
            {
                chart.WindowIndex[i]=new LocalJsonDataIndex(item.Local.Name,item.Local.Args,{JsonData:item.Local.Data});
            }
            else if (item.API)  //使用API挂接指标数据 API:{ Name:指标名字, Script:指标脚本可以为空, Args:参数可以为空, Url:指标执行地址 }
            {
                var apiItem=item.API;
                chart.WindowIndex[i]=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,item);
            }
            else
            {
                let indexItem=JSIndexMap.Get(item.Index);
                if (indexItem)
                {
                    chart.WindowIndex[i]=indexItem.Create();
                    chart.CreateWindowIndex(i);
                }
                else
                {
                    let indexInfo = scriptData.Get(item.Index);
                    if (!indexInfo) continue;

                    if (item.Lock) indexInfo.Lock=item.Lock;
                    indexInfo.ID=item.Index;
                    var args=indexInfo.Args;
                    if (item.Args) args=item.Args;
                    if (item.IsShortTitle) indexInfo.IsShortTitle=item.IsShortTitle;
                    if (item.TitleFont) indexInfo.TitleFont=item.TitleFont;
                    chart.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, args,indexInfo);    //脚本执行
                    if (item.StringFormat>0) chart.WindowIndex[i].StringFormat=item.StringFormat;
                    if (item.FloatPrecision>=0) chart.WindowIndex[i].FloatPrecision=item.FloatPrecision;
                }

            }

            if (item.Modify!=null) chart.Frame.SubFrame[i].Frame.ModifyIndex=item.Modify;
            if (item.Change!=null) chart.Frame.SubFrame[i].Frame.ChangeIndex=item.Change;
            if (item.Close!=null) chart.Frame.SubFrame[i].Frame.CloseIndex=item.Close;
            if (item.Overlay!=null) chart.Frame.SubFrame[i].Frame.OverlayIndex=item.Overlay;
            if (item.IsDrawTitleBG==true)  chart.Frame.SubFrame[i].Frame.IsDrawTitleBG=item.IsDrawTitleBG;

            if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight=item.TitleHeight;
        }
        
        //叠加指标宽度
        if (option.OverlayIndexFrameWidth>40) chart.OverlayIndexFrameWidth=option.OverlayIndexFrameWidth;
        //叠加指标
        for (var i in option.OverlayIndex)
        {
            var item=option.OverlayIndex[i];
            if (item.Windows>=chart.Frame.SubFrame.length) continue;
            chart.CreateOverlayWindowsIndex( { WindowIndex:item.Windows,IndexName: item.Index, ShowRightText:item.ShowRightText } );
        }

        this.AdjustTitleHeight(chart);

        return chart;
    }

    //自定义指数历史K线图
    this.CreateCustomKLineChartContainer=function(option)
    {
        var chart=new CustomKLineChartContainer(this.CanvasElement);

        //创建改参数div
        chart.ModifyIndexDialog=this.ModifyIndexDialog;
        chart.ChangeIndexDialog=this.ChangeIndexDialog;
        chart.MinuteDialog=this.MinuteDialog;
        
        //右键菜单
        if (option.IsShowRightMenu==true) chart.RightMenu=new KLineRightMenu(this.DivElement);

        if (option.KLine)   //k线图的属性设置
        {
            if (option.KLine.DragMode>=0) chart.DragMode=option.KLine.DragMode;
            if (option.KLine.Right>=0) chart.Right=option.KLine.Right;
            if (option.KLine.Period>=0) chart.Period=option.KLine.Period;
            if (option.KLine.MaxReqeustDataCount>0) chart.MaxReqeustDataCount=option.KLine.MaxReqeustDataCount;
            if (option.KLine.Info && option.KLine.Info.length>0) chart.SetKLineInfo(option.KLine.Info,false);
            if (option.KLine.IndexTreeApiUrl) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl;
            if (option.KLine.KLineDoubleClick==false) chart.MinuteDialog=this.MinuteDialog=null;
            if (option.KLine.IndexTreeApiUrl!=null) chart.ChangeIndexDialog.IndexTreeApiUrl=option.KLine.IndexTreeApiUrl;
            if (option.KLine.PageSize>0)  chart.PageSize=option.KLine.PageSize;
            if (option.KLine.IsShowTooltip==false) chart.IsShowTooltip=false;
        }

        if (option.CustomStock) chart.CustomStock=option.CustomStock;
        if (option.QueryDate) chart.QueryDate=option.QueryDate;

        if (!option.Windows || option.Windows.length<=0) return null;

        //创建子窗口
        chart.Create(option.Windows.length);

        if (option.Border)
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
        }

        if (option.IsShowCorssCursorInfo==false)    //取消显示十字光标刻度信息
        {
            chart.ChartCorssCursor.IsShowText=option.IsShowCorssCursorInfo;
        }

        if (option.Frame)
        {
            for(var i in option.Frame)
            {
                var item=option.Frame[i];
                if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount=item.SplitCount;
                if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat=item.StringFormat;
            }
        }

        if(option.KLineTitle)
        {
            if(option.KLineTitle.IsShowName==false) chart.TitlePaint[0].IsShowName=false;
            if(option.KLineTitle.IsShowSettingInfo==false) chart.TitlePaint[0].IsShowSettingInfo=false;
        }

        //创建子窗口的指标
        let scriptData = new JSIndexScript();
        for(var i in option.Windows)
        {
            var item=option.Windows[i];
            if (item.Script)
            {
                chart.WindowIndex[i]=new ScriptIndex(item.Name,item.Script,item.Args,item);    //脚本执行
            }
            else
            {
                let indexItem=JSIndexMap.Get(item.Index);
                if (indexItem)
                {
                    chart.WindowIndex[i]=indexItem.Create();
                    chart.CreateWindowIndex(i);
                }
                else
                {
                    let indexInfo = scriptData.Get(item.Index);
                    if (!indexInfo) continue;

                    if (item.Lock) indexInfo.Lock=item.Lock;
                    chart.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args,indexInfo);    //脚本执行
                }
            }
           
            if (item.Modify!=null)
                chart.Frame.SubFrame[i].Frame.ModifyIndex=item.Modify;
            if (item.Change!=null)
                chart.Frame.SubFrame[i].Frame.ChangeIndex=item.Change;

            if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight=item.TitleHeight;
        }

        return chart;
    }

    //分钟走势图
    this.CreateMinuteChartContainer=function(option)
    {
        var chart=null;
        if (option.Type==="分钟走势图横屏") chart=new MinuteChartHScreenContainer(this.CanvasElement);
        else chart=new MinuteChartContainer(this.CanvasElement);

        if (option.NetworkFilter) chart.NetworkFilter=option.NetworkFilter;

        var windowsCount=2;
        if (option.Windows && option.Windows.length>0) windowsCount+=option.Windows.length; //指标窗口从第3个窗口开始
        if (option.EnableScrollUpDown==true) chart.EnableScrollUpDown=option.EnableScrollUpDown;
        if (option.DisableMouse==true) chart.DisableMouse=option.DisableMouse;

        if (option.Language)
        {
            if (option.Language==='CN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
            else if(option.Language==='EN') chart.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID;
        }

        if (option.Info && option.Info.length>0) chart.SetMinuteInfo(option.Info,false);

        chart.Create(windowsCount);                            //创建子窗口

        if (option.CorssCursorInfo)
        {
            if (!isNaN(option.CorssCursorInfo.Left)) chart.ChartCorssCursor.ShowTextMode.Left=option.CorssCursorInfo.Left;
            if (!isNaN(option.CorssCursorInfo.Right)) chart.ChartCorssCursor.ShowTextMode.Right=option.CorssCursorInfo.Right;
            if (!isNaN(option.CorssCursorInfo.Bottom)) chart.ChartCorssCursor.ShowTextMode.Bottom=option.CorssCursorInfo.Bottom;
            if (option.CorssCursorInfo.IsShowCorss===false) chart.ChartCorssCursor.IsShowCorss=option.CorssCursorInfo.IsShowCorss;
        }

        if (option.MinuteInfo) chart.CreateMinuteInfo(option.MinuteInfo);

        if (option.IsShowRightMenu==true) chart.RightMenu=new MinuteRightMenu(this.DivElement);

        if (option.DayCount>1) chart.DayCount=option.DayCount;

        if (option.Border)
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
            if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom;
        }

        if (option.SplashTitle) chart.ChartSplashPaint.SplashTitle=option.SplashTitle;

        this.AdjustChartBorder(chart);

        if (option.Frame)
        {
            for(var i in option.Frame)
            {
                var item=option.Frame[i];
                if (!chart.Frame.SubFrame[i]) continue;
                if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount=item.SplitCount;
                if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat=item.StringFormat;
                if (item.IsShowLeftText==false) chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText;            //显示左边刻度
                if (item.IsShowRightText==false) chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowRightText=item.IsShowRightText;         //显示右边刻度 
                if (item.Height>0) chart.Frame.SubFrame[i].Height = item.Height;
                if (item.Custom) chart.Frame.SubFrame[i].Frame.YSplitOperator.Custom=item.Custom;
            }
        }

        if (option.ExtendChart)
        {
            for(var i in option.ExtendChart)
            {
                var item=option.ExtendChart[i];
                chart.CreateExtendChart(item.Name, item);
            }
        }

        //叠加股票
        if (option.Overlay)
        {
            for(var i in option.Overlay)
            {
                var item=option.Overlay[i];
                chart.OverlaySymbol(item.Symbol,item);
            }
        }

        if (option.MinuteLine)
        {
            if (option.MinuteLine.IsDrawAreaPrice==false) chart.ChartPaint[0].IsDrawArea=false;
            if (option.MinuteLine.IsShowLead==false) chart.IsShowLead=false;
            if (option.MinuteLine.IsShowAveragePrice==false) chart.ChartPaint[1].IsShow=false;
            if (option.MinuteLine.SplitType===1) chart.Frame.SubFrame[0].Frame.YSplitOperator.SplitType=option.MinuteLine.SplitType;
        }

        if(option.MinuteTitle)
        {
            if(option.MinuteTitle.IsShowName==false) chart.TitlePaint[0].IsShowName=false;
            if(option.MinuteTitle.IsShowDate===false || option.MinuteTitle.IsShowDate===true) chart.TitlePaint[0].IsShowDate=option.MinuteTitle.IsShowDate;
            //if(option.KLineTitle.IsShow == false) chart.TitlePaint[0].IsShow = false;
        }

        if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd;
        if (option.IsShowBeforeData===true) chart.IsShowBeforeData=option.IsShowBeforeData;

        //分钟数据指标从第3个指标窗口设置
        let scriptData = new JSIndexScript();
        for(var i in option.Windows)
        {
            var item=option.Windows[i];
            if (item.Script)
            {
                chart.WindowIndex[2+parseInt(i)]=new ScriptIndex(item.Name,item.Script,item.Args);    //脚本执行
            }
            else if (item.API)  //使用API挂接指标数据 API:{ Name:指标名字, Script:指标脚本可以为空, Args:参数可以为空, Url:指标执行地址 }
            {
                var apiItem=item.API;
                chart.WindowIndex[2+parseInt(i)]=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,item);
            }
            else
            {
                var indexItem=JSIndexMap.Get(item.Index);
                if (indexItem)
                {
                    chart.WindowIndex[2+parseInt(i)]=indexItem.Create();       //创建子窗口的指标
                    chart.CreateWindowIndex(2+parseInt(i));
                }
                else
                {
                    let indexInfo = scriptData.Get(item.Index);
                    if (!indexInfo) continue;
                    indexInfo.ID=item.Index;
                    var args=indexInfo.Args;
                    if (item.Args) args=item.Args;
                    chart.WindowIndex[2+parseInt(i)] = new ScriptIndex(indexInfo.Name, indexInfo.Script, args,indexInfo);    //脚本执行
                    if (item.StringFormat>0) chart.WindowIndex[2+parseInt(i)].StringFormat=item.StringFormat;
                    if (item.FloatPrecision>=0) chart.WindowIndex[2+parseInt(i)].FloatPrecision=item.FloatPrecision;
                }
            }

            if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[2+parseInt(i)].Frame.ChartBorder.TitleHeight=item.TitleHeight;
        }
        this.AdjustTitleHeight(chart);

        if (option.Function)
        {
            var func=option.Function;
            if (func.RequestMinuteData) chart.RequestMinuteData=func.RequestMinuteData;
            if (func.RecvMinuteData) chart.RecvMinuteData=func.RecvMinuteData;
        }

        return chart;
    }

    //历史分钟走势图
    this.CreateHistoryMinuteChartContainer=function(option)
    {
        var chart=new HistoryMinuteChartContainer(this.CanvasElement);

        var windowsCount=2;
        if (option.Windows && option.Windows.length>0) windowsCount+=option.Windows.length; //指标窗口从第3个窗口开始

        chart.Create(windowsCount);                            //创建子窗口

        if (option.IsShowCorssCursorInfo==false)    //取消显示十字光标刻度信息
        {
            chart.ChartCorssCursor.IsShowText=option.IsShowCorssCursorInfo;
        }

        if (option.Border)
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
            if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom;
        }

        let scriptData = new JSIndexScript();
        for(var i in option.Windows)
        {
            var item=option.Windows[i];
            if (item.Script)
            {
                chart.WindowIndex[2+parseInt(i)]=new ScriptIndex(item.Name,item.Script,item.Args);    //脚本执行
            }
            else
            {
                var indexItem=JSIndexMap.Get(item.Index);
                if (indexItem)
                {
                    chart.WindowIndex[2+parseInt(i)]=indexItem.Create();       //创建子窗口的指标
                    chart.CreateWindowIndex(2+parseInt(i));
                }
                else
                {
                    let indexInfo = scriptData.Get(item.Index);
                    if (!indexInfo) continue;

                    chart.WindowIndex[2+parseInt(i)] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args);    //脚本执行
                }
            }

            if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[2+parseInt(i)].Frame.ChartBorder.TitleHeight=item.TitleHeight;
        }

        chart.TradeDate=20181009;
        if (option.HistoryMinute.TradeDate) chart.TradeDate=option.HistoryMinute.TradeDate;
        if (option.HistoryMinute.IsShowName!=null) chart.TitlePaint[0].IsShowName=option.HistoryMinute.IsShowName;  //动态标题是否显示股票名称
        if (option.HistoryMinute.IsShowDate!=null) chart.TitlePaint[0].IsShowDate=option.HistoryMinute.IsShowDate;  //动态标题是否显示日期

        return chart;
    }

    this.CreateKLineTrainChartContainer=function(option)
    {
        var bHScreen=(option.Type=='K线训练横屏'? true:false);
        var chart=new KLineTrainChartContainer(this.CanvasElement,bHScreen);

        if (option.NetworkFilter) chart.NetworkFilter=option.NetworkFilter;
        if (option.IsApiPeriod==true) chart.IsApiPeriod=option.IsApiPeriod;

        //创建改参数div
        chart.ModifyIndexDialog=this.ModifyIndexDialog;
        chart.ChangeIndexDialog=this.ChangeIndexDialog;

        if (option.ScriptError) chart.ScriptErrorCallback=option.ScriptError;

        if (option.KLine)   //k线图的属性设置
        {
            if (option.KLine.Right>=0) chart.Right=option.KLine.Right;
            if (option.KLine.Period>=0) chart.Period=option.KLine.Period;
            if (option.KLine.MaxReqeustDataCount>0) chart.MaxReqeustDataCount=option.KLine.MaxReqeustDataCount;
            if (option.KLine.Info && option.KLine.Info.length>0) chart.SetKLineInfo(option.KLine.Info,false);
            if (option.KLine.PageSize>0)  chart.PageSize=option.KLine.PageSize;
            if (option.KLine.IsShowTooltip==false) chart.IsShowTooltip=false;
            if (option.KLine.MaxRequestMinuteDayCount>0) chart.MaxRequestMinuteDayCount=option.KLine.MaxRequestMinuteDayCount;
            if (option.KLine.DrawType) chart.KLineDrawType=option.KLine.DrawType;
        }

        if (option.Train)
        {
            if (option.Train.DataCount) chart.TrainDataCount=option.Train.DataCount;
            if (option.Train.Callback) chart.TrainCallback=option.Train.Callback;
        }

        if (!option.Windows || option.Windows.length<=0) return null;

        //创建子窗口
        chart.Create(option.Windows.length);

        if (option.Border)
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
            if (!isNaN(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom;
        }

        this.AdjustChartBorder(chart);

        if (option.IsShowCorssCursorInfo==false) chart.ChartCorssCursor.IsShowText=option.IsShowCorssCursorInfo; //取消显示十字光标刻度信息
        if (option.IsCorssOnlyDrawKLine===true) chart.ChartCorssCursor.IsOnlyDrawKLine=option.IsCorssOnlyDrawKLine;
        if (option.CorssCursorTouchEnd===true) chart.CorssCursorTouchEnd = option.CorssCursorTouchEnd;
        if (option.IsClickShowCorssCursor==true) chart.IsClickShowCorssCursor=option.IsClickShowCorssCursor;

        if (option.Frame)
        {
            for(var i in option.Frame)
            {
                var item=option.Frame[i];
                if (!chart.Frame.SubFrame[i]) continue;
                if (item.SplitCount) chart.Frame.SubFrame[i].Frame.YSplitOperator.SplitCount=item.SplitCount;
                if (item.StringFormat) chart.Frame.SubFrame[i].Frame.YSplitOperator.StringFormat=item.StringFormat;
                if (item.IsShowLeftText==false) chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowLeftText=item.IsShowLeftText;            //显示左边刻度
                if (item.IsShowRightText==false) chart.Frame.SubFrame[i].Frame.YSplitOperator.IsShowRightText=item.IsShowRightText;         //显示右边刻度 
            }
        }

        if (option.ExtendChart)
        {
            for(var i in option.ExtendChart)
            {
                var item=option.ExtendChart[i];
                chart.CreateExtendChart(item.Name, item);
            }
        }

        //股票名称 日期 周期都不显示
        chart.TitlePaint[0].IsShowName=false;
        chart.TitlePaint[0].IsShowSettingInfo=false;
        chart.TitlePaint[0].IsShowDateTime=false;

        //创建子窗口的指标
        let scriptData = new JSIndexScript();
        for(var i in option.Windows)
        {
            var item=option.Windows[i];
            if (item.Script)
            {
                chart.WindowIndex[i]=new ScriptIndex(item.Name,item.Script,item.Args,item);    //脚本执行
            }
            else
            {
                let indexItem=JSIndexMap.Get(item.Index);
                if (indexItem)
                {
                    chart.WindowIndex[i]=indexItem.Create();
                    chart.CreateWindowIndex(i);
                }
                else
                {
                    let indexInfo = scriptData.Get(item.Index);
                    if (!indexInfo) continue;

                    if (item.Lock) indexInfo.Lock=item.Lock;
                    chart.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args,indexInfo);    //脚本执行
                }

            }

            if (item.Modify!=null) chart.Frame.SubFrame[i].Frame.ModifyIndex=item.Modify;
            if (item.Change!=null) chart.Frame.SubFrame[i].Frame.ChangeIndex=item.Change;
            if (item.Close!=null) chart.Frame.SubFrame[i].Frame.CloseIndex=item.Close;

            if (!isNaN(item.TitleHeight)) chart.Frame.SubFrame[i].Frame.ChartBorder.TitleHeight=item.TitleHeight;
        }

        this.AdjustTitleHeight(chart);
        
        return chart;
    }

    //根据option内容绘制图形
    this.SetOption=function(option)
    {
        var chart=null;
        switch(option.Type)
        {
            case "历史K线图":
            case '历史K线图横屏':
                chart=this.CreateKLineChartContainer(option);
                break;
            case "自定义指数历史K线图":
                chart=this.CreateCustomKLineChartContainer(option);
                break;
            case "分钟走势图":
            case "分钟走势图横屏":
                chart=this.CreateMinuteChartContainer(option);
                break;
            case "历史分钟走势图":
                chart=this.CreateHistoryMinuteChartContainer(option);
                break;
            case 'K线训练':
            case 'K线训练横屏':
                chart=this.CreateKLineTrainChartContainer(option);
                break;
            case "简单图形":
                return this.CreateSimpleChart(option);
            case "饼图":
            case '雷达图':
                return this.CreatePieChart(option);
            case '地图':
                return this.CreateMapChart(option);
            default:
                return false;
        }

        if (!chart) return false;

        //是否自动更新
        if (option.IsAutoUpdate!=null) chart.IsAutoUpdate=option.IsAutoUpdate;
        if (option.AutoUpdateFrequency>0) chart.AutoUpdateFrequency=option.AutoUpdateFrequency;

        //设置股票代码
        if (!option.Symbol) return false;
        chart.Draw();
        chart.ChangeSymbol(option.Symbol);

        this.JSChartContainer=chart;
        this.DivElement.JSChart=this;   //div中保存一份
        this.JSChartContainer.Draw();
    }

    this.CreateSimpleChart=function(option)
    {
        var chart=new SimlpleChartContainer(this.CanvasElement);
        if (option.MainDataControl) chart.MainDataControl=option.MainDataControl;

        chart.Create();

        if (option.Border)  //边框设置
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
        }

        
        chart.Draw();
        chart.RequestData();

        this.JSChartContainer=chart;
        this.DivElement.JSChart=this;   //div中保存一份
        this.JSChartContainer.Draw();
    }

    //创建饼图
    this.CreatePieChart=function(option)
    {
        var chart=new PieChartContainer(this.CanvasElement);
        if (option.MainDataControl) chart.MainDataControl=option.MainDataControl;

        if(option.Radius) chart.Radius = option.Radius;
        
        chart.Create();

        if (option.Border)  //边框设置
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
        }

        this.AdjustChartBorder(chart);

        if (option.Frame) 
        {
            if (option.Frame[0].IsShowBorder == false) chart.Frame.IsShowBorder = option.Frame[0].IsShowBorder;
        }
        
        chart.Draw();
        chart.RequestData();

        this.JSChartContainer=chart;
        this.DivElement.JSChart=this;   //div中保存一份
        this.JSChartContainer.Draw();
        
    }

    this.CreateMapChart=function(option)
    {
        var chart=new MapChartContainer(this.CanvasElement);
        if (option.MainDataControl) chart.MainDataControl=option.MainDataControl;

        chart.Create();

        if (option.Border)  //边框设置
        {
            if (!isNaN(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
            if (!isNaN(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
            if (!isNaN(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
        }
        
        chart.Draw();
        chart.RequestData();

        this.JSChartContainer=chart;
        this.DivElement.JSChart=this;   //div中保存一份
        this.JSChartContainer.Draw();
    }

    //创建工具条
    this.CreateToolbar=function(option)
    {

    }

    //创建设置div窗口
    this.CreateSettingDiv=function(option)
    {

    }

    this.Focus=function()
    {
        if (this.CanvasElement) this.CanvasElement.focus();
    }

    //切换股票代码接口
    this.ChangeSymbol=function(symbol)
    {
        if (this.JSChartContainer) this.JSChartContainer.ChangeSymbol(symbol);
    }

    //K线切换指标
    this.ChangeIndex=function(windowIndex,indexName,option)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangeIndex)=='function')
            this.JSChartContainer.ChangeIndex(windowIndex,indexName,option);
    }

    this.ChangeScriptIndex=function(windowIndex,indexData)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangeScriptIndex)=='function')
            this.JSChartContainer.ChangeScriptIndex(windowIndex,indexData);
    }

    this.ChangePyScriptIndex=function(windowIndex, indexData)   //切换py指标
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangePyScriptIndex)=='function')
            this.JSChartContainer.ChangePyScriptIndex(windowIndex,indexData);
    }

    this.GetIndexInfo=function()
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.GetIndexInfo)=='function')
            return this.JSChartContainer.GetIndexInfo();
        else 
            return [];
    }

    this.ChangeInstructionIndex=function(indexName) 
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangeInstructionIndex)=='function')
            this.JSChartContainer.ChangeInstructionIndex(indexName);
    }

    this.ChangeInstructionScriptIndex=function(indexData)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangeInstructionIndex)=='function')
            this.JSChartContainer.ChangeInstructionScriptIndex(indexData);
    }

    this.CancelInstructionIndex=function()
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.CancelInstructionIndex)=='function')
            this.JSChartContainer.CancelInstructionIndex();
    }

    //K线周期切换
    this.ChangePeriod=function(period)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangePeriod)=='function')
            this.JSChartContainer.ChangePeriod(period);
    }

    //K线复权切换
    this.ChangeRight=function(right)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangeRight)=='function')
            this.JSChartContainer.ChangeRight(right);
    }

    //叠加股票
    this.OverlaySymbol=function(symbol,option)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.OverlaySymbol)=='function')
            this.JSChartContainer.OverlaySymbol(symbol,option);
    }

    //删除一个叠加股票
    this.DeleteOverlaySymbol=function(symbol)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.DeleteOverlaySymbol)=='function')
        this.JSChartContainer.DeleteOverlaySymbol(symbol);
    }

    //设置当前屏的起始日期 { Date:起始日期(必填), PageSize:一屏显示的数据个数(可选)}
    this.SetFirstShowDate=function(obj)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.SetFirstShowDate)=='function')
            this.JSChartContainer.SetFirstShowDate(obj);
    }

    //K线切换类型 0=实心K线 1=收盘价线 2=美国线 3=空心K线 4=面积图
    this.ChangeKLineDrawType=function(drawType)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.ChangeKLineDrawType)=='function')
            this.JSChartContainer.ChangeKLineDrawType(drawType);
    }
	
	//指标窗口个数
    this.ChangeIndexWindowCount = function(count){
        if(this.JSChartContainer && typeof(this.JSChartContainer.ChangeIndexWindowCount) == 'function'){
            this.JSChartContainer.ChangeIndexWindowCount(count);
        }
    }
	
	//取消叠加
    this.ClearOverlaySymbol = function(){
        if(this.JSChartContainer && typeof(this.JSChartContainer.ClearOverlaySymbol) == 'function'){
            this.JSChartContainer.ClearOverlaySymbol();
        } 
    }

    this.DeleteKLineInfo=function(infoName)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.DeleteKLineInfo) == 'function')
        {
            this.JSChartContainer.DeleteKLineInfo(infoName);
        } 
    }
    
    this.ClearKLineInfo=function()
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.ClearKLineInfo) == 'function')
        {
            this.JSChartContainer.ClearKLineInfo();
        } 
    }

    this.AddKLineInfo=function(infoName, bUpdate)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.AddKLineInfo) == 'function')
        {
            this.JSChartContainer.AddKLineInfo(infoName,bUpdate);
        } 
    }

    this.ChangMainDataControl=function(dataControl)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.SetMainDataConotrl)=='function') 
            this.JSChartContainer.SetMainDataConotrl(dataControl);
    }

    this.AddOverlayIndex=function(obj) //{WindowIndex:窗口ID, IndexName:指标ID, Identify:叠加指标ID(可选)}
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.AddOverlayIndex)=='function') 
            this.JSChartContainer.AddOverlayIndex(obj);
    }

    this.DeleteOverlayWindowsIndex=function(identify)
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.DeleteOverlayWindowsIndex)=='function') 
            this.JSChartContainer.DeleteOverlayWindowsIndex(identify);
    }

    this.StopAutoUpdate=function()
    {
        if (this.JSChartContainer && typeof(this.JSChartContainer.StopAutoUpdate)=='function') 
            this.JSChartContainer.StopAutoUpdate();
    }

    //设置强制横屏
    this.ForceLandscape=function(bForceLandscape)
    {
        if (this.JSChartContainer) 
        {
            JSConsole.Chart.Log("[JSChart::ForceLandscape] bForceLandscape="+bForceLandscape);
            this.JSChartContainer.IsForceLandscape=bForceLandscape;
        }
    }

    //锁指标
    this.LockIndex=function(lockData)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.LockIndex)=='function')
        {
            JSConsole.Chart.Log('[JSChart:LockIndex] lockData', lockData);
            this.JSChartContainer.LockIndex(lockData);
        }
    }

    //历史分钟数据 更改日期
    this.ChangeTradeDate=function(tradeDate)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.ChangeTradeDate)=='function')
        {
            JSConsole.Chart.Log('[JSChart:ChangeTradeDate] date', tradeDate);
            this.JSChartContainer.ChangeTradeDate(tradeDate);
        }
    }

    //多日走势图
    this.ChangeDayCount=function(count)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.ChangeDayCount)=='function')
        {
            JSConsole.Chart.Log('[JSChart:ChangeDayCount] count', count);
            this.JSChartContainer.ChangeDayCount(count);
        }
    }

    //返回弹幕数据类
    this.StartAnimation=function(option)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.StartAnimation)=='function')
        {
            JSConsole.Chart.Log('[JSChart:StartAnimation] start.');
            return this.JSChartContainer.StartAnimation(option);
        }
    }

    this.StopAnimation=function()
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.StopAnimation)=='function')
        {
            JSConsole.Chart.Log('[JSChart:StopAnimation] start.');
            return this.JSChartContainer.StopAnimation();
        }
    }

    this.SaveToImage = function (format,colorGB)    //format=保存的文件格式,  colorGB=背景色
    {
        if (this.JSChartContainer && typeof (this.JSChartContainer.SaveToImage) == 'function')
            return this.JSChartContainer.SaveToImage(format,colorGB);
    }

    this.SaveToImageUrl=function(obj, callback)
    {
        if (this.JSChartContainer && typeof (this.JSChartContainer.SaveToImageUrl) == 'function')
            return this.JSChartContainer.SaveToImageUrl(obj, callback);
    }

    //事件回调
    this.AddEventCallback=function(obj)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.AddEventCallback)=='function')
        {
            JSConsole.Chart.Log('[JSChart:AddEventCallback] ', obj);
            this.JSChartContainer.AddEventCallback(obj);
        }
    }

    //设置语言 'EN', 'CN'
    this.SetLanguage=function(language)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.SetLanguage)=='function')
        {
            JSConsole.Chart.Log('[JSChart:SetLanguage] ', language);
            this.JSChartContainer.SetLanguage(language);
        }
    }

    //切换指标模板
    this.ChangeIndexTemplate=function(option)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.ChangeIndexTemplate)=='function')
        {
            JSConsole.Chart.Log('[JSChart:ChangeIndexTemplate] ', option);
            this.JSChartContainer.ChangeIndexTemplate(option);
        }
    }

    //画图工具
    this.SetChartDrawOption=function(option)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.SetChartDrawOption)=='function')
        {
            JSConsole.Chart.Log('[JSChart:SetChartDrawOption] ', option);
            this.JSChartContainer.SetChartDrawOption(option);
        }
    }

    this.CreateChartDrawPicture=function(name)
    {
        if(this.JSChartContainer && typeof(this.JSChartContainer.CreateChartDrawPicture)=='function')
        {
            JSConsole.Chart.Log('[JSChart:CreateChartDrawPicture] ', name);
            this.JSChartContainer.CreateChartDrawPicture(name);
        }
    }
}

//初始化
JSChart.Init=function(divElement)
{
    var jsChartControl=new JSChart(divElement);
    jsChartControl.OnSize();

    return jsChartControl;
}

JSChart.SetDomain=function(domain,cacheDomain)
{
    if (domain) g_JSChartResource.Domain=domain;
    if (cacheDomain) g_JSChartResource.CacheDomain=cacheDomain;
}

JSChart.SetPyIndexDomain=function(domain)   //设置py指标计算api域名
{
    if  (domain) g_JSChartResource.PyIndexDomain=domain;
}

//自定义风格
JSChart.SetStyle=function(option)
{
    if (option) g_JSChartResource.SetStyle(option);
}

//value { EN:'', CH:'' }
JSChart.SetTextResource=function(key,value)
{
    g_JSChartLocalization.SetTextResource(key,value);
}

//获取本地化资源
JSChart.GetLocalization=function()  
{
    return g_JSChartLocalization;
}

//获取设备分辨率比
JSChart.GetDevicePixelRatio=function()
{
    return GetDevicePixelRatio();
}

JSChart.CreateGuid=function()
{
    return Guid();
}

JSChart.GetResource=function()  //获取颜色配置 (设置配必须啊在JSChart.Init()之前)
{
    return g_JSChartResource;
}

JSChart.GetMinuteTimeStringData=function()
{
    return g_MinuteTimeStringData;
}

JSChart.GetMinuteCoordinateData=function()
{
    return g_MinuteCoordinateData;
}

JSChart.GetKLineZoom=function() //K线缩放配置
{
    return ZOOM_SEED;
}

var JSCHART_EVENT_ID=
{
    RECV_KLINE_MATCH:1, //接收到形态匹配
    RECV_INDEX_DATA:2,  //接收指标数据
    RECV_HISTROY_DATA:3,//接收到历史数据
    RECV_TRAIN_MOVE_STEP:4, //接收K线训练,移动一次K线
    CHART_STATUS:5,          //每次Draw() 以后会调用
    BARRAGE_PLAY_END:6,      //单个弹幕播放完成
    RECV_OVERLAY_INDEX_DATA:7,//接收叠加指标数据
    DBCLICK_KLINE:8,            //双击K线图
    RECV_START_AUTOUPDATE:9,    //开始自动更新
    RECV_STOP_AUTOUPDATE:10,    //停止自动更新
    ON_CONTEXT_MENU:11,         //右键菜单事件
    ON_TITLE_DRAW:12,           //标题信息绘制事件
    ON_SELECT_RECT:13,          //区间选择事件通知
    RECV_MINUTE_DATA:14,        //分时图数据到达
    ON_CLICK_INDEXTITLE:15,     //点击指标标题事件
    RECV_KLINE_UPDATE_DATA:16,   //K线日,分钟更新数据到达
    ON_CLICK_DRAWPICTURE:17,    //点击画图工具 
    ON_FINISH_DRAWPICTURE:18,    //完成画图工具    
    ON_INDEXTITLE_DRAW:19,       //指标标题重绘事件
    ON_CUSTOM_VERTICAL_DRAW:20,  //自定义X轴绘制事件
}

var JSCHART_OPERATOR_ID=
{
    OP_SCROLL_LEFT:1,
    OP_SCROLL_RIGHT:2,
    OP_ZOOM_OUT:3,  //缩小
    OP_ZOOM_IN:4,   //放大
    OP_GOTO_HOME:5, //第1页数据
}

var JSCHART_DRAG_ID=
{
    DISABLE_DRAG_ID:0,
    CLICK_TOUCH_MODE_ID:3   //长按十字光标显示保留/点击十字光标消失 (使用TouchStatus)
}
/*
    图形控件
*/
function JSChartContainer(uielement)
{
    this.ClassName='JSChartContainer';
    var _self = this;
    this.Frame;                                     //框架画法
    this.ChartPaint=new Array();                    //图形画法
    this.ChartPaintEx=[];                           //图形扩展画法
    this.ChartInfo=new Array();                     //K线|走势图上信息地雷
    this.ChartInfoPaint;                            //信息地理
    this.ExtendChartPaint=new Array();              //扩展画法
    this.TitlePaint=new Array();                    //标题画法
    this.OverlayChartPaint=new Array();             //叠加信息画法
    this.ChartDrawPicture=new Array();              //画图工具
    this.ChartDrawStorage;                          //画图工具保存
    this.ChartDrawOption={ IsLockScreen:false, Zoom:5 };   //画图工具设置 { IsLockScreen://是否锁住屏幕, Zoom: //线段|点放大倍数 }
    this.CurrentChartDrawPicture=null;              //当前的画图工具
    this.SelectChartDrawPicture=null;               //当前选中的画图
    this.ChartCorssCursor;                          //十字光标
    this.IsClickShowCorssCursor=false;              //手势点击显示十字光标
    this.ChartSplashPaint=null;                     //等待提示
    this.Canvas=uielement.getContext("2d");         //画布
    this.UIElement=uielement;
    this.MouseDrag;
    this.DragMode=1;                                //拖拽模式 0 禁止拖拽 1 数据拖拽 2 区间选择 3(CLICK_TOUCH_MODE_ID)=长按十字光标显示保留/点击十字光标消失 (使用TouchStatus)
    this.TouchStatus={ CorssCursorShow:false },     //十字光标是否显示
    this.DragTimer;
    this.EnableScrollUpDown=false;                  //是否可以上下滚动图形(手机端才有)

    this.CursorIndex=0;             //十字光标X轴索引
    this.LastPoint=new Point();     //鼠标位置
    this.IsForceLandscape=false;    //是否强制横屏
    this.CorssCursorTouchEnd = false;   //手离开屏幕自动隐藏十字光标
    this.StepPixel=4;                   //移动一个数据需要的像素
    this.ZoomStepPixel=5;               //放大缩小手势需要的最小像素
    this.TouchMoveMinAngle=70;          //左右移动最小角度
    this.EnableAnimation=false;         //是否开启动画

    //tooltip提示信息
    this.Tooltip=document.createElement("div");
    this.Tooltip.className='jschart-tooltip';
    this.Tooltip.style.background=g_JSChartResource.TooltipBGColor;
    this.Tooltip.style.opacity=g_JSChartResource.TooltipAlpha;
    this.Tooltip.id=Guid();
    uielement.parentNode.appendChild(this.Tooltip);
    this.IsShowTooltip=true;    //是否显示K线tooltip

    //区间选择
    this.SelectRect=document.createElement("div");
    this.SelectRect.className="jschart-selectrect";
    this.SelectRect.style.background=g_JSChartResource.SelectRectBGColor;
    //this.SelectRect.style.opacity=g_JSChartResource.SelectRectAlpha;
    this.SelectRect.id=Guid();
    uielement.parentNode.appendChild(this.SelectRect);
    
    //区间选择右键菜单
    this.SelectRectRightMenu;   

    //坐标轴风格方法 double-更加数值型分割  price-更加股票价格分割
    this.FrameSplitData=new Map();
    this.FrameSplitData.set("double",new SplitData());
    this.FrameSplitData.set("price",new PriceSplitData());

    //事件回调
    this.mapEvent=new Map();   //通知外部调用 key:JSCHART_EVENT_ID value:{Callback:回调,}

    this.IsOnTouch = false;     //是否再操作数据
    this.TouchDrawCount = 0;    //手势绘制次数
    this.DisableMouse=false;    //禁止鼠标事件
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
    this.PressTime=500;

    this.NetworkFilter;         //网络请求回调 function(data, callback);

    //设置事件回调
    //{event:事件id, callback:回调函数}
    this.AddEventCallback=function(object)
    {
        if (!object || !object.event || !object.callback) return;

        var data={Callback:object.callback, Source:object};
        this.mapEvent.set(object.event,data);
    }

    this.RemoveEventCallback=function(eventid)
    {
        if (!this.mapEvent.has(eventid)) return;

        this.mapEvent.delete(eventid);
    }

    this.GetEventCallback=function(id)  //获取事件回调
    {
        if (!this.mapEvent.has(id)) return null;
        var item=this.mapEvent.get(id);
        return item;
    }

    //接收指标数据
    this.GetIndexEvent=function()
    {
        return this.GetEventCallback(JSCHART_EVENT_ID.RECV_INDEX_DATA);
    }

    this.GetOverlayIndexEvent=function()
    {
        return this.GetEventCallback(JSCHART_EVENT_ID.RECV_OVERLAY_INDEX_DATA);
    }

    //鼠标事件绑定
    uielement.onmousemove=(e)=>{ this.UIOnMouseMove(e);}
    uielement.oncontextmenu=(e)=> { return this.UIOnContextMenu(e); }
    uielement.ondblclick=(e)=>{ this.UIOnDblClick(e); }
    uielement.onmousedown=(e)=> { this.UIOnMouseDown(e); }

    this.UIOnMouseMove=function(e)
    {
        //JSConsole.Chart.Log('[JSChartContainer.UIOnMouseMove] e.clientX, e.clientY, left, top ',e.clientX, e.clientY, this.getBoundingClientRect().left,this.getBoundingClientRect().top);
        var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速
        var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
        var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;

        //加载数据中,禁用鼠标事件
        if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return;
        if (this.DisableMouse==true) return;

        this.OnMouseMove(x,y,e);
    }

    this.UIOnContextMenu=function(e)
    {
        var x = e.clientX-this.UIElement.getBoundingClientRect().left;
        var y = e.clientY-this.UIElement.getBoundingClientRect().top;

        if(typeof(this.OnRightMenu)=='function') this.OnRightMenu(x,y,e);   //右键菜单事件

        return false;
    }

    this.UIOnDblClick=function(e)
    {
        var x = e.clientX-this.UIElement.getBoundingClientRect().left;
        var y = e.clientY-this.UIElement.getBoundingClientRect().top;
        this.OnDoubleClick(x,y,e);
    }

    this.UIOnMouseDown=function(e)
    {
        if(this.DragMode==0) return;

        var pixelTatio = GetDevicePixelRatio();
        this.IsOnTouch=true;

        if (this.TryClickLock)
        {
            //JSConsole.Chart.Log('[uielement.onmousedown] left, top ',e.clientX, e.clientY, this.getBoundingClientRect().left,this.getBoundingClientRect().top);
            var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
            var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
            if (this.TryClickLock(x,y)) return;
        }

        this.HideSelectRect();
        if (this.SelectRectRightMenu) this.SelectRectRightMenu.Hide();
        if (this.ChartPictureMenu) this.ChartPictureMenu.Hide();

        var drag=
        {
            "Click":{},
            "LastMove":{}  //最后移动的位置
        };

        drag.Click.X=e.clientX;
        drag.Click.Y=e.clientY;
        drag.LastMove.X=e.clientX;
        drag.LastMove.Y=e.clientY;

        this.MouseDrag=drag;
        this.SelectChartDrawPicture=null;
        if (this.CurrentChartDrawPicture)  //画图工具模式
        {
            var drawPicture=this.CurrentChartDrawPicture;
            if (drawPicture.Status==2)
                this.SetChartDrawPictureThirdPoint(drag.Click.X,drag.Click.Y);
            else
            {
                this.SetChartDrawPictureFirstPoint(drag.Click.X,drag.Click.Y);
                //只有1个点 直接完成
                if (this.FinishChartDrawPicturePoint()) this.DrawDynamicInfo();
            }
        }
        else    //是否在画图工具上
        {
            var drawPictrueData={};
            var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速
            drawPictrueData.X=(e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
            drawPictrueData.Y=(e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
            if (this.GetChartDrawPictureByPoint(drawPictrueData))
            {
                drawPictrueData.ChartDrawPicture.Status=20;
                drawPictrueData.ChartDrawPicture.ValueToPoint();
                drawPictrueData.ChartDrawPicture.MovePointIndex=drawPictrueData.PointIndex;
                this.CurrentChartDrawPicture=drawPictrueData.ChartDrawPicture;
                this.SelectChartDrawPicture=drawPictrueData.ChartDrawPicture;
                this.OnSelectChartPicture(drawPictrueData.ChartDrawPicture);    //选中画图工具事件
            }
        }

        document.onmousemove=(e)=>{ this.DocOnMouseMove(e); }
        document.onmouseup=(e)=> { this.DocOnMouseUp(e); }
    }

    this.DocOnMouseMove=function(e)
    {
        //加载数据中,禁用鼠标事件
        if (this.ChartSplashPaint && this.IsEnableSplash == true) return;

        var drag=this.MouseDrag;
        if (!drag) return;

        var moveSetp=Math.abs(drag.LastMove.X-e.clientX);

        if (this.CurrentChartDrawPicture)
        {
            var drawPicture=this.CurrentChartDrawPicture;
            if (drawPicture.Status==1 || drawPicture.Status==2)
            {
                if(Math.abs(drag.LastMove.X-e.clientX)<5 && Math.abs(drag.LastMove.Y-e.clientY)<5) return;
                if(this.SetChartDrawPictureSecondPoint(e.clientX,e.clientY))
                {
                    this.DrawDynamicInfo();
                }
            }
            else if (drawPicture.Status==3)
            {
                if(this.SetChartDrawPictureThirdPoint(e.clientX,e.clientY))
                {
                    this.DrawDynamicInfo();
                }
            }
            else if (drawPicture.Status==20)    //画图工具移动
            {
                if(Math.abs(drag.LastMove.X-e.clientX)<5 && Math.abs(drag.LastMove.Y-e.clientY)<5) return;

                if(this.MoveChartDrawPicture(e.clientX-drag.LastMove.X,e.clientY-drag.LastMove.Y))
                {
                    this.DrawDynamicInfo();
                }
            }

            drag.LastMove.X=e.clientX;
            drag.LastMove.Y=e.clientY;
        }
        else if (this.DragMode==1)  //数据左右拖拽
        {
            if (moveSetp<5) return;

            var isLeft=true;
            if (drag.LastMove.X<e.clientX) isLeft=false;//右移数据

            this.UIElement.style.cursor="pointer";

            if(this.DataMove(moveSetp,isLeft))
            {
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.ResetFrameXYSplit();
                this.Draw();
            }
            else
            {
                if (this.DragDownloadData) this.DragDownloadData();
            }

            drag.LastMove.X=e.clientX;
            drag.LastMove.Y=e.clientY;
        }
        else if (this.DragMode==2) //区间选择
        {
            var yMoveSetp=Math.abs(drag.LastMove.Y-e.clientY);

            if (moveSetp<5 && yMoveSetp<5) return;

            var x=drag.Click.X-uielement.getBoundingClientRect().left;
            var y=drag.Click.Y-uielement.getBoundingClientRect().top;
            var x2=e.clientX-uielement.getBoundingClientRect().left;
            var y2=e.clientY-uielement.getBoundingClientRect().top;
            this.ShowSelectRect(x,y,x2,y2);

            drag.LastMove.X=e.clientX;
            drag.LastMove.Y=e.clientY;
        }
    }

    this.DocOnMouseUp=function(e)
    {
        //清空事件
        document.onmousemove=null;
        document.onmouseup=null;

        var bClearDrawPicture=true;
        if (this.CurrentChartDrawPicture)
        {
            var drawPicture=this.CurrentChartDrawPicture;
            if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3)
            {
                drawPicture.PointStatus=drawPicture.Status;
                if (this.FinishChartDrawPicturePoint())
                    this.DrawDynamicInfo();
                else
                    bClearDrawPicture=false;
            }
            else if (drawPicture.Status==20)
            {
                if (this.FinishMoveChartDrawPicture())
                    this.DrawDynamicInfo();
            }
        }
        else if (this.DragMode==2)  //区间选择
        {
            var drag=this.MouseDrag;

            var selectData=new SelectRectData();
            //区间起始位置 结束位子
            selectData.XStart=drag.Click.X-uielement.getBoundingClientRect().left;
            selectData.XEnd=drag.LastMove.X-uielement.getBoundingClientRect().left;
            selectData.JSChartContainer=this.JSChartContainer;
            selectData.Stock={Symbol:this.Symbol, Name:this.Name};

            if (this.GetSelectRectData(selectData))
            {
                var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SELECT_RECT);
                if (event && event.Callback)
                {
                    var data=
                    { 
                        X:drag.LastMove.X-uielement.getBoundingClientRect().left,
                        Y:drag.LastMove.Y-uielement.getBoundingClientRect().top,
                        SelectData:selectData,                      //区间选择的数据 
                    };
                    event.Callback(event,data,this);
                }

                if (this.SelectRectRightMenu)
                {
                    e.data=
                    {
                        Chart:this,
                        X:drag.LastMove.X-uielement.getBoundingClientRect().left,
                        Y:drag.LastMove.Y-uielement.getBoundingClientRect().top,
                        SelectData:selectData,          //区间选择的数据
                    };
                    this.SelectRectRightMenu.DoModal(e);
                }
            }
        }

        //清空数据
        JSConsole.Chart.Log('[KLineChartContainer::document.onmouseup]',e);
        this.UIElement.style.cursor="default";
        this.MouseDrag=null;
        this.IsOnTouch=false;
        if (bClearDrawPicture===true) this.CurrentChartDrawPicture=null;
    }


    //判断是单个手指
    this.IsPhoneDragging=function(e)
    {
        // JSConsole.Chart.Log(e);
        var changed=e.changedTouches.length;
        var touching=e.touches.length;

        return changed==1 && touching==1;
    }

    //是否是2个手指操所
    this.IsPhonePinching=function(e)
    {
        var changed=e.changedTouches.length;
        var touching=e.touches.length;

        return (changed==1 || changed==2) && touching==2;
    }

    this.PreventTouchEvent=function(e)
    {
        if (e.cancelable) e.preventDefault();
        e.stopPropagation();
    }

    this.GetToucheData=function(e, isForceLandscape)
    {
        var touches=new Array();
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        for(var i=0; i<e.touches.length; ++i)
        {
            var item=e.touches[i];
            if (isForceLandscape)
            {
                touches.push(
                    {
                        clientX:item.clientY*pixelTatio, clientY:item.clientX*pixelTatio, 
                        pageX:item.pageY*pixelTatio, pageY:item.pageX*pixelTatio
                    });
            }
            else
            {
                touches.push(
                    {
                        clientX:item.clientX*pixelTatio, clientY:item.clientY*pixelTatio, 
                        pageX:item.pageX*pixelTatio, pageY:item.pageY*pixelTatio
                    });
            }
        }

        return touches;
    }

    this.IsSingleTouch=function(e)  //是否是单点触屏
    {
        var touchCount=e.touches.length;

        return touchCount==1;
    }

    this.StopDragTimer=function()
    {
        if (IFrameSplitOperator.IsNumber(this.DragTimer))
        {
            clearTimeout(this.DragTimer);
            this.DragTimer=null;
        }
    }


    this.GetMoveAngle=function(pt,pt2)  //计算角度
    {
        var xMove=Math.abs(pt.X-pt2.X);
        var yMove=Math.abs(pt.Y-pt2.Y);
        var angle=Math.atan(xMove/yMove)*180/Math.PI;
        return angle;
    }

    //手机拖拽
    uielement.ontouchstart=(e)=> { this.OnTouchStart(e); } 
    uielement.ontouchmove=(e)=> {this.OnTouchMove(e); }
    uielement.ontouchend=(e)=> {this.OnTouchEnd(e); }    

    this.OnTouchStart=function(e)
    {
        this.IsOnTouch=true;
        this.TouchDrawCount=0;
        this.PhonePinch=null;
        this.StopDragTimer();

        var isSingleTouch=this.IsSingleTouch(e);
        if (this.EnableScrollUpDown==false ||  !isSingleTouch || //多点触屏
            (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID && this.TouchStatus.CorssCursorShow==true)) //十字光标显示,不能滚动页面   
        {
            if (e.cancelable) e.preventDefault();
        }

        if (this.IsPhoneDragging(e))
        {
            if (this.TryClickLock || this.TryClickIndexTitle) //指标枷锁区域 , 指标标题点击
            {
                var touches = this.GetToucheData(e, this.IsForceLandscape);
                var x = touches[0].clientX;
                var y = touches[0].clientY;
                var pixelTatio = GetDevicePixelRatio();
                x -= uielement.getBoundingClientRect().left*pixelTatio;    //减去控件的偏移偏移量
                y -= uielement.getBoundingClientRect().top*pixelTatio;
                if (this.TryClickLock && this.TryClickLock(x, y)) return;
                if (this.TryClickIndexTitle && this.TryClickIndexTitle(x,y)) return;
            }

            var bStartTimer=true;
            if (this.ChartDrawOption.IsLockScreen)
            {
                bStartTimer=false;
            }
            else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
            {
                if (this.TouchStatus.CorssCursorShow==true) bStartTimer=false;
            }
            else if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID)
            {
                bStartTimer=false;
            }
            else
            {
                if (!isSingleTouch) bStartTimer=false;
            }

            var drag=
            {
                "Click":{},
                "LastMove":{}  //最后移动的位置
            };

            var touches=this.GetToucheData(e,this.IsForceLandscape);

            drag.Click.X=touches[0].clientX;
            drag.Click.Y=touches[0].clientY;
            drag.LastMove.X=touches[0].clientX;
            drag.LastMove.Y=touches[0].clientY;

            this.MouseDrag=drag;
            if (this.SelectChartDrawPicture) this.SelectChartDrawPicture.IsSelected=false;
            this.SelectChartDrawPicture=null;
            var isDrawPictrue=false;
            if (this.CurrentChartDrawPicture)  //画图工具模式
            {
                var drawPicture=this.CurrentChartDrawPicture;
                if (drawPicture.Status==2)
                    this.SetChartDrawPictureThirdPoint(drag.Click.X,drag.Click.Y);
                else
                {
                    this.SetChartDrawPictureFirstPoint(drag.Click.X,drag.Click.Y);
                    //只有1个点 直接完成
                    if (this.FinishChartDrawPicturePoint()) this.DrawDynamicInfo({Corss:false, Tooltip:false});
                }

                if (e.cancelable) e.preventDefault();
                return;
            }
            else
            {
                var drawPictrueData={};
                var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速
                drawPictrueData.X=(touches[0].clientX-uielement.getBoundingClientRect().left);
                drawPictrueData.Y=(touches[0].clientY-uielement.getBoundingClientRect().top);
                if (this.GetChartDrawPictureByPoint(drawPictrueData))
                {
                    drawPictrueData.ChartDrawPicture.Status=20;
                    drawPictrueData.ChartDrawPicture.ValueToPoint();
                    drawPictrueData.ChartDrawPicture.MovePointIndex=drawPictrueData.PointIndex;
                    drawPictrueData.ChartDrawPicture.IsSelected=true;
                    this.CurrentChartDrawPicture=drawPictrueData.ChartDrawPicture;
                    this.SelectChartDrawPicture=drawPictrueData.ChartDrawPicture;
                    let event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_DRAWPICTURE); //选中画图工具事件
                    if (event && event.Callback)
                    {
                        let sendData={ DrawPicture: drawPictrueData.ChartDrawPicture };
                        event.Callback(event,sendData,this);
                    }

                    if (e.cancelable) e.preventDefault();
                    return;
                }
            }

            if (bStartTimer)
            {
                //长按2秒,十字光标
                var self=this;
                this.DragTimer=setTimeout(function()
                {
                    if (drag.Click.X==drag.LastMove.X && drag.Click.Y==drag.LastMove.Y) //手指没有移动,出现十字光标
                    {
                        var mouseDrag=self.MouseDrag;
                        self.MouseDrag=null;
                        if (self.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) self.TouchStatus.CorssCursorShow=true;    //十字显示
                        self.MoveCorssCursor(drag.Click,e);//移动十字光标
                    }

                }, self.PressTime);
            }

            if (this.ChartDrawOption.IsLockScreen)
            {
                this.MouseDrag=null;
            }
            else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
            {

            }
            else if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID)
            {
                this.MouseDrag=null;
                this.MoveCorssCursor(drag.Click,e);
            }
            else if (this.IsClickShowCorssCursor)
            {
                this.MoveCorssCursor(drag.Click,e);
            }    
        }
        else if (this.IsPhonePinching(e))
        {
            var phonePinch=
            {
                "Start":{},
                "Last":{}
            };

            var touches=this.GetToucheData(e,this.IsForceLandscape);

            phonePinch.Start={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};
            phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};

            this.PhonePinch=phonePinch;
            this.SelectChartDrawPicture=null;
        }
    }

    this.OnTouchMove=function(e)
    {
        var touches=this.GetToucheData(e,this.IsForceLandscape);

        if (this.IsPhoneDragging(e))
        {
            var drag=this.MouseDrag;
            if (drag==null)
            {
                if (this.IsForceLandscape) y=uielement.getBoundingClientRect().width-touches[0].clientY;    //强制横屏Y计算
                if (!this.ChartDrawOption.IsLockScreen) this.MoveCorssCursor({X:touches[0].clientX, Y:touches[0].clientY},e);
            }
            else
            {
                var moveAngle=this.GetMoveAngle(drag.LastMove,{X:touches[0].clientX, Y:touches[0].clientY});
                var moveSetp=Math.abs(drag.LastMove.X-touches[0].clientX);
                var moveUpDown=Math.abs(drag.LastMove.Y-touches[0].clientY);
                moveSetp=parseInt(moveSetp);
                var isMoveCorssCursor=(this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID && this.TouchStatus.CorssCursorShow==true); //是否移动十字光标

                //JSConsole.Chart.Log("[JSChartContainer::uielement.ontouchmove]  moveAngle , moveUpDown", moveAngle,moveUpDown);

                if (this.CurrentChartDrawPicture)
                {
                    var drawPicture=this.CurrentChartDrawPicture;
                    if (drawPicture.Status==1 || drawPicture.Status==2)
                    {
                        if(moveSetp<5 && moveUpDown<5) return;
                        if(this.SetChartDrawPictureSecondPoint(touches[0].clientX,touches[0].clientY))
                        {
                            this.DrawDynamicInfo();
                        }
                    }
                    else if (drawPicture.Status==3)
                    {
                        if(this.SetChartDrawPictureThirdPoint(touches[0].clientX,touches[0].clientY))
                        {
                            this.DrawDynamicInfo();
                        }
                    }
                    else if (drawPicture.Status==20)    //画图工具移动
                    {
                        if(moveSetp<5 && moveUpDown<5) return;

                        if(this.MoveChartDrawPicture(touches[0].clientX-drag.LastMove.X,touches[0].clientY-drag.LastMove.Y))
                        {
                            this.DrawDynamicInfo();
                        }
                    }

                    drag.LastMove.X=touches[0].clientX;
                    drag.LastMove.Y=touches[0].clientY;
                }
                else if (isMoveCorssCursor)  //点击模式下 十字光标显示 左右移动十字光标
                {
                    var mouseDrag=this.MouseDrag;
                    this.MouseDrag=null;
                    this.MoveCorssCursor(drag.Click,e); //移动十字光标
                }
                else if (this.DragMode==1 || isMoveCorssCursor==false)  //数据左右拖拽
                {
                    if ( ((moveUpDown>0 && moveSetp<=3) || moveAngle<=this.TouchMoveMinAngle) && this.EnableScrollUpDown==true ) 
                    {
                        this.StopDragTimer();
                        return;
                    }

                    if (moveSetp<5 || moveAngle<=this.TouchMoveMinAngle) 
                    {
                        this.PreventTouchEvent(e);
                        return;
                    }

                    var isLeft=true;
                    if (drag.LastMove.X<touches[0].clientX) isLeft=false;//右移数据

                    if(this.DataMove(moveSetp,isLeft))
                    {
                        this.UpdataDataoffset();
                        this.UpdatePointByCursorIndex();
                        this.UpdateFrameMaxMin();
                        this.ResetFrameXYSplit();
                        this.Draw();
                    }
                    else
                    {
                        if (this.DragDownloadData) this.DragDownloadData();
                    }

                    drag.LastMove.X=touches[0].clientX;
                    drag.LastMove.Y=touches[0].clientY;
                }
            }
        }
        else if (this.IsPhonePinching(e))
        {
            if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID) return;

            this.PreventTouchEvent(e);
            var phonePinch=this.PhonePinch;
            if (!phonePinch) return;

            var yHeight=Math.abs(touches[0].pageY-touches[1].pageY);
            var yLastHeight=Math.abs(phonePinch.Last.Y-phonePinch.Last.Y2);
            var yStep=yHeight-yLastHeight;

            var xHeight=Math.abs(touches[0].pageX-touches[1].pageX);
            var xLastHeight=Math.abs(phonePinch.Last.X-phonePinch.Last.X2);
            var xStep=xHeight-xLastHeight;
            var minStep=this.ZoomStepPixel;
            if (Math.abs(yStep)<minStep && Math.abs(xStep)<minStep) return;
            var step=yStep;
            if (Math.abs(yStep)<minStep) step=xStep;

            if (step>0)    //放大
            {
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomUp(cursorIndex)) return;
                this.CursorIndex=cursorIndex.Index;
                this.UpdatePointByCursorIndex();
                this.UpdataDataoffset();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
                this.StopDragTimer();
            }
            else        //缩小
            {
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomDown(cursorIndex)) return;
                this.CursorIndex=cursorIndex.Index;
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
                this.StopDragTimer();
            }

            phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};
        }

        this.PreventTouchEvent(e);
    }

    this.OnTouchEnd=function(e)
    {
        JSConsole.Chart.Log('[KLineChartContainer:OnTouchEnd]',e);

        var bClearDrawPicture=true;
        if (this.CurrentChartDrawPicture)
        {
            var drawPicture=this.CurrentChartDrawPicture;
            if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3)
            {
                drawPicture.PointStatus=drawPicture.Status;
                if (this.FinishChartDrawPicturePoint())
                    this.DrawDynamicInfo();
                else
                    bClearDrawPicture=false;
            }
            else if (drawPicture.Status==20)
            {
                if (this.FinishMoveChartDrawPicture())
                    this.DrawDynamicInfo();
            }
        }

        this.IsOnTouch = false;
        if (bClearDrawPicture===true) this.CurrentChartDrawPicture=null;
        this.StopDragTimer();
        this.OnTouchFinished();
        this.TouchDrawCount=0;
    }

    this.MoveCorssCursor=function(point,e)
    {
        var pixelTatio = GetDevicePixelRatio();
        var x = point.X-this.UIElement.getBoundingClientRect().left*pixelTatio;
        var y = point.Y-this.UIElement.getBoundingClientRect().top*pixelTatio;
        if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID) this.TouchStatus.CorssCursorShow=true;    //十字显示
        this.OnMouseMove(x,y,e);
    }

    this.Draw=function()
    {
        if (this.UIElement.width<=0 || this.UIElement.height<=0) return; 

        this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height);
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        this.Canvas.lineWidth=pixelTatio;       //手机端需要根据分辨率比调整线段宽度

        if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash)
        {
            this.Frame.Draw();
            this.ChartSplashPaint.Draw();
            return;
        }
        //框架
        this.Frame.Draw();
        if (this.Frame.DrawCustomVertical) 
        {
            var eventCVericalDraw=this.GetEventCallback(JSCHART_EVENT_ID.ON_CUSTOM_VERTICAL_DRAW);
            this.Frame.DrawCustomVertical(eventCVericalDraw);
        }
        this.Frame.CalculateLock();

        //框架内图形
        for (var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];
            if (item.IsDrawFirst)
                item.Draw();
        }

        for(var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];
            if (!item.IsDrawFirst)
                item.Draw();
        }

        for(var i in this.ChartPaintEx)
        {
            var item=this.ChartPaintEx[i];
            item.Draw();
        }

        //叠加股票
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            item.Draw();
        }

        if (this.Frame.DrawOveraly)
            this.Frame.DrawOveraly();   //画叠加指标

        //固定扩展图形
        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            if (!item.IsDynamic && item.IsAnimation==false) item.Draw();
        }

        if (this.Frame.DrawInsideHorizontal) this.Frame.DrawInsideHorizontal();
        if (this.Frame.DrawCustomHorizontal) this.Frame.DrawCustomHorizontal();
        if (this.ChartInfoPaint) this.ChartInfoPaint.Draw();
        this.Frame.DrawLock();
        this.Frame.Snapshot();

        for(var i in this.ExtendChartPaint) //动态扩展图形
        {
            var item=this.ExtendChartPaint[i];
            if (item.IsDynamic && item.DrawAfterTitle===false) item.Draw();
        }

        if (this.LastPoint.X!=null || this.LastPoint.Y!=null)
        {
            if (this.ChartCorssCursor)
            {
                this.ChartCorssCursor.LastPoint=this.LastPoint;
                this.ChartCorssCursor.CursorIndex=this.CursorIndex;
                
                if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
                {   //点击手势模式下
                    if (this.TouchStatus.CorssCursorShow==true) this.ChartCorssCursor.Draw();
                }
                else if (!(this.CorssCursorTouchEnd===true && this.MouseDrag) && !(this.CorssCursorTouchEnd==true && this.IsOnTouch==false)) 
                {   //移动端 拖拽数据的时候 不显示十字光标, 没有按屏的时候也不显示十字光标
                    this.ChartCorssCursor.Draw();
                }
            }
        }

        var eventTitleDraw=this.GetEventCallback(JSCHART_EVENT_ID.ON_TITLE_DRAW);
        var eventIndexTitleDraw=this.GetEventCallback(JSCHART_EVENT_ID.ON_INDEXTITLE_DRAW);
        for(var i in this.TitlePaint)
        {
            var item=this.TitlePaint[i];
            if (!item.IsDynamic) continue;

            item.CursorIndex=this.CursorIndex;
            if (item.ClassName=='DynamicChartTitlePainting') item.OnDrawEvent=eventIndexTitleDraw
            else item.OnDrawEvent=eventTitleDraw;
            item.Draw();
        }

        for(var i in this.ExtendChartPaint) //动态扩展图形
        {
            var item=this.ExtendChartPaint[i];
            if (item.IsDynamic && item.DrawAfterTitle===true && item.IsAnimation==false) item.Draw();
        }

        if (this.EnableAnimation)
        {
            for(var i in this.ExtendChartPaint)    //动画
            {
                var item=this.ExtendChartPaint[i];
                if (item.IsAnimation===true) item.Draw();
            }
        }

        for(var i in this.ChartDrawPicture)
        {
            var item=this.ChartDrawPicture[i];
            item.Draw();
        }

        if (this.CurrentChartDrawPicture && this.CurrentChartDrawPicture.Status!=10)
        {
            this.CurrentChartDrawPicture.Draw();
        }

        //发送图形状态给外部
        if (this.mapEvent.has(JSCHART_EVENT_ID.CHART_STATUS))
        {
            var event=this.mapEvent.get(JSCHART_EVENT_ID.CHART_STATUS);
            
            var data={  };
            if (typeof(this.GetChartStatus)=='function') data=this.GetChartStatus();
            event.Callback(event,data,this);
        }

        ++this.TouchDrawCount;
    }

    //画动态信息
    this.DrawDynamicInfo=function(option)
    {
        if (this.Frame.ScreenImageData==null) return;

        var isErase=false;
        if (this.ChartCorssCursor)
        {
            if (this.ChartCorssCursor.PointX!=null || this.ChartCorssCursor.PointY!=null)
                isErase=true;
        }

        isErase=true;   //每次都擦除背景
        if (isErase==false)
        {
            for(var i in this.ExtendChartPaint)
            {
                var item=this.ExtendChartPaint[i];
                if (item.IsDynamic && item.IsEraseBG) 
                {
                    isErase=true;
                    break;
                }
            }
        }

        if (isErase) this.Canvas.putImageData(this.Frame.ScreenImageData,0,0);

        for(var i in this.ExtendChartPaint)    //动态扩展图形
        {
            var item=this.ExtendChartPaint[i];
            if (item.IsDynamic && item.DrawAfterTitle===false && item.IsAnimation==false) item.Draw();
        }

        if (this.ChartCorssCursor)
        {
            this.ChartCorssCursor.LastPoint=this.LastPoint;
            this.ChartCorssCursor.CursorIndex=this.CursorIndex;
            if (option && option.Corss==false)
            {

            }
            else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
            {
                if (this.TouchStatus.CorssCursorShow==true) this.ChartCorssCursor.Draw();
            }
            else if ( !(this.IsOnTouch===false && this.CorssCursorTouchEnd===true) && !this.CurrentChartDrawPicture)
            {
                this.ChartCorssCursor.Draw();
            }
        }

        for(var i in this.TitlePaint)
        {
            var item=this.TitlePaint[i];
            if (!item.IsDynamic) continue;

            item.CursorIndex=this.CursorIndex;
            item.Draw();
        }

        for(var i in this.ExtendChartPaint)    //动态扩展图形   在动态标题以后画
        {
            var item=this.ExtendChartPaint[i];
            if (item.ClassName=='KLineTooltipPaint' && option && option.Tooltip==false) continue;
            if (item.IsDynamic && item.DrawAfterTitle===true) item.Draw();
        }

        if (this.EnableAnimation)
        {
            for(var i in this.ExtendChartPaint)    //动画
            {
                var item=this.ExtendChartPaint[i];
                if (item.IsAnimation===true) item.Draw();
            }
        }

        for(var i in this.ChartDrawPicture)
        {
            var item=this.ChartDrawPicture[i];
            item.Draw();
        }

        if (this.CurrentChartDrawPicture && this.CurrentChartDrawPicture.Status!=10)
        {
            this.CurrentChartDrawPicture.Draw();
        }

        ++this.TouchDrawCount;
    }

    this.DrawAnimation=function()   //绘制动画 如弹幕
    {
        if (!this.EnableAnimation) return;

        if (this.Frame.ScreenImageData && !this.IsOnTouch)
        {
            for(var i in this.ExtendChartPaint)
            {
                var item=this.ExtendChartPaint[i];
                if (item.IsAnimation===true) item.IsMoveStep=true;  //移动弹幕
            }

            this.DrawDynamicInfo();
        }
        
        var self=this;
        window.requestAnimationFrame(function() { self.DrawAnimation(); });
    }

    this.StartAnimation=function(option)
    {
        var bCreated=false; //是否已经创建了弹幕画法
        var barrageData=null;
        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            if (item.ClassName==='BarragePaint')
            {
                bCreated=true;
                barrageData=item.BarrageList;
                break;
            }
        }

        if (!bCreated)
        {
            var chart=new BarragePaint();
            chart.Canvas=this.Canvas;
            chart.ChartBorder=this.Frame.ChartBorder;
            chart.ChartFrame=this.Frame;
            chart.HQChart=this;
            chart.SetOption(option);
            this.ExtendChartPaint.push(chart);
            barrageData=chart.BarrageList;
        }

        this.EnableAnimation=true;
        var self=this;
        window.requestAnimationFrame(function() { self.DrawAnimation(); });

        return barrageData;
    }

    this.StopAnimation=function()
    {
        this.EnableAnimation=false;
        this.DrawDynamicInfo();
    }

    this.OnMouseMove=function(x,y,e)
    {
        this.LastPoint.X=x;
        this.LastPoint.Y=y;
        this.CursorIndex=this.Frame.GetXData(x);

        var bDrawPicture=false; //是否正在画图
        if (this.CurrentChartDrawPicture)
        {
            if (this.CurrentChartDrawPicture.SetLastPoint) this.CurrentChartDrawPicture.SetLastPoint({X:x,Y:y});
            bDrawPicture=true;
        }
        else
        {
            var drawPictrueData={};
            drawPictrueData.X=x;
            drawPictrueData.Y=y;
            if (this.GetChartDrawPictureByPoint(drawPictrueData)) 
            {
                if (drawPictrueData.PointIndex===100) this.UIElement.style.cursor="move";
                else this.UIElement.style.cursor="pointer";
                bDrawPicture=true;
            }
            else 
            {
                if (!this.MouseDrag) this.UIElement.style.cursor="default";
            }
        }

        this.DrawDynamicInfo();

        if (this.IsShowTooltip && bDrawPicture==false)
        {
            var toolTip=new TooltipData();
            for(var i in this.ChartPaint)
            {
                var item=this.ChartPaint[i];
                if (item.GetTooltipData(x,y,toolTip))
                {
                    break;
                }
            }

            if (!toolTip.Data)
            {
                for(var i in this.OverlayChartPaint)
                {
                    var item=this.OverlayChartPaint[i];
                    if (item.GetTooltipData(x,y,toolTip))
                    {
                        break;
                    }
                }
            }

            if (toolTip.Data)
            {
                var xTooltip = e.clientX-this.UIElement.getBoundingClientRect().left;
                var yTooltip = e.clientY-this.UIElement.getBoundingClientRect().top;
                this.ShowTooltip(xTooltip,yTooltip,toolTip);
            }
            else
            {
                this.HideTooltip();
            }
        }
    }

    this.OnKeyDown=function(e)
    {
        var keyID = e.keyCode ? e.keyCode :e.which;
        switch(keyID)
        {
            case 37: //left
                if (this.CursorIndex<=0.99999)
                {
                    if (!this.DataMoveLeft()) break;
                    this.UpdataDataoffset();
                    this.UpdatePointByCursorIndex();
                    this.UpdateFrameMaxMin();
                    this.Draw();
                    this.ShowTooltipByKeyDown();
                }
                else
                {
                    --this.CursorIndex;
                    this.UpdatePointByCursorIndex();
                    this.DrawDynamicInfo();
                    this.ShowTooltipByKeyDown();
                }
                break;
            case 39: //right
                var xPointcount=0;
                if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount;
                else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount;
                if (this.CursorIndex+1>=xPointcount)
                {
                    if (!this.DataMoveRight()) break;
                    this.UpdataDataoffset();
                    this.UpdatePointByCursorIndex();
                    this.UpdateFrameMaxMin();
                    this.Draw();
                    this.ShowTooltipByKeyDown();
                }
                else
                {
                    //判断是否在最后一个数据上
                    var data=null;
                    if (this.Frame.Data) data=this.Frame.Data;
                    else data=this.Frame.SubFrame[0].Frame.Data;
                    if (!data) break;
                    if (this.CursorIndex+data.DataOffset+1>=data.Data.length) break;

                    ++this.CursorIndex;
                    this.UpdatePointByCursorIndex();
                    this.DrawDynamicInfo();
                    this.ShowTooltipByKeyDown();
                }
                break;
            case 38:    //up
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomUp(cursorIndex)) break;
                this.CursorIndex=cursorIndex.Index;
                this.UpdatePointByCursorIndex();
                this.UpdataDataoffset();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
                break;
            case 40:    //down
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomDown(cursorIndex)) break;
                this.CursorIndex=cursorIndex.Index;
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
                break;
            case 46:    //del
                if (!this.SelectChartDrawPicture) break;
                var drawPicture=this.SelectChartDrawPicture;
                JSConsole.Chart.Log(drawPicture,"drawPicturedrawPicturedrawPicture")
                this.SelectChartDrawPicture=null;
                this.ClearChartDrawPicture(drawPicture);    //删除选中的画图工具
                break;
            default:
                return;
        }

        //不让滚动条滚动
        if(e.preventDefault) e.preventDefault();
        else e.returnValue = false;
    }

    this.OnDoubleClick=function(x,y,e)
    {
        //JSConsole.Chart.Log(e);
    }

    this.UpdatePointByCursorIndex=function()
    {
        this.LastPoint.X=this.Frame.GetXFromIndex(this.CursorIndex);
        var index=Math.abs(this.CursorIndex-0.5);
        index=parseInt(index.toFixed(0));
        if (this.ClassName=='KLineChartContainer') index=this.CursorIndex;

        var data=this.Frame.Data;
        if (data.DataOffset+index>=data.Data.length)
        {
            return;
        }
        var close=data.Data[data.DataOffset+index].Close;

        this.LastPoint.Y=this.Frame.GetYFromData(close);
    }

    this.ShowTooltipByKeyDown=function()
    {
        var index=Math.abs(this.CursorIndex-0.5);
        index=parseInt(index.toFixed(0));
        if (this.ClassName=='KLineChartContainer') index=this.CursorIndex;

        var data=this.Frame.Data;
        var toolTip=new TooltipData();
        toolTip.Data=data.Data[data.DataOffset+index];
        toolTip.ChartPaint=this.ChartPaint[0];

        var pixelTatio = GetDevicePixelRatio();
        if (pixelTatio===0) pixelTatio=1;   //div 缩放还是使用原始坐标
        this.ShowTooltip(this.LastPoint.X/pixelTatio,this.LastPoint.Y/pixelTatio,toolTip);
    }

    this.ShowTooltip=function(x,y,toolTip)
    {
        if (!this.IsShowTooltip) return;

        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        var xMove=15/pixelTatio;    //顶部坐标偏移位置
        if (toolTip.Type===0) //K线信息
        {
            var format=new HistoryDataStringFormat();
            format.Value=toolTip;
            format.Symbol=this.Symbol;
            format.LanguageID=this.LanguageID;
            if (!format.Operator()) return;
            var textHeight=format.LineCount*25; //每行的行高25

            var scrollPos=GetScrollPosition();
            var left = x;
            var top = y;
            var width=157;
            if (this.LanguageID==JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID) width=180;
            this.Tooltip.style.width = width+"px";
            this.Tooltip.style.height =textHeight+"px";
            //JSConsole.Chart.Log(`[JSChartContainer::ShowTooltip] left=${left} top=${top} xMove=${xMove}` );
            //if (toolTip.ChartPaint.Name=="Overlay-KLine")  this.Tooltip.style.height =220+"px";
            this.Tooltip.style.position = "absolute";
            //JSConsole.Chart.Log('[JSChartContainer::ShowTooltip] getBoundingClientRect() ',this.UIElement.getBoundingClientRect())
            if (left+width>this.UIElement.getBoundingClientRect().width)
                this.Tooltip.style.left = (left-width) + "px";
            else
                this.Tooltip.style.left = left + "px";
            this.Tooltip.style.top = (top + xMove)+ "px";
            this.Tooltip.className='jschart-tooltip';
            this.Tooltip.innerHTML=format.Text;
            this.Tooltip.style.display = "block";
        }
        else if (toolTip.Type===1)   //信息地雷提示信息
        {
            var scrollPos=GetScrollPosition();
            var left = x;
            var top = y;
            var width=500;
            var format=new KLineInfoDataStringFormat();
            format.Value=toolTip;
            format.Symbol=this.Symbol;
            if (!format.Operator()) return;

            this.Tooltip.className='jchart-klineinfo-tooltip';
            this.Tooltip.style.position = "absolute";
            if(left+width>this.UIElement.getBoundingClientRect().width){
                this.Tooltip.style.left = (left-width) + "px";
            }else{
                this.Tooltip.style.left = left + "px";
            }
            
            this.Tooltip.style.top = (top +xMove)+ "px";
            this.Tooltip.style.width = width+"px";
            this.Tooltip.style.height =null;
            this.Tooltip.innerHTML=format.Text;
            this.Tooltip.style.display = "block";
        }
    }

    this.HideTooltip=function()
    {
        this.Tooltip.style.display = "none";
    }

    this.ShowSelectRect=function(x,y,x2,y2)
    {
        var left = x;
        var top = y;

        var borderRight=this.Frame.ChartBorder.GetRight();
        var borderLeft=this.Frame.ChartBorder.GetLeft();

        if (x>borderRight) x=borderRight;
        if (x2>borderRight) x2=borderRight;

        if (x<borderLeft) x=borderLeft;
        if (x2<borderLeft) x2=borderLeft;

        if (x>x2) left=x2;
        if (y>y2) top=y2;

        var width=Math.abs(x-x2);
        var height=Math.abs(y-y2);

        this.SelectRect.style.width = width+"px";
        this.SelectRect.style.height =height+"px";
        this.SelectRect.style.position = "absolute";
        this.SelectRect.style.left = left +"px";
        this.SelectRect.style.top = top +"px";
        this.SelectRect.style.display = "block";
    }

    this.UpdateSelectRect=function(start,end)
    {
        if (!this.ChartPaint[0].Data) return;

        var data=this.ChartPaint[0].Data;
        var offset=data.DataOffset;
        var fixedStart=start-offset;
        var fixedEnd=end-offset;
        var x=this.Frame.GetXFromIndex(fixedStart);
        var x2=this.Frame.GetXFromIndex(fixedEnd);

        JSConsole.Chart.Log('[JSChartContainer::UpdateSelectRect]',start,end,x,x2);
        var scrollPos=GetScrollPosition();
        this.SelectRect.style.left = x + scrollPos.Left+"px";
        this.SelectRect.style.width = (x2-x)+"px";
    }

    this.HideSelectRect=function()
    {
        this.SelectRect.style.display = "none";
    }

    this.ResetFrameXYSplit=function()
    {
        if (typeof(this.Frame.ResetXYSplit)=='function')
            this.Frame.ResetXYSplit();
    }

    this.UpdateFrameMaxMin=function()
    {
        var frameMaxMinData=new Array();

        var chartPaint=new Array();

        for(var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];
            if (item.IsShow==false) continue;   //隐藏的图形不计算
            chartPaint.push(this.ChartPaint[i]);
        }
        for(var i in this.OverlayChartPaint)
        {
            chartPaint.push(this.OverlayChartPaint[i]);
        }

        for(var i in chartPaint)
        {
            var paint=chartPaint[i];
            var range=paint.GetMaxMin();
            if (range==null || range.Max==null || range.Min==null) continue;
            var frameItem=null;
            for(var j in frameMaxMinData)
            {
                if (frameMaxMinData[j].Frame==paint.ChartFrame)
                {
                    frameItem=frameMaxMinData[j];
                    break;
                }
            }

            if (frameItem)
            {
                if (frameItem.Range.Max<range.Max) frameItem.Range.Max=range.Max;
                if (frameItem.Range.Min>range.Min) frameItem.Range.Min=range.Min;
            }
            else
            {
                frameItem={};
                frameItem.Frame=paint.ChartFrame;
                frameItem.Range=range;
                frameMaxMinData.push(frameItem);
            }
        }

        for(var i in frameMaxMinData)
        {
            var item=frameMaxMinData[i];
            if (!item.Frame || !item.Range) continue;
            if (item.Range.Max==null || item.Range.Min==null) continue;
            if (item.Frame.YSpecificMaxMin)
            {
                item.Frame.HorizontalMax=item.Frame.YSpecificMaxMin.Max;
                item.Frame.HorizontalMin=item.Frame.YSpecificMaxMin.Min;
            }
            else
            {
                item.Frame.HorizontalMax=item.Range.Max;
                item.Frame.HorizontalMin=item.Range.Min;
            }
            item.Frame.XYSplit=true;
        }

        //更新子坐标
        for(var i in this.Frame.SubFrame)
        {
            var subFrame=this.Frame.SubFrame[i];
            for(var j in subFrame.OverlayIndex)
            {
                var overlayItem=subFrame.OverlayIndex[j];
                overlayItem.UpdateFrameMaxMin();
            }
        }
    }

    this.DataMoveLeft=function()
    {
        var data=null;
        if (!this.Frame.Data) data=this.Frame.Data;
        else data=this.Frame.SubFrame[0].Frame.Data;
        if (!data) return false;
        if (data.DataOffset<=0) return false;
        --data.DataOffset;
        return true;
    }

    this.DataMoveRight=function()
    {
        var data=null;
        if (!this.Frame.Data) data=this.Frame.Data;
        else data=this.Frame.SubFrame[0].Frame.Data;
        if (!data) return false;

        var xPointcount=0;
        if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount;
        else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount;
        if (!xPointcount) return false;

        if (xPointcount+data.DataOffset>=data.Data.length) return false;

        ++data.DataOffset;
        return true;
    }

    this.UpdataDataoffset=function()
    {
        var data=null;
        if (this.Frame.Data)
            data=this.Frame.Data;
        else
            data=this.Frame.SubFrame[0].Frame.Data;

        if (!data) return;

        for(var i in this.ChartPaint)
        {
            var item =this.ChartPaint[i];
            if (!item.Data) continue;
            item.Data.DataOffset=data.DataOffset;
        }

        for(var i in this.OverlayChartPaint)
        {
            var item =this.OverlayChartPaint[i];
            if (!item.Data) continue;
            item.Data.DataOffset=data.DataOffset;
        }

        //叠加指标当前显示的数据偏移
        for (var i in this.Frame.SubFrame)
        {
            var subFrame=this.Frame.SubFrame[i];
            for(var j in subFrame.OverlayIndex)
            {
                var overlayItem=subFrame.OverlayIndex[j];
                for(var k in overlayItem.ChartPaint)
                {
                    var item=overlayItem.ChartPaint[k];
                    if (!item.Data) continue;
                    item.Data.DataOffset=data.DataOffset;
                }
            }
        }

    }

    this.DataMove=function(step,isLeft)
    {
        step=parseInt(step/this.StepPixel);  //除以4个像素
        if (step<=0) return false;

        var data=null;
        if (!this.Frame.Data) data=this.Frame.Data;
        else data=this.Frame.SubFrame[0].Frame.Data;
        if (!data) return false;

        var xPointcount=0;
        if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount;
        else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount;
        if (!xPointcount) return false;

        if (isLeft) //-->
        {
            if (this.RightSpaceCount>0)
            {
                if (xPointcount+data.DataOffset>=data.Data.length+this.RightSpaceCount-1) return false;
                data.DataOffset+=step;

                if (data.DataOffset+xPointcount>=data.Data.length+this.RightSpaceCount)
                    data.DataOffset=data.Data.length-(xPointcount-this.RightSpaceCount);
            }
            else
            {
                if (xPointcount+data.DataOffset>=data.Data.length) return false;
                data.DataOffset+=step;

                if (data.DataOffset+xPointcount>=data.Data.length)
                    data.DataOffset=data.Data.length-xPointcount;
            }
            return true;
        }
        else        //<--
        {
            if (data.DataOffset<=0) return false;

            data.DataOffset-=step;
            if (data.DataOffset<0) data.DataOffset=0;

            return true;
        }
    }

    //获取鼠标在当前子窗口id
    this.GetSubFrameIndex=function(x,y)
    {
        if (!this.Frame.SubFrame || this.Frame.SubFrame.length<=0) return -1;

        for(var i in this.Frame.SubFrame)
        {
            var frame=this.Frame.SubFrame[i].Frame;
            var left=frame.ChartBorder.GetLeft();
            var top=frame.ChartBorder.GetTop();
            var height=frame.ChartBorder.GetHeight();
            var width=frame.ChartBorder.GetWidth();

            this.Canvas.beginPath();
            this.Canvas.rect(left,top,width,height);
            if (this.Canvas.isPointInPath(x,y)) return parseInt(i);

        }
        return 0;
    }

    //根据X坐标获取数据索引
    this.GetDataIndexByPoint=function(x)
    {
        var frame=this.Frame;
        if (this.Frame.SubFrame && this.Frame.SubFrame.length>0) frame=this.Frame.SubFrame[0].Frame;

        var data=null;
        if (this.Frame.Data)
            data=this.Frame.Data;
        else
            data=this.Frame.SubFrame[0].Frame.Data;

        if (!data || !frame) return;

        var index=parseInt(frame.GetXData(x));

        //JSConsole.Chart.Log('x='+ x +' date='+data.Data[data.DataOffset+index].Date);
        return data.DataOffset+index;
    }

    //获取主数据
    this.GetSelectRectData=function(selectData)
    {
        if (Math.abs(selectData.XStart-selectData.XEnd)<5) return false;

        var data=null;
        if (this.Frame.Data)
            data=this.Frame.Data;
        else
            data=this.Frame.SubFrame[0].Frame.Data;

        if (!data) return false;

        var start=this.GetDataIndexByPoint(selectData.XStart);
        var end=this.GetDataIndexByPoint(selectData.XEnd);

        if (Math.abs(start-end)<2) return false;

        selectData.Data=data;
        if (start>end)
        {
            selectData.Start=end;
            selectData.End=start;
        }
        else
        {
            selectData.Start=start;
            selectData.End=end;
        }

        return true;
    }

    //获取当前的点对应的 画图工具的图形
    //data.X data.Y 鼠标位置  返回 data.ChartDrawPicture 数据在画图工具 data.PointIndex 在画图工具对应点索引
    this.GetChartDrawPictureByPoint=function(data)
    {
        for(var i in this.ChartDrawPicture)
        {
            var item =this.ChartDrawPicture[i];
            var pointIndex=item.IsPointIn(data.X,data.Y);
            if (pointIndex===false) continue;

            if (pointIndex>=0)
            {
                data.ChartDrawPicture=item;
                data.PointIndex=pointIndex;
                return true;
            }
        }

        return false;
    }

    // 保存图片
    this.SaveToImage = function (format,colorGB) 
    {
        if (this.UIElement.width<=0 || this.UIElement.height<=0) return null;
        if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) return null; // 数据加载中不能保存

        JSConsole.Chart.Log('[JSChartContainer::SaveToImage]', this.UIElement);
        var clrBG='rgb(255,255,255)';
        if (colorGB) clrBG=colorGB;
        this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height);
        this.Canvas.fillStyle=clrBG;
        this.Canvas.fillRect(0,0,this.UIElement.width,this.UIElement.height);   //画一个背景色, 不然是一个黑的背景
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        this.Canvas.lineWidth=pixelTatio;       //手机端需要根据分辨率比调整线段宽度

        this.Frame.Draw();  //框架

        for (var i in this.ChartPaint)  //框架内图形
        {
            var item=this.ChartPaint[i];
            if (item.IsDrawFirst)
                item.Draw();
        }

        for(var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];
            if (!item.IsDrawFirst)
                item.Draw();
        }

        for(var i in this.ChartPaintEx)
        {
            var item=this.ChartPaintEx[i];
            item.Draw();
        }

        for(var i in this.OverlayChartPaint)    //叠加股票
        {
            var item=this.OverlayChartPaint[i];
            item.Draw();
        }

        
        for(var i in this.ExtendChartPaint)     //固定扩展图形
        {
            var item=this.ExtendChartPaint[i];
            if (!item.IsDynamic && item.IsAnimation==false) item.Draw();
        }

        if (this.Frame.DrawInsideHorizontal) this.Frame.DrawInsideHorizontal();
        this.Frame.DrawLock();

        for(var i in this.ExtendChartPaint) //动态扩展图形
        {
            var item=this.ExtendChartPaint[i];
            if (item.IsDynamic && item.DrawAfterTitle===false) item.Draw();
        }

        if (this.LastPoint.X!=null || this.LastPoint.Y!=null)
        {
            if (this.ChartCorssCursor)  //十字光标不画 
            {
                this.ChartCorssCursor.LastPoint=this.LastPoint;
                this.ChartCorssCursor.CursorIndex=this.CursorIndex;
            }
        }

        for(var i in this.TitlePaint)
        {
            var item=this.TitlePaint[i];
            if (!item.IsDynamic) continue;

            item.CursorIndex=this.CursorIndex;
            item.Draw();
        }

        for(var i in this.ExtendChartPaint) //动态扩展图形
        {
            var item=this.ExtendChartPaint[i];
            if (item.IsDynamic && item.DrawAfterTitle===true && item.IsAnimation==false) item.Draw();
        }

        if (this.EnableAnimation)
        {
            for(var i in this.ExtendChartPaint)    //动画
            {
                var item=this.ExtendChartPaint[i];
                if (item.IsAnimation===true) item.Draw();
            }
        }

        for(var i in this.ChartDrawPicture)
        {
            var item=this.ChartDrawPicture[i];
            item.Draw();
        }

        if (this.CurrentChartDrawPicture && this.CurrentChartDrawPicture.Status!=10)
        {
            this.CurrentChartDrawPicture.Draw();
        }

        var dataURL=this.UIElement.toDataURL(format ? format:'image/png', 1.0);
        JSConsole.Chart.Log('[JSChartContainer::SaveToImage] data= ', dataURL);
        return dataURL;
    }


    this.SaveToImageUrl=function(obj,callback) //obj:{Format: 图片格式, ColorGB: 背景色}, callback:function(bSuccess,obj)
    {
        if (!obj) obj={Format:'image/png', ColorGB:'rgb(255,255,255)'};
        var imageData=this.SaveToImage(obj.Format, obj.ColorGB);
        var postData={"Base64":imageData, "BucketName":"downloadcache", "Path":"hqchart/hq_snapshot"};
        var url=g_JSChartResource.Domain+'/API/FileUploadForBase64';

        JSNetwork.HttpRequest({
            url: url,
            method: "POST",
            dataType: "json",
            data: postData,
            success: function (data) 
            {
                JSConsole.Chart.Log('[JSChartContainer::SaveToImageUrl] recv data', data);
                var result={Path:data.relativeurl, Domain:'https://opensourcedownload.zealink.com'};
                result.Url=`${result.Domain}/${result.Path}`;
                if (callback) callback(true,result,'');
            },
            error: function (request) 
            {
                JSConsole.Chart.Log('[JSChartContainer::SaveToImageUrl] error ', request);
                if (callback) callback(false,null,'upload failed');
            }
        });

    }

    this.SetLanguage=function(language)
    {
        var languageID=null;
        switch(language)
        {
            case 'EN':
                languageID=JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID;
                break;
            case 'CN':
                languageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
                break;
            default:
                console.warn(`[JSChartContainer::SetLanguage] language=${language} error`);
                return;
        }

        if (this.LanguageID==languageID) return;
        
        this.LanguageID=languageID;
        if (this.ChartCorssCursor && this.ChartCorssCursor.StringFormatY) this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID;

        for(var i in this.TitlePaint)
        {
            var item=this.TitlePaint[i];
            if (item) item.LanguageID=this.LanguageID;
        }

        if (this.Frame && this.Frame.SetLanguage) this.Frame.SetLanguage(this.LanguageID);

        this.Frame.SetSizeChage(true);
        this.Draw();
    }
}

function GetDevicePixelRatio()
{
    if (typeof(window) =='undefined') return 1;
    return window.devicePixelRatio || 1;
}

function IsPhoneWeb()
{
    var userAgentInfo=navigator.userAgent;   
    const Agents =new Array("Android","iPhone","SymbianOS","Windows Phone","iPad","iPod");   
    for(var v=0;v<Agents.length;v++) 
    {      
        if(userAgentInfo.indexOf(Agents[v])>0) return true;
    }    
    return false; 
}

function OnKeyDown(e)   //键盘事件
{
    if(this.JSChartContainer && this.JSChartContainer.OnKeyDown)
        this.JSChartContainer.OnKeyDown(e);
}

function OnWheel(e)    //上下滚动事件
{
    if(this.JSChartContainer && this.JSChartContainer.OnWheel)
        this.JSChartContainer.OnWheel(e);
}

function ToFixed(number, precision)
{
    var b = 1;
    if (isNaN(number)) return number;
    if (number < 0) b = -1;
    var multiplier = Math.pow(10, precision);
    var value=Math.round(Math.abs(number) * multiplier) / multiplier * b;

    if (/^(\d+(?:\.\d+)?)(e)([\-]?\d+)$/.test(value))
        var s=value.toFixed2(precision);
    else 
        var s = value.toString();
    
    var rs = s.indexOf('.');
    if (rs < 0 && precision>0)
    {
        rs = s.length;
        s += '.';
    }

    while (s.length <= rs + precision)
    {
        s += '0';
    }

    

    return s;
}

Number.prototype.toFixed2=Number.prototype.toFixed; //备份下老的
Number.prototype.toFixed = function( precision )
{
    return ToFixed(this,precision);
}

function Guid()
{
    function S4()
    {
       return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    }
    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}

function GetScrollPosition()
{
    var scrollPos={};
    var scrollTop=0;
    var scrollLeft=0;
    if(document.documentElement && document.documentElement.scrollTop)
    {
        scrollTop=document.documentElement.scrollTop;
        scrollLeft=document.documentElement.scrollLeft;
    }else if(document.body)
    {
        scrollTop=document.body.scrollTop;
        scrollLeft=document.body.scrollLeft;
    }

    scrollPos.Top=scrollTop;
    scrollPos.Left=scrollLeft;
    return scrollPos;
}

//修正线段有毛刺
function ToFixedPoint(value)
{
    return parseInt(value)+0.5;
}

function ToFixedRect(value)
{
    // With a bitwise or.
    //rounded = (0.5 + somenum) | 0;
    // A double bitwise not.
    //rounded = ~~ (0.5 + somenum);
    // Finally, a left bitwise shift.
    var rounded;
    return rounded = (0.5 + value) << 0;
}



function Point()
{
    this.X;
    this.Y;
}

function SelectRectData()
{
    this.Data;                  //主数据
    this.JSChartContainer;      //行情控件

    this.Start; //数据起始位子
    this.End;   //数据结束位置

    this.XStart;//X坐标起始位置
    this.XEnd;  //X位置结束为止
}

//坐标信息
function CoordinateInfo()
{
    this.Value;                                                 //坐标数据
    this.Message=new Array();                                   //坐标输出文字信息
    this.TextColor=g_JSChartResource.FrameSplitTextColor        //文字颜色
    this.Font=g_JSChartResource.FrameSplitTextFont;             //字体
    this.LineColor=g_JSChartResource.FrameSplitPen;             //线段颜色
    this.LineType=1;                                            //线段类型 -1 不画线段 2 虚线
}


//边框信息
function ChartBorder()
{
    this.UIElement;

    //四周间距
    this.Left=50;
    this.Right=80;
    this.Top=50;
    this.Bottom=50;
    this.TitleHeight=24;    //标题高度
    this.TopSpace=0;
    this.BottomSpace=0;

    this.GetChartWidth=function()
    {
        return this.UIElement.width;
    }

    this.GetChartHeight=function()
    {
        return this.UIElement.height;
    }

    this.GetLeft=function()
    {
        return this.Left;
    }

    this.GetRight=function()
    {
        return this.UIElement.width-this.Right;
    }

    this.GetTop=function()
    {
        return this.Top;
    }

    this.GetTopEx=function()    //去掉标题,上面间距
    {
        return this.Top+this.TitleHeight+this.TopSpace;
    }

    this.GetTopTitle=function() //去掉标题
    {
        return this.Top+this.TitleHeight;
    }

    this.GetBottom=function()
    {
        return this.UIElement.height-this.Bottom;
    }

    this.GetBottomEx=function()
    {
        return this.UIElement.height-this.Bottom-this.BottomSpace;
    }

    this.GetWidth=function()
    {
        return this.UIElement.width-this.Left-this.Right;
    }

    this.GetHeight=function()
    {
        return this.UIElement.height-this.Top-this.Bottom;
    }

    this.GetHeightEx=function() //去掉标题的高度, 上下间距
    {
        return this.UIElement.height-this.Top-this.Bottom-this.TitleHeight-this.TopSpace-this.BottomSpace;
    }

    this.GetRightEx=function()  //横屏去掉标题高度的 上面间距
    {
        return this.UIElement.width-this.Right-this.TitleHeight- this.TopSpace;
    }

    this.GetWidthEx=function()  //横屏去掉标题宽度 上下间距
    {
        return this.UIElement.width-this.Left-this.Right-this.TitleHeight- this.TopSpace - this.BottomSpace;
    }

    this.GetLeftEx = function () //横屏
    {
        return this.Left+this.BottomSpace;
    }

    this.GetRightTitle = function ()//横屏
    {
        return this.UIElement.width - this.Right - this.TitleHeight;
    }

    this.GetTitleHeight=function()
    {
        return this.TitleHeight;
    }
}

function IChartFramePainting()
{
    this.HorizontalInfo=new Array();    //Y轴
    this.VerticalInfo=new Array();      //X轴

    this.Canvas;                        //画布

    this.Identify;                      //窗口标识

    this.ChartBorder;
    this.PenBorder=g_JSChartResource.FrameBorderPen;        //边框颜色
    this.TitleBGColor=g_JSChartResource.FrameTitleBGColor;  //标题背景色
    this.IsShow=true;                   //是否显示
    this.SizeChange=true;               //大小是否改变
    this.XYSplit=true;                  //XY轴坐标信息改变

    this.HorizontalMax;                 //Y轴最大值
    this.HorizontalMin;                 //Y轴最小值
    this.XPointCount=10;                //X轴数据个数

    this.YSplitOperator;               //Y轴分割
    this.XSplitOperator;               //X轴分割
    this.Data;                         //主数据

    this.IsLocked=false;               //是否上锁
    this.LockPaint = null;

    this.YSpecificMaxMin=null;         //指定Y轴最大最小值
    this.IsShowBorder = true;            //是否显示边框

    this.Draw=function()
    {
        this.DrawFrame();
        this.DrawBorder();

        this.SizeChange=false;
        this.XYSplit=false;
    }

    this.DrawFrame=function() { }

    //画边框
    this.DrawBorder=function()
    {
        if (!this.IsShowBorder) return;

        var left=ToFixedPoint(this.ChartBorder.GetLeft());
        var top=ToFixedPoint(this.ChartBorder.GetTop());
        var right=ToFixedPoint(this.ChartBorder.GetRight());
        var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
        var width=right-left;
        var height=bottom-top;

        //JSConsole.Chart.Log('[IChartFramePainting.DrawBorder] bottom',bottom);

        this.Canvas.strokeStyle=this.PenBorder;
        this.Canvas.strokeRect(left,top,width,height);
    }

    //画标题背景色
    this.DrawTitleBG=function()
    {
        if (this.ChartBorder.TitleHeight<=0) return;

        var left=ToFixedPoint(this.ChartBorder.GetLeft());
        var top=ToFixedPoint(this.ChartBorder.GetTop());
        var right=ToFixedPoint(this.ChartBorder.GetRight());
        var bottom=ToFixedPoint(this.ChartBorder.GetTopTitle());
        var width=right-left;
        var height=bottom-top;

        this.Canvas.fillStyle=this.TitleBGColor;
        this.Canvas.fillRect(left,top,width,height);
    }

    this.DrawLock=function()
    {
        if (this.IsLocked)
        {
            if (this.LockPaint == null)
                this.LockPaint = new ChartLock();
            this.LockPaint.Canvas=this.Canvas;
            this.LockPaint.ChartBorder=this.ChartBorder;
            this.LockPaint.ChartFrame=this;
            this.LockPaint.Draw(true);
        }
    }

    this.CalculateLock=function()
    {
        if (this.IsLocked)
        {
            if (this.LockPaint == null)
                this.LockPaint = new ChartLock();
            this.LockPaint.Canvas=this.Canvas;
            this.LockPaint.ChartBorder=this.ChartBorder;
            this.LockPaint.ChartFrame=this;
            this.LockPaint.Draw(false);
        }
    }

    //设施上锁
    this.SetLock=function(lockData)
    {
        if (!lockData)  //空数据不上锁
        {
            this.IsLocked=false;
            return;
        }

        this.IsLocked=true;
        if (!this.LockPaint) this.LockPaint=new ChartLock();    //创建锁

        if (lockData.Callback) this.LockPaint.Callback=lockData.Callback;       //回调
        if (lockData.IndexName) this.LockPaint.IndexName=lockData.IndexName;    //指标名字
        if (lockData.ID) this.LockPaint.LockID=lockData.ID;                     //锁ID
        if (lockData.BG) this.LockPaint.BGColor=lockData.BG;                    //背景色 
        if (lockData.Text) this.LockPaint.Title= lockData.Text;   
        if (lockData.TextColor) this.LockPaint.TextColor=lockData.TextColor;  
        if (lockData.Font) this.LockPaint.Font=lockData.Font;
        if (lockData.Count) this.LockPaint.LockCount=lockData.Count;
        if (lockData.MinWidth>0) this.LockPaint.MinWidth=lockData.MinWidth;
    }

    this.GetLockRect=function()
    {
        if (!this.IsLocked) return null;
        if (!this.LockPaint) return null; 
        return this.LockPaint.LockRect;
    }
}

//空框架只画边框
function NoneFrame()
{
    this.newMethod=IChartFramePainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Snapshot=function()
    {

    }

    this.SetSizeChage=function(sizeChange)
    {
        this.SizeChange=sizeChange;

        //画布的位置
        this.Position={
            X:this.ChartBorder.UIElement.offsetLeft,
            Y:this.ChartBorder.UIElement.offsetTop,
            W:this.ChartBorder.UIElement.clientWidth,
            H:this.ChartBorder.UIElement.clientHeight
        };
    }
}

function AverageWidthFrame()
{
    this.newMethod=IChartFramePainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.DataWidth=50*GetDevicePixelRatio();
    this.DistanceWidth=10*GetDevicePixelRatio();
    this.MinXDistance = 30*GetDevicePixelRatio();       //X轴刻度最小间距
    this.MinYDistance=10*GetDevicePixelRatio();         //Y轴刻度最小间距
    this.CoordinateType=0;  //坐标类型 0=普通坐标 1=反转坐标
    this.IsShowYText=[true,true];       //是否显示Y轴坐标坐标 [0=左侧] [1=右侧]
    this.XBottomOffset=g_JSChartResource.Frame.XBottomOffset;   //X轴文字显示向下偏移

    this.DrawFrame=function()
    {
        if (this.XPointCount>0)
        {
            let dInterval=this.ChartBorder.GetWidth()/(6*this.XPointCount); //分6份, 数据4 间距2
            this.DistanceWidth=2*dInterval;
			this.DataWidth=4*dInterval;
        }

        this.DrawHorizontal();
        this.DrawVertical();
    }

    //isLimit 是否限制在当前坐标下
    this.GetYFromData=function(value, isLimit)
    {
        if (isLimit===false)
        {
            if (this.CoordinateType==1)
            {
                var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin);
                return this.ChartBorder.GetTopEx()+height;
            }
            else
            {
                var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin);
                return this.ChartBorder.GetBottomEx()-height;
            }
        }
        else
        {
            if (this.CoordinateType==1)
            {
                if(value<=this.HorizontalMin) return this.ChartBorder.GetTopEx();
                if(value>=this.HorizontalMax) return this.ChartBorder.GetBottomEx();

                var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin);
                return this.ChartBorder.GetTopEx()+height;
            }
            else
            {
                if(value<=this.HorizontalMin) return this.ChartBorder.GetBottomEx();
                if(value>=this.HorizontalMax) return this.ChartBorder.GetTopEx();

                var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin);
                return this.ChartBorder.GetBottomEx()-height;
            }
        }
    }

    //画Y轴
    this.DrawHorizontal=function()
    {
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var bottom = this.ChartBorder.GetBottom();
        var top = this.ChartBorder.GetTopTitle();
        var borderRight=this.ChartBorder.Right;
        var borderLeft=this.ChartBorder.Left;

        var yPrev=null; //上一个坐标y的值
        for(var i=this.HorizontalInfo.length-1; i>=0; --i)  //从上往下画分割线
        {
            var item=this.HorizontalInfo[i];
            var y=this.GetYFromData(item.Value);
            if (y!=null && Math.abs(y-yPrev)<this.MinYDistance) continue;  //两个坐标在近了 就不画了

            this.Canvas.strokeStyle=item.LineColor;

            if (item.LineType==2)
            {
                this.Canvas.save();
                this.Canvas.setLineDash([5,5]);   //虚线
                this.Canvas.beginPath();
                this.Canvas.moveTo(left,ToFixedPoint(y));
                this.Canvas.lineTo(right,ToFixedPoint(y));
                this.Canvas.stroke();
                this.Canvas.restore();
            }
            else
            {
                this.Canvas.beginPath();
                this.Canvas.moveTo(left,ToFixedPoint(y));
                this.Canvas.lineTo(right,ToFixedPoint(y));
                this.Canvas.stroke();
            }

            if (y >= bottom - 2) this.Canvas.textBaseline = 'bottom';
            else if (y <= top + 2) this.Canvas.textBaseline = 'top';
            else this.Canvas.textBaseline = "middle";

            //坐标信息 左边 间距小于10 不画坐标
            if (item.Message[0]!=null && borderLeft>10 && this.IsShowYText[0]===true)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="right";
                this.Canvas.fillText(item.Message[0],left-2,y);
            }

            //坐标信息 右边 间距小于10 不画坐标
            if (item.Message[1]!=null && borderRight>10 && this.IsShowYText[1]===true)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="left";
                this.Canvas.fillText(item.Message[1],right+2,y);
            }

            yPrev=y;
        }
    }

    //Y刻度画在左边内部
    this.DrawInsideHorizontal = function () 
    {
        if (this.IsHScreen===true) return;  //横屏不画
        if (this.IsShowYText[0]===false && this.IsShowYText[1]===false) return;

        var left = this.ChartBorder.GetLeft();
        var right = this.ChartBorder.GetRight();
        var bottom = this.ChartBorder.GetBottom();
        var top = this.ChartBorder.GetTopTitle();
        var borderRight = this.ChartBorder.Right;
        var borderLeft = this.ChartBorder.Left;
        var titleHeight = this.ChartBorder.TitleHeight;

        if ( (borderLeft<10 && this.IsShowYText[0]===true) || (borderRight<10 && this.IsShowYText[1]===true) )
        {
            var pixelTatio = GetDevicePixelRatio();
            var yPrev = null; //上一个坐标y的值
            for (var i = this.HorizontalInfo.length - 1; i >= 0; --i)  //从上往下画分割线
            {
                var item = this.HorizontalInfo[i];
                var y = this.GetYFromData(item.Value);
                if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue;  //两个坐标在近了 就不画了

                //坐标信息 左边 间距小于10 画在内部
                if (item.Message[0] != null && borderLeft < 10 && this.IsShowYText[0]===true) 
                {
                    if (item.Font != null) this.Canvas.font = item.Font;
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.textAlign = "left";
                    if (y >= bottom - 2) this.Canvas.textBaseline = 'bottom';
                    else if (y <= top + 2) this.Canvas.textBaseline = 'top';
                    else this.Canvas.textBaseline = "middle";

                    var textObj={ X:left, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ;
                    if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj))
                        this.Canvas.fillText(item.Message[0], left + 1*pixelTatio, y);
                }

                if (item.Message[1] != null && borderRight < 10 && this.IsShowYText[1]===true)
                {
                    if (item.Font != null) this.Canvas.font = item.Font;
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.textAlign = "right";
                    if (y >= bottom - 2) this.Canvas.textBaseline = 'bottom';
                    else if (y <= top + 2) this.Canvas.textBaseline = 'top';
                    else this.Canvas.textBaseline = "middle";
                    var textWidth = this.Canvas.measureText(item.Message[1]).width;
                    var textObj={ X:right-textWidth, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ;
                    if (!this.IsOverlayMaxMin || !this.IsOverlayMaxMin(textObj))
                        this.Canvas.fillText(item.Message[1], right - 1*pixelTatio, y);
                }
                yPrev = y;
            }
        }
    }

    this.GetXFromIndex=function(index)
    {
        var count=this.XPointCount;

        if (count==1)
        {
            if (index==0) return this.ChartBorder.GetLeft();
            else return this.ChartBorder.GetRight();
        }
        else if (count<=0)
        {
            return this.ChartBorder.GetLeft();
        }
        else if (index>=count)
        {
            return this.ChartBorder.GetRight();
        }
        else
        {
            var offset=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*index/count;
            return offset;
        }
    }

    //画X轴
    this.DrawVertical=function()
    {
        var top=this.ChartBorder.GetTopTitle();
        var bottom=this.ChartBorder.GetBottom();
        var right=this.ChartBorder.GetRight();
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        //JSConsole.Chart.Log('[AverageWidthFrame.DrawVertical] bottom',bottom);

        var xPrev=null; //上一个坐标x的值
        var textRightPrev=null; //上一次刻度输出右边x坐标
        for(var i in this.VerticalInfo)
        {
            var x=this.GetXFromIndex(this.VerticalInfo[i].Value);
            if (x>right) break;
            if (xPrev!=null && Math.abs(x-xPrev)<this.MinXDistance) continue;
            
            if (this.VerticalInfo[i].LineType>0)
            {
                this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor;
                this.Canvas.beginPath();
                this.Canvas.moveTo(ToFixedPoint(x),top);
                this.Canvas.lineTo(ToFixedPoint(x),bottom);
                this.Canvas.stroke();
            }

            if (this.VerticalInfo[i].Message[0]!=null)
            {
                if (this.VerticalInfo[i].Font!=null)
                    this.Canvas.font=this.VerticalInfo[i].Font;
                
                var textLeft=0;
                this.Canvas.fillStyle=this.VerticalInfo[i].TextColor;
                var testWidth=this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width;
                if (x<testWidth/2)
                {
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="top";
                    textLeft=x;
                }
                else if ((x + testWidth / 2) >= this.ChartBorder.GetChartWidth())
                {
                    this.Canvas.textAlign = "right";
                    this.Canvas.textBaseline="top";
                    textLeft=x-testWidth;
                }
                else
                {
                    this.Canvas.textAlign="center";
                    this.Canvas.textBaseline="top";
                    textLeft=x-(testWidth/2);
                }
                
                if (textRightPrev==null || textLeft>textRightPrev)
                {
                    this.Canvas.fillText(this.VerticalInfo[i].Message[0],x,bottom+this.XBottomOffset);
                    textRightPrev=textLeft+testWidth;
                }
            }

            xPrev=x;
        }
    }

    //Y坐标转y轴数值
    this.GetYData=function(y)
    {
        if (this.CoordinateType==1) //反转坐标
        {
            if (y<this.ChartBorder.GetTopEx()) return this.HorizontalMin;
            if (y>this.ChartBorder.GetBottomEx()) return this.HorizontalMax;

            return (y-this.ChartBorder.GetTopEx())/this.ChartBorder.GetHeightEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin;
        }
        else
        {
            if (y<this.ChartBorder.GetTopEx()) return this.HorizontalMax;
            if (y>this.ChartBorder.GetBottomEx()) return this.HorizontalMin;

            return (this.ChartBorder.GetBottomEx()-y)/this.ChartBorder.GetHeightEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin;
        }
    }

    //X坐标转x轴数值
    this.GetXData=function(x)
    {
        if (x<=this.ChartBorder.GetLeft()) return 0;
		if (x>=this.ChartBorder.GetRight()) return this.XPointCount;

		return (x-this.ChartBorder.GetLeft())*(this.XPointCount*1.0/this.ChartBorder.GetWidth());
    }

    this.DrawCustomItem=function(item) //显示自定义刻度
    {
        //if (this.IsHScreen===true) return;  //横屏不画
        if (!item.Message[1] && !item.Message[0]) return;
        if (item.Value>this.HorizontalMax || item.Value<this.HorizontalMin) return;

        var left = this.ChartBorder.GetLeft();
        var right = this.ChartBorder.GetRight();
        var bottom = this.ChartBorder.GetBottom();
        var top = this.ChartBorder.GetTopTitle();
        var borderRight = this.ChartBorder.Right;
        var borderLeft = this.ChartBorder.Left;
        var titleHeight = this.ChartBorder.TitleHeight;

        if (this.IsHScreen)
        {
            borderLeft=this.ChartBorder.Top;
            borderRight=this.ChartBorder.Bottom;
            top=this.ChartBorder.GetTop();
            bottom=this.ChartBorder.GetBottom();
        }

        var pixelTatio = GetDevicePixelRatio();
        var textHeight=18*pixelTatio;
        var y = this.GetYFromData(item.Value);

        if (item.Message[0])    // 左
        {
            if (borderLeft<10)
            {
                if (item.Font != null) this.Canvas.font = item.Font;
                this.Canvas.textAlign = "left";
                this.Canvas.textBaseline = "middle";
                var textWidth = this.Canvas.measureText(item.Message[0]).width+2*pixelTatio;
                var bgColor=item.LineColor;
                var rgb=this.RGBToStruct(item.LineColor);
                if (rgb) bgColor=`rgba(${rgb.R}, ${rgb.G}, ${rgb.B}, ${g_JSChartResource.FrameLatestPrice.BGAlpha})`;   //内部刻度 背景增加透明度
                this.Canvas.fillStyle=bgColor;

                if (this.IsHScreen)
                {
                    var bgTop=top;
                    var textLeft=y-textHeight/2-1*pixelTatio;
                    this.Canvas.fillRect(textLeft,bgTop,textHeight,textWidth);
                    this.DrawHScreenText({X:y, Y:bgTop}, {Text:item.Message[0], Color:item.TextColor, XOffset:1*pixelTatio, YOffset:2*pixelTatio});
                    if (item.LineType!=-1) this.DrawDotLine(bgTop+textWidth,bottom,y,item.LineColor);
                }
                else
                {
                    var bgTop=y-textHeight/2-1*pixelTatio;
                    var textLeft=left + 1*pixelTatio
                    this.Canvas.fillRect(textLeft,bgTop,textWidth,textHeight);
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.fillText(item.Message[0], textLeft + 1*pixelTatio, y);
                    if (item.LineType!=-1) this.DrawDotLine(textLeft+textWidth,right,y,item.LineColor);
                }
            }
            else
            {
                if (item.Font != null) this.Canvas.font = item.Font;
                this.Canvas.textAlign = "right";
                this.Canvas.textBaseline = "middle";
                var textWidth = this.Canvas.measureText(item.Message[0]).width+2*pixelTatio;
                this.Canvas.fillStyle=item.LineColor;
                if (this.IsHScreen)
                {
                    var bgTop=top-textWidth;
                    var textLeft=y-textHeight/2-1*pixelTatio;
                    this.Canvas.fillRect(textLeft,bgTop,textHeight,textWidth);
                    this.DrawHScreenText({X:y, Y:bgTop}, {Text:item.Message[0], Color:item.TextColor, XOffset:1*pixelTatio, YOffset:2*pixelTatio});
                    if (item.LineType!=-1) this.DrawDotLine(bgTop+textWidth,bottom,y,item.LineColor);
                }
                else
                {
                    var bgTop=y-textHeight/2-1*pixelTatio;
                    this.Canvas.fillRect(left-textWidth,bgTop,textWidth,textHeight);
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.fillText(item.Message[0], left - 1*pixelTatio, y);
                    if (item.LineType!=-1) this.DrawDotLine(left,right,y,item.LineColor);
                }
            }
        }
        else if (item.Message[1])   //右
        {
            if (borderRight<10)
            {
                if (item.Font != null) this.Canvas.font = item.Font;
                this.Canvas.textAlign = "left";
                this.Canvas.textBaseline = "middle";
                var textWidth = this.Canvas.measureText(item.Message[1]).width+2*pixelTatio;
                var bgColor=item.LineColor;
                var rgb=this.RGBToStruct(item.LineColor);
                if (rgb) bgColor=`rgba(${rgb.R}, ${rgb.G}, ${rgb.B}, ${g_JSChartResource.FrameLatestPrice.BGAlpha})`;   //内部刻度 背景增加透明度
                this.Canvas.fillStyle=bgColor;
                if (this.IsHScreen)
                {
                    var bgTop=bottom-textWidth;
                    var textLeft=y-textHeight/2-1*pixelTatio;
                    this.Canvas.fillRect(textLeft,bgTop,textHeight,textWidth);
                    this.DrawHScreenText({X:y, Y:bgTop}, {Text:item.Message[1], Color:item.TextColor, XOffset:1*pixelTatio, YOffset:2*pixelTatio});
                    if (item.LineType!=-1) this.DrawDotLine(top,bgTop,y,item.LineColor);
                }
                else
                {
                    var bgTop=y-textHeight/2-1*pixelTatio;
                    var textLeft=right-textWidth;
                    this.Canvas.fillRect(textLeft,bgTop,textWidth,textHeight);  //文本背景区域
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.fillText(item.Message[1], textLeft + 1*pixelTatio, y);
                    if (item.LineType!=-1) this.DrawDotLine(left,textLeft,y,item.LineColor);    //虚线
                }
            }
            else
            {
                if (item.Font != null) this.Canvas.font = item.Font;
                this.Canvas.textAlign = "left";
                this.Canvas.textBaseline = "middle";
                var textWidth = this.Canvas.measureText(item.Message[1]).width+2*pixelTatio;
                this.Canvas.fillStyle=item.LineColor;
                if (this.IsHScreen)
                {
                    var bgTop=bottom;
                    var textLeft=y-textHeight/2-1*pixelTatio;
                    this.Canvas.fillRect(textLeft,bgTop,textHeight,textWidth);
                    this.DrawHScreenText({X:y, Y:bgTop}, {Text:item.Message[1], Color:item.TextColor, XOffset:1*pixelTatio, YOffset:2*pixelTatio});
                    if (item.LineType!=-1) this.DrawDotLine(top,bgTop,y,item.LineColor);
                }
                else
                {
                    var bgTop=y-textHeight/2-1*pixelTatio;
                    this.Canvas.fillRect(right,bgTop,textWidth,textHeight);
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.fillText(item.Message[1], right + 1*pixelTatio, y);
                    if (item.LineType!=-1) this.DrawDotLine(left,right,y,item.LineColor);
                }
            }
        }
    }

    this.DrawDotLine=function(left,right,y, color)
    {
        var pixelTatio = GetDevicePixelRatio();
        this.Canvas.save();
        this.Canvas.strokeStyle=color;
        this.Canvas.setLineDash([5*pixelTatio,5*pixelTatio]);   //虚线
        this.Canvas.beginPath();
        if (this.IsHScreen)
        {
            this.Canvas.moveTo(ToFixedPoint(y),left);
            this.Canvas.lineTo(ToFixedPoint(y),right);
        }
        else
        {
            this.Canvas.moveTo(left,ToFixedPoint(y));
            this.Canvas.lineTo(right,ToFixedPoint(y));
        }
        this.Canvas.stroke();
        this.Canvas.restore();
    }

    this.DrawHScreenText=function(center,data)
    {
        this.Canvas.textAlign = "left";
        this.Canvas.textBaseline = "middle";
        this.Canvas.fillStyle = data.Color;

        this.Canvas.save();
        this.Canvas.translate(center.X, center.Y);
        this.Canvas.rotate(90 * Math.PI / 180);
        this.Canvas.fillText(data.Text, data.XOffset, data.YOffset);
        this.Canvas.restore();
    }

    this.RGBToStruct=function(rgb)
    {
        if(/^(rgb|RGB)/.test(rgb))
        {
            var aColor = rgb.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(",");
            var result={};
            if (aColor.length!=3) return null;

            result.R = Number(aColor[0]);
            result.G = Number(aColor[1]);
            result.B = Number(aColor[2]);
            return result;
        }
       
        return null;
    }
}

function MinuteFrame()
{
    this.newMethod=AverageWidthFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.MinuteCount=243;   //每天的分钟个数
    this.IsBeforeData=false;    //是否显示集合竞价
    this.BeforeBGColor=g_JSChartResource.Minute.BeforeBGColor;  //集合竞价背景
    this.CustomHorizontalInfo=[];
    this.RightFrame=null;   //右侧多重坐标

    this.DrawFrame=function()
    {
        this.SplitXYCoordinate();

        if (this.IsBeforeData) this.DrawBeforeDataBG();
        this.DrawTitleBG();
        this.DrawHorizontal();
        this.DrawVertical();
    }

    //画集合竞价背景
    this.DrawBeforeDataBG=function()
    {
        var left=ToFixedPoint(this.ChartBorder.GetLeft());
        var top=ToFixedPoint(this.ChartBorder.GetTopEx());
        var right=this.GetXFromIndex(14);
        var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
        var width=right-left;
        var height=bottom-top;

        this.Canvas.fillStyle=this.BeforeBGColor;
        this.Canvas.fillRect(left,top,width,height);
    }

    //分割x,y轴坐标信息
    this.SplitXYCoordinate=function()
    {
        if (this.XYSplit==false) return;
        if (this.YSplitOperator!=null) this.YSplitOperator.Operator();
        if (this.XSplitOperator!=null) this.XSplitOperator.Operator();
    }

    this.GetXFromIndex=function(index)
    {
        var count=this.XPointCount-1;

        if (count==1)
        {
            if (index==0) return this.ChartBorder.GetLeft();
            else return this.ChartBorder.GetRight();
        }
        else if (count<=0)
        {
            return this.ChartBorder.GetLeft();
        }
        else if (index>=count)
        {
            return this.ChartBorder.GetRight();
        }
        else
        {
            var offset=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*index/count;
            return offset;
        }
    }

    //X坐标转x轴数值
    this.GetXData=function(x)
    {
    var count=this.XPointCount-1;
    if (count<0) count=0;

        if (x<=this.ChartBorder.GetLeft()) return 0;
        if (x>=this.ChartBorder.GetRight()) return count;

        return (x-this.ChartBorder.GetLeft())*(count*1.0/this.ChartBorder.GetWidth());
    }

    this.DrawCustomHorizontal=function()    //Y轴刻度定制显示
    {
        for(var i in this.CustomHorizontalInfo)
        {
            var item=this.CustomHorizontalInfo[i];
            switch(item.Type)
            {
                case 1:
                    this.DrawCustomItem(item);  //自定义刻度
                    break;
            }
        }
    }
}

function OverlayMinuteFrame()
{
    this.newMethod=MinuteFrame;     //派生
    this.newMethod();
    delete this.newMethod;
    this.IsShow=true;               //坐标是否显示

    this.Draw=function()
    {
        this.SplitXYCoordinate();
        if (this.IsShow)
        {

        }

        this.SizeChange=false;
        this.XYSplit=false;
    }
}

//走势图 横屏框架
function MinuteHScreenFrame()
{
    this.newMethod=MinuteFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.IsHScreen=true;        //是否是横屏

    //画标题背景色
    this.DrawTitleBG=function()
    {
        if (this.ChartBorder.TitleHeight<=0) return;

        var left=ToFixedPoint(this.ChartBorder.GetRightEx());
        var top=ToFixedPoint(this.ChartBorder.GetTop());
        var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
        var width=this.ChartBorder.TitleHeight;
        var height=bottom-top;

        this.Canvas.fillStyle=this.TitleBGColor;
        this.Canvas.fillRect(left,top,width,height);
    }

    //画集合竞价背景
    this.DrawBeforeDataBG=function()
    {
        var left=ToFixedPoint(this.ChartBorder.GetLeft());
        var right=ToFixedPoint(this.ChartBorder.GetRightEx());
        var top=ToFixedPoint(this.ChartBorder.GetTop());
        var bottom=this.GetXFromIndex(14);
        
        var width=right-left;
        var height=bottom-top;

        this.Canvas.fillStyle=this.BeforeBGColor;
        this.Canvas.fillRect(left,top,width,height);
    }

    //Y坐标转y轴数值
    this.GetYData=function(x)
    {
        if (x<this.ChartBorder.GetLeft()) return this.HorizontalMin;
		if (x>this.ChartBorder.GetRightEx()) return this.HorizontalMax;

		return (x-this.ChartBorder.GetLeft())/this.ChartBorder.GetWidthEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin;
    }

    //X坐标转x轴数值
    this.GetXData=function(y)
    {
        if (y<=this.ChartBorder.GetTop()) return 0;
		if (y>=this.ChartBorder.GetBottom()) return this.XPointCount;

		return (y-this.ChartBorder.GetTop())*(this.XPointCount*1.0/this.ChartBorder.GetHeight());
    }

    this.GetXFromIndex=function(index)
    {
        var count=this.XPointCount-1;

        if (count==1)
        {
            if (index==0) return this.ChartBorder.GetTop();
            else return this.ChartBorder.GetBottom();
        }
        else if (count<=0)
        {
            return this.ChartBorder.GetTop();
        }
        else if (index>=count)
        {
            return this.ChartBorder.GetBottom();
        }
        else
        {
            var offset=this.ChartBorder.GetTop()+this.ChartBorder.GetHeight()*index/count;
            return offset;
        }
    }

    
    this.GetYFromData=function(value)
    {
        if(value<=this.HorizontalMin) return this.ChartBorder.GetLeft();
        if(value>=this.HorizontalMax) return this.ChartBorder.GetRightEx();

        var width=this.ChartBorder.GetWidthEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin);
        return this.ChartBorder.GetLeft()+width;
    }

    //画Y轴
    this.DrawHorizontal=function()
    {
        var top=this.ChartBorder.GetTop();
        var bottom=this.ChartBorder.GetBottom();
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var borderTop=this.ChartBorder.Top;
        var borderBottom=this.ChartBorder.Bottom;

        var yPrev=null; //上一个坐标y的值
        for(var i=this.HorizontalInfo.length-1; i>=0; --i)  //从左往右画分割线
        {
            var item=this.HorizontalInfo[i];
            var y=this.GetYFromData(item.Value);
            if (y!=null && Math.abs(y-yPrev)<this.MinYDistance) continue;  //两个坐标在近了 就不画了

            this.Canvas.strokeStyle=item.LineColor;
            this.Canvas.beginPath();
            this.Canvas.moveTo(ToFixedPoint(y),top);
            this.Canvas.lineTo(ToFixedPoint(y),bottom);
            this.Canvas.stroke();

            if (y >= right - 2) 
            {
                this.Canvas.textBaseline = 'top';
                y = right;
            }
            else if (y <= left + 2) 
            {
                this.Canvas.textBaseline = 'bottom';
                y=left;
                if (y != null && Math.abs(y - yPrev) < 2*this.MinYDistance) continue;  //两个坐标在近了 就不画了
            }
            else 
            {
                this.Canvas.textBaseline = "middle";
            }

            //坐标信息 左边 间距小于10 不画坐标
            if (item.Message[0]!=null && borderTop>10)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="right";

                var xText=y,yText=top;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);
                this.Canvas.fillText(item.Message[0], -2, 0);
                this.Canvas.restore();
            }

            //坐标信息 右边 间距小于10 不画坐标
            if (item.Message[1]!=null && borderBottom>10)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="left";
               
                var xText=y,yText=bottom;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);
                this.Canvas.fillText(item.Message[1], 2, 0);
                this.Canvas.restore();
            }

            yPrev=y;
        }
    }

    //画X轴
    this.DrawVertical=function()
    {
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRightEx();
        var bottom=this.ChartBorder.GetBottom();

        var xPrev=null; //上一个坐标x的值
        for(var i in this.VerticalInfo)
        {
            var x=this.GetXFromIndex(this.VerticalInfo[i].Value);
            if (x>bottom) break;
            if (xPrev!=null && Math.abs(x-xPrev)<this.MinXDistance) continue;

            this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor;
            this.Canvas.beginPath();
            this.Canvas.moveTo(left,ToFixedPoint(x));
            this.Canvas.lineTo(right,ToFixedPoint(x));
            this.Canvas.stroke();

            if (this.VerticalInfo[i].Message[0]!=null)
            {
                if (this.VerticalInfo[i].Font!=null)
                    this.Canvas.font=this.VerticalInfo[i].Font;

                this.Canvas.fillStyle=this.VerticalInfo[i].TextColor;
                var testWidth=this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width;
                if (x<testWidth/2)
                {
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="top";
                }
                else if ((x + testWidth / 2) >= this.ChartBorder.GetChartHeight())
                {
                    this.Canvas.textAlign = "right";
                    this.Canvas.textBaseline = "top";
                }
                else
                {
                    this.Canvas.textAlign="center";
                    this.Canvas.textBaseline="top";
                }

                var xText=left,yText=x;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);
                this.Canvas.fillText(this.VerticalInfo[i].Message[0], 0, 0);
                this.Canvas.restore();
            }

            xPrev=x;
        }
    }

     //Y刻度画在左边内部
     this.DrawInsideHorizontal = function () 
     {
         if (this.IsShowYText[0]===false && this.IsShowYText[1]===false) return;
 
         var left = this.ChartBorder.GetLeft();
         var right = this.ChartBorder.GetRightEx();
         var top=this.ChartBorder.GetTop();
         var bottom=this.ChartBorder.GetBottom();
         var borderTop=this.ChartBorder.Top;
         var borderBottom=this.ChartBorder.Bottom;
         var titleHeight = this.ChartBorder.TitleHeight;
         var pixelTatio = GetDevicePixelRatio();
         
         if ( (borderTop<10*pixelTatio && this.IsShowYText[0]===true) || (borderBottom<10*pixelTatio && this.IsShowYText[1]===true) )
         {
             var pixelTatio = GetDevicePixelRatio();
             var yPrev = null; //上一个坐标y的值
             for (var i = this.HorizontalInfo.length - 1; i >= 0; --i)  //从上往下画分割线
             {
                 var item = this.HorizontalInfo[i];
                 var y = this.GetYFromData(item.Value);
                 if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue;  //两个坐标在近了 就不画了
 
                 //坐标信息 左边 间距小于10 画在内部
                 if (item.Message[0] != null && borderTop < 10*pixelTatio && this.IsShowYText[0]===true) 
                 {
                     if (item.Font != null) this.Canvas.font = item.Font;
                     this.Canvas.fillStyle = item.TextColor;
                     this.Canvas.textAlign = "left";
                     if (y >= right - 2) this.Canvas.textBaseline = 'top';
                     else if (y <= left + 2) this.Canvas.textBaseline = 'bottom';
                     else this.Canvas.textBaseline = "middle";
 
                     var textObj={ X:left, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ;
                     var xText=y,yText=top;
                     this.Canvas.save();
                     this.Canvas.translate(xText, yText);
                     this.Canvas.rotate(90 * Math.PI / 180);
                     this.Canvas.fillText(item.Message[0], -2, 0);
                     this.Canvas.restore();
                 }
 
                 if (item.Message[1] != null && borderBottom < 10*pixelTatio && this.IsShowYText[1]===true)
                 {
                     if (item.Font != null) this.Canvas.font = item.Font;
                     this.Canvas.fillStyle = item.TextColor;
                     this.Canvas.textAlign = "right";
                     if (y >= right - 2) this.Canvas.textBaseline = 'top';
                     else if (y <= left + 2) this.Canvas.textBaseline = 'bottom';
                     else this.Canvas.textBaseline = "middle";
                     var textWidth = this.Canvas.measureText(item.Message[1]).width;
                     var textObj={ X:right-textWidth, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ;
 
                     var xText=y,yText=bottom;
                     this.Canvas.save();
                     this.Canvas.translate(xText, yText);
                     this.Canvas.rotate(90 * Math.PI / 180);
                     this.Canvas.fillText(item.Message[1], 2, 0);
                     this.Canvas.restore();
                 }
                 yPrev = y;
             }
         }
     }
 
}

//K线框架
function KLineFrame()
{
    this.newMethod=AverageWidthFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ToolbarID=Guid();  //工具条Div id

    this.ModifyIndex=true;      //是否显示'改参数'菜单
    this.ChangeIndex=true;      //是否显示'换指标'菜单
    this.CloseIndex=true;       //是否显示'关闭指标窗口'菜单
    this.OverlayIndex=false;    //是否显示叠加指标

    this.ModifyIndexEvent;  //改参数 点击事件
    this.ChangeIndexEvent;   //换指标 点击事件

    this.LastCalculateStatus={ Width:0, XPointCount:0 };    //最后一次计算宽度的状态

    this.CustomHorizontalInfo=[];   //定制Y轴刻度
    this.IsDrawTitleBG=false;

    this.CustomVerticalInfo=[];     //定制X轴刻度 Type:0,  Date:, Time: ,        Line:{ Color:线段颜色, Type:线段类型 0 直线 1 虚线 }
                                    //           Type:1,  Space: 第几个空白间距,  Line:{ Color:线段颜色, Type:线段类型 0 直线 1 虚线 }
    this.DrawCustomVerticalEvent;

    this.DrawToolbar=function()
    {
        if (!this.ChartBorder.UIElement) return;

        var divToolbar=document.getElementById(this.ToolbarID);
        if (!divToolbar)
        {
            divToolbar=document.createElement("div");
            divToolbar.className='klineframe-toolbar';
            divToolbar.id=this.ToolbarID;
            //为divToolbar添加属性identify
            divToolbar.setAttribute("identify",this.Identify.toString());
            this.ChartBorder.UIElement.parentNode.appendChild(divToolbar);
        }

        if (!this.ModifyIndex && !this.ChangeIndex && !this.OverlayIndex)
        {
            divToolbar.style.display='none';
            return;
        }

        //使用外城div尺寸 画图尺寸是被放大的
        var pixelTatio = GetDevicePixelRatio();
        var chartWidth=parseInt(this.ChartBorder.UIElement.parentElement.style.width.replace("px",""));  
        var chartHeight=parseInt(this.ChartBorder.UIElement.parentElement.style.height.replace("px",""));
        //JSConsole.Chart.Log('[KLineFrame::DrawToolbar] ',chartWidth,chartHeight,pixelTatio);

        var toolbarWidth=100;
        var toolbarHeight=this.ChartBorder.GetTitleHeight();
        var left=chartWidth-(this.ChartBorder.Right/pixelTatio)-toolbarWidth;
        var top=this.ChartBorder.GetTop()/pixelTatio;

        const modifyButton=`<span class='index_param icon iconfont icon-index_param' id='modifyindex' style='cursor:pointer;margin-left:2px;margin-right:2px;' title='调整指标参数'></span>`;
        const changeButton=`<span class='index_change icon iconfont icon-change_index' id='changeindex' style='cursor:pointer;margin-left:2px;margin-right:2px;' title='切换指标'></span>`;
        const overlayButton=`<span class='index_overlay icon iconfont icon-overlay_index' id='overlayindex' style='cursor:pointer;margin-left:2px;margin-right:2px;' title='叠加指标'></span>`;
        const closeButton=`<span class='index_close icon iconfont icon-close' id='closeindex' style='cursor:pointer;margin-left:2px;margin-right:2px;' title='关闭指标窗口'></span>`;

        var spanIcon=modifyButton+changeButton+overlayButton;

        if (this.Identify!==0 && this.CloseIndex)  //第1个窗口不能关闭
        {
            spanIcon+=closeButton;
        }

        //var scrollPos=GetScrollPosition();
        //left = left+scrollPos.Left;
        //top = top+scrollPos.Top;
        divToolbar.style.left = left + "px";
        divToolbar.style.top = top + "px";
        divToolbar.style.width=toolbarWidth+"px";                   //宽度先不调整吧
        divToolbar.style.height=(toolbarHeight/pixelTatio)+'px';    //只调整高度
        divToolbar.innerHTML=spanIcon;

        var chart=this.ChartBorder.UIElement.JSChartContainer;
        var identify=this.Identify;
        if (!this.ModifyIndex)  //隐藏'改参数'
            $("#"+divToolbar.id+" .index_param").hide();
        else if (typeof(this.ModifyIndexEvent)=='function')  //绑定点击事件
            $("#"+divToolbar.id+" .index_param").click(
                {
                    Chart:this.ChartBorder.UIElement.JSChartContainer,
                    Identify:this.Identify
                },this.ModifyIndexEvent);

        if (!this.ChangeIndex)  //隐藏'换指标'
        {
            $("#"+divToolbar.id+" .index_change").hide();
        }  
        else if (typeof(this.ChangeIndexEvent)=='function')
        {
            $("#"+divToolbar.id+" .index_change").click(
                {
                    Chart:this.ChartBorder.UIElement.JSChartContainer,
                    Identify:this.Identify,
                    IsOverlay:false
                },this.ChangeIndexEvent);
        }

        if (!this.OverlayIndex)
        {
            $("#"+divToolbar.id+" .index_overlay").hide();
        }
        else
        {
            $("#"+divToolbar.id+" .index_overlay").click(
                {
                    Chart:this.ChartBorder.UIElement.JSChartContainer,
                    Identify:this.Identify,
                    IsOverlay:true
                },this.ChangeIndexEvent);
        }
        
        $("#"+divToolbar.id+" .index_close").click(
            {
                Chart:this.ChartBorder.UIElement.JSChartContainer,
                Identify:this.Identify
            },
            function(event)
            {
                var hqChart=event.data.Chart;
                var id=event.data.Identify;
                hqChart.RemoveIndexWindow(id);
            });

        divToolbar.style.display = "block";
    }

    this.ClearToolbar=function()
    {
        var divToolbar=document.getElementById(this.ToolbarID);
        if (!divToolbar) return;
        this.ChartBorder.UIElement.parentNode.removeChild(divToolbar);
    }

    this.DrawFrame=function()
    {
        this.SplitXYCoordinate();

        if (this.SizeChange==true) this.CalculateDataWidth();

        this.DrawTitleBG();
        this.DrawHorizontal();
        this.DrawVertical();

        if (this.SizeChange==true) this.DrawToolbar();  //大小变动才画工具条
    }

    //isLimit 是否限制在当前屏坐标下
    this.GetXFromIndex=function(index,isLimit)
    {
        if (isLimit===false)
        {
            if (index>=0)
            {
                var offset=this.ChartBorder.GetLeft()+2+this.DistanceWidth/2+this.DataWidth/2;
                for(var i=1;i<=index;++i)
                {
                    offset+=this.DistanceWidth+this.DataWidth;
                }
            }
            else
            {
                var offset=this.ChartBorder.GetLeft()+2+this.DistanceWidth/2+this.DataWidth/2;
                var absIndex=Math.abs(index);
                for(var i=0;i<absIndex;++i)
                {
                    offset-=(this.DistanceWidth+this.DataWidth);
                }
            }
        }
        else
        {
            if (index < 0) index = 0;
            if (index > this.xPointCount - 1) index = this.xPointCount - 1;

            var offset=this.ChartBorder.GetLeft()+2+this.DistanceWidth/2+this.DataWidth/2;
            for(var i=1;i<=index;++i)
            {
                offset+=this.DistanceWidth+this.DataWidth;
            }
        }

        return offset;
    }

    //X坐标转x轴数值
    this.GetXData=function(x)
    {
        if (x<=this.ChartBorder.GetLeft()) return 0;
        if (x>=this.ChartBorder.GetRight()) return this.XPointCount;
        
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var distanceWidth=this.DistanceWidth;
        var dataWidth=this.DataWidth;

        var index=0;
        var xPoint=left+dataWidth+distanceWidth;
        while(xPoint<right && index<10000)  //自己算x的数值
        {
            if (xPoint>x) break;
            xPoint+=(dataWidth+distanceWidth);
            ++index;
        }

        //var test=(x-this.ChartBorder.GetLeft())*(this.XPointCount*1.0/this.ChartBorder.GetWidth());
        return index;
    }

    //计算数据宽度
    this.CalculateDataWidth=function()
    {
        if (this.XPointCount<2) return;

        //JSConsole.Chart.Log(`[KLineFrame::CalculateDataWidth] ZoomIndex=${this.ZoomIndex}, XPointCount=${this.XPointCount}, DataWidth=${this.DataWidth}, DistanceWidth=${this.DistanceWidth}`);
        var width=this.ChartBorder.GetWidth()-4;

        if (this.ZoomIndex>0 && this.LastCalculateStatus.Width==width && this.LastCalculateStatus.XPointCount==this.XPointCount) //宽度没变 尝试使用原来的柱子宽度
        {
            if ((this.DataWidth + this.DistanceWidth) * this.XPointCount <= width) //当前的柱子宽度够用 就不调整了
                return;
        }

        this.LastCalculateStatus.Width=width;
        this.LastCalculateStatus.XPointCount=this.XPointCount;
        for(var i=0;i<ZOOM_SEED.length;++i)
        {
            if((ZOOM_SEED[i][0] + ZOOM_SEED[i][1]) * this.XPointCount < width)
            {
                this.ZoomIndex=i;
                this.DataWidth = ZOOM_SEED[i][0];
                this.DistanceWidth = ZOOM_SEED[i][1];
                this.TrimKLineDataWidth(width);
                JSConsole.Chart.Log('[KLineFrame::CalculateDataWidth] ZOOM_SEED, DataWidth, DistanceWidth, XPointCount', ZOOM_SEED[this.ZoomIndex], this.DataWidth,this.DistanceWidth,this.XPointCount);
                return;
            }
        }

        //太多了 就平均分了
        this.ZoomIndex=ZOOM_SEED.length-1;
        this.DataWidth=width/this.XPointCount;
        this.DistanceWidth=0;
    }

    this.TrimKLineDataWidth=function(width)
    {
        var dataWidth=ZOOM_SEED[this.ZoomIndex][0];
        var distanceWidth=ZOOM_SEED[this.ZoomIndex][1];
        if (dataWidth==1 && distanceWidth==0) 
        {
            this.DataWidth=width/this.XPointCount;
            return;
        }

        while(true)
        {
            if((this.DistanceWidth + this.DataWidth) * this.XPointCount + this.DistanceWidth > width)
            {
                this.DistanceWidth -= 0.01;
                break;
            }
            this.DistanceWidth += 0.01;
        }
    }

    //当前坐标信息 是否覆盖最大 最小值输出
    this.IsOverlayMaxMin=function(obj) 
    {
        if (!this.ChartKLine) return false;
        if (!this.ChartKLine.Max || !this.ChartKLine.Min) return false;

        var textWidth=this.Canvas.measureText(obj.Text.Value).width+4;    //刻度文字宽度
        if (obj.Text.TextAlign==='right') obj.X-=textWidth;
        var max=this.ChartKLine.Max, min=this.ChartKLine.Min;
        var isOverlayMax=false, isOverlayMin=false;
        const textHeight=20;    //字体高度
        if (max.X>=obj.X && max.X<=obj.X+textWidth) //最大值X 坐标不在 刻度文字范围内
        {
            var y1=max.Y+textHeight, y2=max.Y-textHeight;
            if ( (y1>=obj.Y-textHeight && y1<=obj.Y+textHeight) || (y2>=obj.Y-textHeight && y2<=obj.Y+textHeight))
                isOverlayMax=true; 
        }

        if (isOverlayMax==true) return true;

        if (min.X>=obj.X && min.X<=obj.X+textWidth)
        {
            var y1=min.Y+textHeight, y2=min.Y-textHeight;
            if ( (y1>=obj.Y-textHeight && y1<=obj.Y+textHeight) || (y2>=obj.Y-textHeight && y2<=obj.Y+textHeight))
                isOverlayMin=true; 
        }

        return isOverlayMax || isOverlayMin;
    }

    //分割x,y轴坐标信息
    this.SplitXYCoordinate=function()
    {
        if (this.XYSplit==false) return;
        if (this.YSplitOperator!=null) this.YSplitOperator.Operator();
        if (this.XSplitOperator!=null) this.XSplitOperator.Operator();
    }

    this.CalculateCount=function(zoomIndex)
    {
        var width=this.ChartBorder.GetWidth()-4;

        return parseInt(width/(ZOOM_SEED[zoomIndex][0] + ZOOM_SEED[zoomIndex][1]));
    }

    this.ZoomUp=function(cursorIndex)
    {
        if (this.ZoomIndex<=0) return false;
        if (this.Data.DataOffset<0) return false;

        var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1;    //最右边的数据索引
        var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index;

        if (lastDataIndex>this.Data.Data.length) lastDataIndex=this.Data.Data.length-1;
        //JSConsole.Chart.Log(`[KLineFrame::ZoomUp] old status. XPointCount=${this.XPointCount} ZoomIndex=${this.ZoomIndex}`);

        --this.ZoomIndex;
        var xPointCount=this.CalculateCount(this.ZoomIndex);

        this.XPointCount=xPointCount;

        this.DataWidth = ZOOM_SEED[this.ZoomIndex][0];
        this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1];
       
        this.TrimKLineDataWidth(this.ChartBorder.GetWidth());
        this.LastCalculateStatus.XPointCount=this.XPointCount;

        //JSConsole.Chart.Log(`[KLineFrame::ZoomUp] new status. XPointCount=${this.XPointCount} ZoomIndex=${this.ZoomIndex}`);

        if (lastDataIndex>=this.Data.Data.length)
        {
            this.Data.DataOffset=this.Data.Data.length-this.XPointCount-2;
            cursorIndex.Index=this.Data.Data.length-this.Data.DataOffset-1;
        }
        else
        {
            if (lastDataIndex<this.XPointCount)
            {
                this.Data.DataOffset=0;
                cursorIndex.Index=lastCursorIndex;
            }
            else
            {
                this.Data.DataOffset = lastDataIndex - this.XPointCount+1;
                cursorIndex.Index=lastCursorIndex-this.Data.DataOffset;
            }
        }

        return true;
    }

    this.ZoomDown=function(cursorIndex)
    {
        if (this.ZoomIndex+1>=ZOOM_SEED.length) return false;
        if (this.Data.DataOffset<0) return false;

        var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1;    //最右边的数据索引
        if (lastDataIndex>=this.Data.Data.length) lastDataIndex=this.Data.Data.length-1;

        //JSConsole.Chart.Log(`[KLineFrame::ZoomDown] old status. XPointCount=${this.XPointCount} ZoomIndex=${this.ZoomIndex}`);

        var xPointCount=this.CalculateCount(this.ZoomIndex+1);
        var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index;

        ++this.ZoomIndex;
        this.XPointCount=xPointCount;
        this.DataWidth = ZOOM_SEED[this.ZoomIndex][0];
        this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1];
        
        this.TrimKLineDataWidth(this.ChartBorder.GetWidth());
        this.LastCalculateStatus.XPointCount=this.XPointCount;

        //JSConsole.Chart.Log(`[KLineFrame::ZoomDown] new status. XPointCount=${this.XPointCount} ZoomIndex=${this.ZoomIndex}`);

        if (lastDataIndex-xPointCount+1<0)
            this.Data.DataOffset=0;
        else
            this.Data.DataOffset = lastDataIndex - this.XPointCount+1;

        cursorIndex.Index=lastCursorIndex-this.Data.DataOffset;

        return true;
    }

    this.DrawCustomHorizontal=function()    //Y轴刻度定制显示
    {
        for(var i in this.CustomHorizontalInfo)
        {
            var item=this.CustomHorizontalInfo[i];
            switch(item.Type)
            {
                case 0: //最新价格刻度
                case 1: //固定价格刻度
                    this.DrawCustomItem(item);  
                    break;
            }
        }
    }

    this.DrawCustomVerticalItem=function(item)
    {
        this.Canvas.save(); 
        if (item.Data.Line.Type==1) this.Canvas.setLineDash([5,5]);   //虚线
        this.Canvas.strokeStyle=item.Data.Line.Color;
        this.Canvas.beginPath();
        if (item.IsHScreen)
        {
            this.Canvas.moveTo(item.Top,ToFixedPoint(item.X));
            this.Canvas.lineTo(item.Bottom,ToFixedPoint(item.X));
        }
        else
        {
            this.Canvas.moveTo(ToFixedPoint(item.X),item.Top);
            this.Canvas.lineTo(ToFixedPoint(item.X),item.Bottom);
        }
        this.Canvas.stroke();
        this.Canvas.restore();
    }

    this.DrawCustomVertical=function()  //X轴定制刻度显示
    {
        if (!this.CustomVerticalInfo) return;
        if (this.CustomVerticalInfo.length<=0) return;
        if (!this.Data) return;

        var isHScreen=this.IsHScreen;
        var top=this.ChartBorder.GetTopEx();
        var bottom=this.ChartBorder.GetBottomEx();
       
        var dataWidth=this.DataWidth;
        var distanceWidth=this.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;

        if (isHScreen) 
        {
            xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
            top=this.ChartBorder.GetLeftEx();
            bottom=this.ChartBorder.GetRightEx();
        }

        var j=0;
        for(var i=this.Data.DataOffset;i<this.Data.Data.length && j<this.XPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var kItem=this.Data.Data[i];
            for(var k in this.CustomVerticalInfo)
            {
                var item=this.CustomVerticalInfo[k];
                if (item.Type!=0) continue;

                if (IFrameSplitOperator.IsNumber(item.Time))
                {
                    if (kItem.Date!=item.Date || kItem.Time!=item.Time) continue;
                }
                else
                {
                    if (kItem.Date!=item.Date) continue;
                }

                var left=xOffset;
                var right=xOffset+dataWidth;
                var x=left+(right-left)/2;  

                var DrawData={X:x, Top:top, Bottom:bottom, Data:item , IsHScreen:isHScreen};
                this.DrawCustomVerticalItem(DrawData);
                if (this.DrawCustomVerticalEvent) 
                    this.DrawCustomVerticalEvent.Callback(this.DrawCustomVerticalEvent, DrawData, this);

                break;
            }
        }

        for(var i=1;j<this.XPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            for(var k in  this.CustomVerticalInfo)
            {
                var item=this.CustomVerticalInfo[k];
                if (item.Type!=1) continue;
                if (item.Space!=i) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                var x=left+(right-left)/2; 
                
                var DrawData={X:x, Top:top, Bottom:bottom, Data:item,IsHScreen:isHScreen };
                this.DrawCustomVerticalItem(DrawData);
                if (this.DrawCustomVerticalEvent) 
                    this.DrawCustomVerticalEvent.Callback(this.DrawCustomVerticalEvent, DrawData, this);

                break;
            }
        }
    }
}

function OverlayKLineFrame()
{
    this.newMethod=KLineFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.MainFrame=null;    //主框架
    this.RightOffset=50;
    this.PenBorder=g_JSChartResource.OverlayFrame.BolderPen; //'rgb(0,0,0)'
    this.IsShow=true;   //坐标是否显示
    this.Title=null;
    this.TitleColor=g_JSChartResource.OverlayFrame.TitleColor;
    this.TitleFont=g_JSChartResource.OverlayFrame.TitleFont;

    this.Draw=function()
    {
        this.SplitXYCoordinate();

        if (this.IsShow)
        {
            this.DrawVertical();
            this.DrawHorizontal();
            this.DrawTitle();
        }
        
        this.SizeChange=false;
        this.XYSplit=false;
    }

    this.DrawTitle=function()   //画标题
    {
        if (!this.Title) return;
        var top = this.ChartBorder.GetTopTitle();
        var bottom = this.ChartBorder.GetBottom();
        var right=this.ChartBorder.GetRight();
        right+=this.RightOffset;

        this.Canvas.fillStyle=this.TitleColor;
        this.Canvas.font=this.TitleFont;
        this.Canvas.textAlign="center";
        this.Canvas.textBaseline="top";

        var xText=right-2,yText=top+(bottom-top)/2;
        this.Canvas.save();
        this.Canvas.translate(xText, yText);
        this.Canvas.rotate(90 * Math.PI / 180);
        this.Canvas.fillText(this.Title, 0, 0);
        this.Canvas.restore();
    }

    //分割x,y轴坐标信息
    this.SplitXYCoordinate=function()
    {
        if (this.XYSplit==false) return;
        if (this.YSplitOperator!=null) this.YSplitOperator.Operator();
        // if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); 子坐标和主坐标X轴一致 所以不用计算
    }

    //画Y轴
    this.DrawHorizontal=function()
    {
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var bottom = this.ChartBorder.GetBottom();
        var top = this.ChartBorder.GetTopTitle();
        var borderRight=this.ChartBorder.Right;
        right+=this.RightOffset;

        var yPrev=null; //上一个坐标y的值
        for(var i=this.HorizontalInfo.length-1; i>=0; --i)  //从上往下画分割线
        {
            var item=this.HorizontalInfo[i];
            var y=this.GetYFromData(item.Value);
            if (y!=null && Math.abs(y-yPrev)<this.MinYDistance) continue;  //两个坐标在近了 就不画了

            if (y >= bottom - 2) this.Canvas.textBaseline = 'bottom';
            else if (y <= top + 2) this.Canvas.textBaseline = 'top';
            else this.Canvas.textBaseline = "middle";

            this.Canvas.strokeStyle=this.PenBorder;
            this.Canvas.beginPath();
            this.Canvas.moveTo(right-2,ToFixedPoint(y));
            this.Canvas.lineTo(right,ToFixedPoint(y));
            this.Canvas.stroke();

            //坐标信息 右边 间距小于10 不画坐标
            if (item.Message[1]!=null && borderRight>10)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="left";
                this.Canvas.fillText(item.Message[1],right+2,y);
            }

            yPrev=y;
        }
    }

    //画X轴
    this.DrawVertical=function()
    {
        var top=this.ChartBorder.GetTopEx();
        //var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var bottom=this.ChartBorder.GetBottomEx();
        right+=this.RightOffset;

        this.Canvas.strokeStyle=this.PenBorder;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ToFixedPoint(right),ToFixedPoint(top));
        this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(bottom));
        this.Canvas.stroke();
    }
}

//K线横屏框架
function KLineHScreenFrame()
{
    this.newMethod=KLineFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.IsHScreen=true;        //是否是横屏

    //画标题背景色
    this.DrawTitleBG=function()
    {
        if (this.ChartBorder.TitleHeight<=0) return;

        var left=ToFixedPoint(this.ChartBorder.GetRightTitle());
        var top=ToFixedPoint(this.ChartBorder.GetTop());
        var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
        var width=this.ChartBorder.TitleHeight;
        var height=bottom-top;

        this.Canvas.fillStyle=this.TitleBGColor;
        this.Canvas.fillRect(left,top,width,height);
    }

    this.DrawToolbar=function()
    {
        return;
    }

    this.GetYFromData=function(value)
    {
        if(value<=this.HorizontalMin) return this.ChartBorder.GetLeftEx();
        if(value>=this.HorizontalMax) return this.ChartBorder.GetRightEx();

        var width=this.ChartBorder.GetWidthEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin);
        return this.ChartBorder.GetLeftEx()+width;
    }

    //画Y轴
    this.DrawHorizontal=function()
    {
        var top=this.ChartBorder.GetTop();
        var bottom=this.ChartBorder.GetBottom();
        var borderTop=this.ChartBorder.Top;
        var borderBottom=this.ChartBorder.Bottom;

        var yPrev=null; //上一个坐标y的值
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        for(var i=this.HorizontalInfo.length-1; i>=0; --i)  //从左往右画分割线
        {
            var item=this.HorizontalInfo[i];
            var y=this.GetYFromData(item.Value);
            if (y!=null && Math.abs(y-yPrev)<this.MinYDistance) continue;  //两个坐标在近了 就不画了

            this.Canvas.strokeStyle=item.LineColor;
            this.Canvas.beginPath();
            this.Canvas.moveTo(ToFixedPoint(y),top);
            this.Canvas.lineTo(ToFixedPoint(y),bottom);
            this.Canvas.stroke();

            //坐标信息 左边 间距小于10 不画坐标
            if (item.Message[0]!=null && borderTop>10*pixelTatio)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="right";
                this.Canvas.textBaseline="middle";

                var xText=y,yText=top;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);
                this.Canvas.fillText(item.Message[0], -2, 0);
                this.Canvas.restore();
            }

            //坐标信息 右边 间距小于10 不画坐标
            if (item.Message[1]!=null && borderBottom>10*pixelTatio)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                var xText=y,yText=bottom;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);
                this.Canvas.fillText(item.Message[1], 2, 0);
                this.Canvas.restore();
            }

            yPrev=y;
        }
    }

    //Y刻度画在左边内部
    this.DrawInsideHorizontal = function () 
    {
        if (this.IsShowYText[0]===false && this.IsShowYText[1]===false) return;

        var left = this.ChartBorder.GetLeft();
        var right = this.ChartBorder.GetRightEx();
        var top=this.ChartBorder.GetTop();
        var bottom=this.ChartBorder.GetBottom();
        var borderTop=this.ChartBorder.Top;
        var borderBottom=this.ChartBorder.Bottom;
        var titleHeight = this.ChartBorder.TitleHeight;
        var pixelTatio = GetDevicePixelRatio();
        
        if ( (borderTop<10*pixelTatio && this.IsShowYText[0]===true) || (borderBottom<10*pixelTatio && this.IsShowYText[1]===true) )
        {
            var pixelTatio = GetDevicePixelRatio();
            var yPrev = null; //上一个坐标y的值
            for (var i = this.HorizontalInfo.length - 1; i >= 0; --i)  //从上往下画分割线
            {
                var item = this.HorizontalInfo[i];
                var y = this.GetYFromData(item.Value);
                if (y != null && yPrev!=null && Math.abs(y - yPrev) < this.MinYDistance) continue;  //两个坐标在近了 就不画了

                //坐标信息 左边 间距小于10 画在内部
                if (item.Message[0] != null && borderTop < 10*pixelTatio && this.IsShowYText[0]===true) 
                {
                    if (item.Font != null) this.Canvas.font = item.Font;
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.textAlign = "left";
                    if (y >= right - 2) this.Canvas.textBaseline = 'top';
                    else if (y <= left + 2) this.Canvas.textBaseline = 'bottom';
                    else this.Canvas.textBaseline = "middle";

                    var textObj={ X:left, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[0]}} ;
                    var xText=y,yText=top;
                    this.Canvas.save();
                    this.Canvas.translate(xText, yText);
                    this.Canvas.rotate(90 * Math.PI / 180);
                    this.Canvas.fillText(item.Message[0], -2, 0);
                    this.Canvas.restore();
                }

                if (item.Message[1] != null && borderBottom < 10*pixelTatio && this.IsShowYText[1]===true)
                {
                    if (item.Font != null) this.Canvas.font = item.Font;
                    this.Canvas.fillStyle = item.TextColor;
                    this.Canvas.textAlign = "right";
                    if (y >= right - 2) this.Canvas.textBaseline = 'top';
                    else if (y <= left + 2) this.Canvas.textBaseline = 'bottom';
                    else this.Canvas.textBaseline = "middle";
                    var textWidth = this.Canvas.measureText(item.Message[1]).width;
                    var textObj={ X:right-textWidth, Y:y, Text:{ BaseLine:this.Canvas.textBaseline, TextAlign: this.Canvas.textAlign, Font:this.Canvas.font, Value:item.Message[1]}} ;

                    var xText=y,yText=bottom;
                    this.Canvas.save();
                    this.Canvas.translate(xText, yText);
                    this.Canvas.rotate(90 * Math.PI / 180);
                    this.Canvas.fillText(item.Message[1], 2, 0);
                    this.Canvas.restore();
                }
                yPrev = y;
            }
        }
    }

    this.GetXFromIndex=function(index)
    {
        if (index < 0) index = 0;
	    if (index > this.xPointCount - 1) index = this.xPointCount - 1;

        var offset=this.ChartBorder.GetTop()+2+this.DistanceWidth/2+this.DataWidth/2;
        for(var i=1;i<=index;++i)
        {
            offset+=this.DistanceWidth+this.DataWidth;
        }

        return offset;
    }

    //画X轴
    this.DrawVertical=function()
    {
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRightTitle();
        var bottom=this.ChartBorder.GetBottom();

        var xPrev=null; //上一个坐标x的值
        for(var i in this.VerticalInfo)
        {
            var x=this.GetXFromIndex(this.VerticalInfo[i].Value);
            if (x>=bottom) break;
            if (xPrev!=null && Math.abs(x-xPrev)<80) continue;

            this.Canvas.strokeStyle=this.VerticalInfo[i].LineColor;
            this.Canvas.beginPath();
            this.Canvas.moveTo(left,ToFixedPoint(x));
            this.Canvas.lineTo(right,ToFixedPoint(x));
            this.Canvas.stroke();

            if (this.VerticalInfo[i].Message[0]!=null)
            {
                if (this.VerticalInfo[i].Font!=null)
                    this.Canvas.font=this.VerticalInfo[i].Font;

                this.Canvas.fillStyle=this.VerticalInfo[i].TextColor;
                var testWidth=this.Canvas.measureText(this.VerticalInfo[i].Message[0]).width;
                if (x<testWidth/2)
                {
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="top";
                }
                else
                {
                    this.Canvas.textAlign="center";
                    this.Canvas.textBaseline="top";
                }

                var xText=left,yText=x;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);
                this.Canvas.fillText(this.VerticalInfo[i].Message[0], 0, 0);
                this.Canvas.restore();
            }

            xPrev=x;
        }
    }

    //Y坐标转y轴数值
    this.GetYData=function(x)
    {
        if (x<this.ChartBorder.GetLeftEx()) return this.HorizontalMin;
		if (x>this.ChartBorder.GetRightEx()) return this.HorizontalMax;

		return (x-this.ChartBorder.GetLeftEx())/this.ChartBorder.GetWidthEx()*(this.HorizontalMax-this.HorizontalMin)+this.HorizontalMin;
    }

    //X坐标转x轴数值
    this.GetXData=function(y)
    {
        if (y<=this.ChartBorder.GetTop()) return 0;
		if (y>=this.ChartBorder.GetBottom()) return this.XPointCount;

		return (y-this.ChartBorder.GetTop())*(this.XPointCount*1.0/this.ChartBorder.GetHeight());
    }

    //计算数据宽度
    this.CalculateDataWidth=function()
    {
        if (this.XPointCount<2) return;

        var width=this.ChartBorder.GetHeight()-4;

        for(var i=0;i<ZOOM_SEED.length;++i)
        {
            if((ZOOM_SEED[i][0] + ZOOM_SEED[i][1]) * this.XPointCount < width)
            {
                this.ZoomIndex=i;
                this.DataWidth = ZOOM_SEED[i][0];
                this.DistanceWidth = ZOOM_SEED[i][1];
                if (i == 0) break;      // 如果是最大的缩放因子,不再调整数据宽度

                this.TrimKLineDataWidth(width);
                return;
            }
        }
    }

    this.CalculateCount=function(zoomIndex) //计算当天的缩放比例下 一屏显示的数据个数
    {
        var width=this.ChartBorder.GetHeight();
        return parseInt(width/(ZOOM_SEED[zoomIndex][0] + ZOOM_SEED[zoomIndex][1]));
    }

    this.ZoomUp=function(cursorIndex)
    {
        if (this.ZoomIndex<=0) return false;
        if (this.Data.DataOffset<0) return false;

        var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1;    //最右边的数据索引
        var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index;

        if (lastDataIndex>this.Data.Data.length) lastDataIndex=this.Data.Data.length-1;

        --this.ZoomIndex;
        var xPointCount=this.CalculateCount(this.ZoomIndex);

        this.XPointCount=xPointCount;

        this.DataWidth = ZOOM_SEED[this.ZoomIndex][0];
	    this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1];

        this.TrimKLineDataWidth(this.ChartBorder.GetHeight());

        if (lastDataIndex>=this.Data.Data.length)
        {
            this.Data.DataOffset=this.Data.Data.length-this.XPointCount-2;
            cursorIndex.Index=this.Data.Data.length-this.Data.DataOffset-1;
        }
        else
        {
            if (lastDataIndex<this.XPointCount)
            {
                this.Data.DataOffset=0;
                cursorIndex.Index=lastCursorIndex;
            }
            else
            {
                this.Data.DataOffset = lastDataIndex - this.XPointCount+1;
                cursorIndex.Index=lastCursorIndex-this.Data.DataOffset;
            }
        }

        return true;
    }

    this.ZoomDown=function(cursorIndex)
    {
        if (this.ZoomIndex+1>=ZOOM_SEED.length) return false;
        if (this.Data.DataOffset<0) return false;

        var lastDataIndex = this.Data.DataOffset + this.XPointCount - 1;    //最右边的数据索引
        if (lastDataIndex>=this.Data.Data.length) lastDataIndex=this.Data.Data.length-1;
        var xPointCount=this.CalculateCount(this.ZoomIndex+1);

        var lastCursorIndex=this.Data.DataOffset + cursorIndex.Index;

        ++this.ZoomIndex;
        this.XPointCount=xPointCount;
        this.DataWidth = ZOOM_SEED[this.ZoomIndex][0];
	    this.DistanceWidth = ZOOM_SEED[this.ZoomIndex][1];

        this.TrimKLineDataWidth(this.ChartBorder.GetHeight());

        if (lastDataIndex-xPointCount+1<0)
            this.Data.DataOffset=0;
        else
            this.Data.DataOffset = lastDataIndex - this.XPointCount+1;

        cursorIndex.Index=lastCursorIndex-this.Data.DataOffset;

        return true;
    }
}


function OverlayKLineHScreenFrame()
{
    this.newMethod=KLineHScreenFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.MainFrame=null;    //主框架
    this.RightOffset=50;
    this.PenBorder=g_JSChartResource.OverlayFrame.BolderPen; //'rgb(0,0,0)'
    this.IsShow=true;   //坐标是否显示
    this.Title=null;
    this.TitleColor=g_JSChartResource.OverlayFrame.TitleColor;
    this.TitleFont=g_JSChartResource.OverlayFrame.TitleFont;

    this.Draw=function()
    {
        this.SplitXYCoordinate();

        if (this.IsShow)
        {
            this.DrawVertical();
            this.DrawHorizontal();
            this.DrawTitle();
        }
        
        this.SizeChange=false;
        this.XYSplit=false;
    }

    this.DrawTitle=function()   //画标题
    {
        /*
        if (!this.Title) return;
        var top = this.ChartBorder.GetTopTitle();
        var bottom = this.ChartBorder.GetBottom();
        var right=this.ChartBorder.GetRight();
        right+=this.RightOffset;

        this.Canvas.fillStyle=this.TitleColor;
        this.Canvas.font=this.TitleFont;
        this.Canvas.textAlign="center";
        this.Canvas.textBaseline="top";

        var xText=right-2,yText=top+(bottom-top)/2;
        this.Canvas.save();
        this.Canvas.translate(xText, yText);
        this.Canvas.rotate(90 * Math.PI / 180);
        this.Canvas.fillText(this.Title, 0, 0);
        this.Canvas.restore();
        */
    }

    //分割x,y轴坐标信息
    this.SplitXYCoordinate=function()
    {
        if (this.XYSplit==false) return;
        if (this.YSplitOperator!=null) this.YSplitOperator.Operator();
        // if (this.XSplitOperator!=null) this.XSplitOperator.Operator(); 子坐标和主坐标X轴一致 所以不用计算
    }

    //画Y轴
    this.DrawHorizontal=function()
    {
        /*
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var bottom = this.ChartBorder.GetBottom();
        var top = this.ChartBorder.GetTopTitle();
        var borderRight=this.ChartBorder.Right;
        right+=this.RightOffset;

        var yPrev=null; //上一个坐标y的值
        for(var i=this.HorizontalInfo.length-1; i>=0; --i)  //从上往下画分割线
        {
            var item=this.HorizontalInfo[i];
            var y=this.GetYFromData(item.Value);
            if (y!=null && Math.abs(y-yPrev)<this.MinYDistance) continue;  //两个坐标在近了 就不画了

            if (y >= bottom - 2) this.Canvas.textBaseline = 'bottom';
            else if (y <= top + 2) this.Canvas.textBaseline = 'top';
            else this.Canvas.textBaseline = "middle";

            this.Canvas.strokeStyle=this.PenBorder;
            this.Canvas.beginPath();
            this.Canvas.moveTo(right-2,ToFixedPoint(y));
            this.Canvas.lineTo(right,ToFixedPoint(y));
            this.Canvas.stroke();

            //坐标信息 右边 间距小于10 不画坐标
            if (item.Message[1]!=null && borderRight>10)
            {
                if (item.Font!=null) this.Canvas.font=item.Font;

                this.Canvas.fillStyle=item.TextColor;
                this.Canvas.textAlign="left";
                this.Canvas.fillText(item.Message[1],right+2,y);
            }

            yPrev=y;
        }
        */
    }

    //画X轴
    this.DrawVertical=function()
    {
        /*
        var top=this.ChartBorder.GetTopEx();
        //var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var bottom=this.ChartBorder.GetBottomEx();
        right+=this.RightOffset;

        this.Canvas.strokeStyle=this.PenBorder;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ToFixedPoint(right),ToFixedPoint(top));
        this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(bottom));
        this.Canvas.stroke();
        */
    }
}

function SubFrameItem()
{
    this.Frame;
    this.Height;
    this.OverlayIndex=[];   //叠加指标
    this.Interval=60;   //子坐标间间距
}

function OverlayIndexItem()
{
    this.Frame;
    this.ChartPaint=[];
    this.Identify=Guid();
    this.Scprit;        //脚本

    this.UpdateFrameMaxMin=function()   //调整坐标最大 最小值
    {
        var value={Max:null, Min:null}
        if (this.Frame.YSpecificMaxMin) //固定坐标
        {
            value.Max=this.Frame.YSpecificMaxMin.Max;
            value.Min=this.Frame.YSpecificMaxMin.Min;
        }
        else
        {
            for(var i in this.ChartPaint)
            {
                var paint=this.ChartPaint[i];
                var range=paint.GetMaxMin();

                if (value.Max==null || value.Max<range.Max) value.Max=range.Max;
                if (value.Min==null || value.Min>range.Min) value.Min=range.Min
            }
        }

        if (value.Max!=null && value.Min!=null)
        {
            this.Frame.HorizontalMax=value.Max;
            this.Frame.HorizontalMin=value.Min;
            this.Frame.XYSplit=true;
        }
    }
}

//行情框架
function HQTradeFrame()
{
    this.SubFrame=new Array();              //SubFrameItem 数组
    this.SizeChange=true;                   //大小是否改变
    this.ChartBorder;
    this.Canvas;                            //画布
    this.ScreenImageData;                   //截图
    this.Data;                              //主数据
    this.Position;                          //画布的位置
    this.SizeChange=true;

    this.CalculateChartBorder=function()    //计算每个子框架的边框信息
    {
        if (this.SubFrame.length<=0) return;

        var top=this.ChartBorder.GetTop();
        var height=this.ChartBorder.GetHeight();
        var totalHeight=0;

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            totalHeight+=item.Height;
        }

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            item.Frame.ChartBorder.Top=top;
            item.Frame.ChartBorder.Left=this.ChartBorder.Left;
            item.Frame.ChartBorder.Right=this.ChartBorder.Right;
            var frameHeight=height*(item.Height/totalHeight)+top;
            item.Frame.ChartBorder.Bottom=this.ChartBorder.GetChartHeight()-frameHeight;
            top=frameHeight;
        }
    }

    this.CalculateChartBorder2=function()   //计算每个子框架的边框信息(思维导图用)
    {
        if (this.SubFrame.length<=0) return;

        var top=this.ChartBorder.Top;
        var bottom=this.ChartBorder.Bottom;
        var height=this.ChartBorder.GetHeight();
        var totalHeight=0;

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            totalHeight+=item.Height;
        }

        var tempHeight=0;
        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            item.Frame.ChartBorder.Top=top;
            item.Frame.ChartBorder.Left=this.ChartBorder.Left;
            item.Frame.ChartBorder.Right=this.ChartBorder.Right;
            item.Frame.ChartBorder.X=this.ChartBorder.X;
            item.Frame.ChartBorder.Y=this.ChartBorder.Y;
            item.Frame.ChartBorder.Width=this.ChartBorder.Width;
            item.Frame.ChartBorder.Height=this.ChartBorder.Height;
            var frameHeight=height*(item.Height/totalHeight);
            tempHeight+=frameHeight;
            bottom=this.ChartBorder.Bottom+(height-tempHeight);
            item.Frame.ChartBorder.Bottom=bottom;
            top+=frameHeight;
        }
    }

    this.Draw=function()
    {
        if (this.SizeChange===true) this.CalculateChartBorder();

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            item.Frame.Draw();

            var rightOffset=item.Interval;
            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                //把主坐标部分设置给子坐标下来
                overlayItem.Frame.DataWidth=item.Frame.DataWidth;
                overlayItem.Frame.DistanceWidth=item.Frame.DistanceWidth;
                overlayItem.Frame.XPointCount=item.Frame.XPointCount;
                if (overlayItem.ChartPaint.length>0 && overlayItem.Frame.IsShow) 
                {
                    overlayItem.Frame.RightOffset=rightOffset;
                    overlayItem.Frame.Draw();
                    rightOffset+=item.Interval;
                }
            }
        }

        this.SizeChange=false;
    }

    this.DrawOveraly=function()
    {
        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                for(var k in overlayItem.ChartPaint)
                {
                    if (overlayItem.ChartPaint[k].IsShow)
                        overlayItem.ChartPaint[k].Draw();
                }
            }
        }
    }

    this.DrawLock=function()
    {
        for (var i in this.SubFrame)
        {
            var item = this.SubFrame[i];
            item.Frame.DrawLock();
        }
    }

    this.CalculateLock=function()
    {
        for (var i in this.SubFrame)
        {
            var item = this.SubFrame[i];
            item.Frame.CalculateLock();
        }
    }

    this.DrawInsideHorizontal = function () 
    {
        for (var i in this.SubFrame) 
        {
          var item = this.SubFrame[i];
          if (item.Frame.DrawInsideHorizontal) item.Frame.DrawInsideHorizontal();
        }
    }

    this.DrawCustomHorizontal=function()    //定制坐标
    {
        for (var i in this.SubFrame) 
        {
          var item = this.SubFrame[i];
          if (item.Frame.DrawCustomHorizontal) item.Frame.DrawCustomHorizontal();
        }
    }

    this.DrawCustomVertical=function(event)
    {
        for (var i in this.SubFrame) 
        {
          var item = this.SubFrame[i];
          item.Frame.DrawCustomVerticalEvent=event;
          if (item.Frame.DrawCustomVertical) item.Frame.DrawCustomVertical();
        }
    }

    this.SetSizeChage=function(sizeChange)
    {
        this.SizeChange=sizeChange;

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            item.Frame.SizeChange=sizeChange;

            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                if (overlayItem.Frame) overlayItem.Frame.SizeChange=sizeChange;
            }
        }

        //画布的位置
        if (this.ChartBorder.UIElement)
        {
            this.Position={
                X:this.ChartBorder.UIElement.offsetLeft,
                Y:this.ChartBorder.UIElement.offsetTop,
                W:this.ChartBorder.UIElement.clientWidth,
                H:this.ChartBorder.UIElement.clientHeight
            };
        }
    }

    //图形快照
    this.Snapshot=function()
    {
        this.ScreenImageData=this.Canvas.getImageData(0,0,this.ChartBorder.GetChartWidth(),this.ChartBorder.GetChartHeight());
    }

    this.GetXData=function(x)
    {
        return this.SubFrame[0].Frame.GetXData(x);
    }

    this.GetYData=function(y,outObject) //outObject 可以保存返回的额外数据
    {
        var frame;
        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            var left=item.Frame.ChartBorder.GetLeft();
            var top=item.Frame.ChartBorder.GetTopEx();
            var width=item.Frame.ChartBorder.GetWidth();
            var height=item.Frame.ChartBorder.GetHeightEx();

            item.Frame.Canvas.beginPath();
            item.Frame.Canvas.rect(left,top,width,height);
            if (item.Frame.Canvas.isPointInPath(left,y))
            {
                frame=item.Frame;
                if (outObject) outObject.FrameID=parseInt(i);   //转成整形
                break;
            }
        }

        if (frame!=null) 
        {
            if (frame.RightFrame) outObject.RightYValue=frame.RightFrame.GetYData(y);    //右侧子坐标

            var yValue=frame.GetYData(y);
            if (frame.YSplitOperator.CoordinateType==1) //百分比坐标 右边显示百分比信息
            {
                var  firstOpenPrice=frame.YSplitOperator.GetFirstOpenPrice();
                outObject.RightYValue=((yValue-firstOpenPrice)/firstOpenPrice*100).toFixed(2)+'%';
            }
            return yValue;
        }
    }

    this.PtInFrame=function(x,y)    //鼠标哪个指标窗口
    {
        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            var left=item.Frame.ChartBorder.GetLeft();
            var top=item.Frame.ChartBorder.GetTop();
            var width=item.Frame.ChartBorder.GetWidth();
            var height=item.Frame.ChartBorder.GetHeight();

            item.Frame.Canvas.beginPath();
            item.Frame.Canvas.rect(left,top,width,height);
            if (item.Frame.Canvas.isPointInPath(x,y))
            {
                return parseInt(i);   //转成整形
            }
        }

        return -1;
    }

    this.GetXFromIndex=function(index)
    {
        return this.SubFrame[0].Frame.GetXFromIndex(index);
    }

    this.GetYFromData=function(value)
    {
        return this.SubFrame[0].Frame.GetYFromData(value);
    }

    this.ZoomUp=function(cursorIndex)
    {
        var result=this.SubFrame[0].Frame.ZoomUp(cursorIndex);
        var mainFrame=this.SubFrame[0].Frame;
        for(var i=0;i<this.SubFrame.length;++i)
        {
            var item=this.SubFrame[i];
            if (i>0) //第1个窗口主坐标已经算好了
            {
                item.Frame.XPointCount= mainFrame.XPointCount;
                item.Frame.ZoomIndex= mainFrame.ZoomIndex;
                item.Frame.DataWidth= mainFrame.DataWidth;
                item.Frame.DistanceWidth= mainFrame.DistanceWidth;
                item.Frame.LastCalculateStatus.Width=mainFrame.LastCalculateStatus.Width;
                item.Frame.LastCalculateStatus.XPointCount=mainFrame.LastCalculateStatus.XPointCount;
            }

            for(var j in item.OverlayIndex)
            {
                var overlayItem=this.SubFrame[i].OverlayIndex[j];
                overlayItem.Frame.XPointCount= mainFrame.XPointCount;
                overlayItem.Frame.ZoomIndex= mainFrame.ZoomIndex;
                overlayItem.Frame.DataWidth= mainFrame.DataWidth;
                overlayItem.Frame.DistanceWidth= mainFrame.DistanceWidth;
                overlayItem.Frame.LastCalculateStatus.Width=mainFrame.LastCalculateStatus.Width;
                overlayItem.Frame.LastCalculateStatus.XPointCount=mainFrame.LastCalculateStatus.XPointCount;
            }
        }

        return result;
    }

    this.ZoomDown=function(cursorIndex)
    {
        var result=this.SubFrame[0].Frame.ZoomDown(cursorIndex);
        var mainFrame=this.SubFrame[0].Frame;
        for(var i=0;i<this.SubFrame.length;++i)
        {
            var item=this.SubFrame[i];
            if (i>0)    //第1个窗口主坐标已经算好了
            {
                item.Frame.XPointCount= mainFrame.XPointCount;
                item.Frame.ZoomIndex= mainFrame.ZoomIndex;
                item.Frame.DataWidth= mainFrame.DataWidth;
                item.Frame.DistanceWidth= mainFrame.DistanceWidth;
                item.Frame.LastCalculateStatus.Width=mainFrame.LastCalculateStatus.Width;
                item.Frame.LastCalculateStatus.XPointCount=mainFrame.LastCalculateStatus.XPointCount;
            }

            for(var j in item.OverlayIndex)
            {
                var overlayItem=this.SubFrame[i].OverlayIndex[j];
                overlayItem.Frame.XPointCount= mainFrame.XPointCount;
                overlayItem.Frame.ZoomIndex= mainFrame.ZoomIndex;
                overlayItem.Frame.DataWidth= mainFrame.DataWidth;
                overlayItem.Frame.DistanceWidth= mainFrame.DistanceWidth;
                overlayItem.Frame.LastCalculateStatus.Width=mainFrame.LastCalculateStatus.Width;
                overlayItem.Frame.LastCalculateStatus.XPointCount=mainFrame.LastCalculateStatus.XPointCount;
            }
        }

        return result;
    }

    //设置重新计算刻度坐标
    this.ResetXYSplit=function()
    {
        for(let i in this.SubFrame)
        {
            this.SubFrame[i].Frame.XYSplit=true;
        }
    }

    this.SetLanguage=function(languageID)
    {
        for(let i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            if (item && item.Frame )
            {
                if (item.Frame.YSplitOperator) item.Frame.YSplitOperator.LanguageID=languageID;
                if (item.Frame.XSplitOperator) item.Frame.XSplitOperator.LanguageID=languageID;
            }
        }
    }
}

//行情框架横屏
function HQTradeHScreenFrame()
{
    this.newMethod=HQTradeFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.IsHScreen=true;        //是否是横屏

    this.CalculateChartBorder=function()    //计算每个子框架的边框信息
    {
        if (this.SubFrame.length<=0) return;

        var right=this.ChartBorder.Right;
        var left=this.ChartBorder.GetRight();
        var width=this.ChartBorder.GetWidth();
        var totalHeight=0;

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            totalHeight+=item.Height;
        }

        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            item.Frame.ChartBorder.Top=this.ChartBorder.Top;
            item.Frame.ChartBorder.Bottom=this.ChartBorder.Bottom;

            var frameWidth=width*(item.Height/totalHeight);
            item.Frame.ChartBorder.Right=right;
            item.Frame.ChartBorder.Left=left-frameWidth;
            
            right+=frameWidth;
            left-=frameWidth;
        }
    }

    this.GetYData=function(x,outObject)
    {
        var frame;
        for(var i in this.SubFrame)
        {
            var item=this.SubFrame[i];
            var left=item.Frame.ChartBorder.GetLeftEx();
            var top=item.Frame.ChartBorder.GetTop();
            var width=item.Frame.ChartBorder.GetWidthEx();
            var height=item.Frame.ChartBorder.GetHeight();

            item.Frame.Canvas.beginPath();
            item.Frame.Canvas.rect(left,top,width,height);
            if (item.Frame.Canvas.isPointInPath(x,top))
            {
                frame=item.Frame;
                if (outObject) outObject.FrameID=i;
                break;
            }
        }

        if (frame!=null) return frame.GetYData(x);
    }
}


function SimpleChartFrame()
{
    this.newMethod=AverageWidthFrame;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ScreenImageData;                   //截图
    this.Position;                          //画布的位置

    this.DrawFrame=function()
    {
        if (this.XPointCount>0)
        {
            let dInterval=this.ChartBorder.GetWidth()/(6*this.XPointCount); //分6份, 数据4 间距2
            this.DistanceWidth=2*dInterval;
			this.DataWidth=4*dInterval;
        }

        this.SplitXYCoordinate();
        this.DrawHorizontal();
        this.DrawVertical();
    }

    this.GetXFromIndex=function(index)
    {
        if (index < 0) index = 0;
	    if (index > this.xPointCount - 1) index = this.xPointCount - 1;

        var offset=this.ChartBorder.GetLeft()+2+this.DistanceWidth/2+this.DataWidth/2;
        for(var i=1;i<=index;++i)
        {
            offset+=this.DistanceWidth+this.DataWidth;
        }

        return offset;
    }

    //分割x,y轴坐标信息
    this.SplitXYCoordinate=function()
    {
        if (this.XYSplit==false) return;
        if (this.YSplitOperator!=null) this.YSplitOperator.Operator();
        if (this.XSplitOperator!=null) this.XSplitOperator.Operator();
    }

    //图形快照
    this.Snapshot=function()
    {
        this.ScreenImageData=this.Canvas.getImageData(0,0,this.ChartBorder.GetChartWidth(),this.ChartBorder.GetChartHeight());
    }

    this.SetSizeChage=function(sizeChange)
    {
        this.SizeChange=sizeChange;

        //画布的位置
        this.Position={
            X:this.ChartBorder.UIElement.offsetLeft,
            Y:this.ChartBorder.UIElement.offsetTop,
            W:this.ChartBorder.UIElement.clientWidth,
            H:this.ChartBorder.UIElement.clientHeight
        };
    }
}


//历史K线数据
function HistoryData()
{
    this.Date;
    this.YClose;
    this.Open;
    this.Close;
    this.High;
    this.Low;
    this.Vol;
    this.Amount;
    this.Time;      //mmhh 或者 mmhhss
    this.FlowCapital=0;   //流通股本
    this.Position=null;   //持仓量

    //指数才有的数据
    this.Stop;  //停牌家数
    this.Up;    //上涨
    this.Down;  //下跌
    this.Unchanged; //平盘
}

//数据复制
HistoryData.Copy=function(data)
{
    var newData=new HistoryData();
    newData.Date=data.Date;
    newData.YClose=data.YClose;
    newData.Open=data.Open;
    newData.Close=data.Close;
    newData.High=data.High;
    newData.Low=data.Low;
    newData.Vol=data.Vol;
    newData.Amount=data.Amount;
    newData.Time=data.Time;
    newData.FlowCapital=data.FlowCapital;

    newData.Stop=data.Stop;
    newData.Up=data.Up;
    newData.Down=data.Down;
    newData.Unchanged=data.Unchanged;

    return newData;
}

//把数据 src 复制到 dest中
HistoryData.CopyTo=function(dest,src)
{
    dest.Date=src.Date;
    dest.YClose=src.YClose;
    dest.Open=src.Open;
    dest.Close=src.Close;
    dest.High=src.High;
    dest.Low=src.Low;
    dest.Vol=src.Vol;
    dest.Amount=src.Amount;
    dest.Time=src.Time;
    dest.FlowCapital=src.FlowCapital;

    dest.Stop=src.Stop;
    dest.Up=src.Up;
    dest.Down=src.Down;
    dest.Unchanged=src.Unchanged;
}

//数据复权拷贝
HistoryData.CopyRight=function(data,seed)
{
    var newData=new HistoryData();
    newData.Date=data.Date;
    newData.YClose=data.YClose*seed;
    newData.Open=data.Open*seed;
    newData.Close=data.Close*seed;
    newData.High=data.High*seed;
    newData.Low=data.Low*seed;

    newData.Vol=data.Vol;
    newData.Amount=data.Amount;
    newData.FlowCapital=data.FlowCapital;

    return newData;
}

function MinuteData()
{
    this.Close;
    this.Open;
    this.High;
    this.Low;
    this.Vol;           //量
    this.Amount;        //金额
    this.DateTime;
    this.Increase;      //涨幅
    this.Risefall;      //涨跌
    this.AvPrice;       //均价
    this.Lead=null;     //领先指标
    this.Time;          //时间
    this.Date;          //日期
    this.Position=null;  //持仓量
}

//单指标数据
function SingleData()
{
    this.Date;  //日期
    this.Time;  //时间
    this.Value; //数据  (可以是一个数组)
}

var KLINE_INFO_TYPE=
{
    INVESTOR:1,         //互动易
    ANNOUNCEMENT:2,     //公告
    PFORECAST:3,        //业绩预告

    ANNOUNCEMENT_QUARTER_1:4,   //一季度报
    ANNOUNCEMENT_QUARTER_2:5,   //半年报
    ANNOUNCEMENT_QUARTER_3:6,   //2季度报
    ANNOUNCEMENT_QUARTER_4:7,   //年报

    RESEARCH:8,                 //调研
    BLOCKTRADING:9,             //大宗交易
    TRADEDETAIL:10              //龙虎榜


}

function KLineInfoData()
{
    this.ID;
    this.Date;
    this.Title;
    this.InfoType;
    this.ExtendData;    //扩展数据
}

function DataPlus () { };       //外部数据计算方法接口
DataPlus.GetMinutePeriodData=null;
/*
DataPlus.GetMinutePeriodData=function(period,data,self)
{

}
*/

function ChartData()
{
    this.Data=new Array();
    this.DataOffset=0;                        //数据偏移
    this.Period=0;                            //周期 0 日线 1 周线 2 月线 3年线
    this.Right=0;                             //复权 0 不复权 1 前复权 2 后复权
    this.Symbol;                              //股票代码

    this.Data2=new Array();                   //第1组数据 走势图:历史分钟数据

    this.GetCloseMA=function(dayCount)
    {
        var result=new Array();
        for (var i = 0, len = this.Data.length; i < len; i++)
        {
            if (i < dayCount)
            {
                result[i]=null;
                continue;
            }

            var sum = 0;
            for (var j = 0; j < dayCount; j++)
            {
                sum += this.Data[i - j].Close;
            }
            result[i]=sum / dayCount;
        }
        return result;
    }

    this.GetVolMA=function(dayCount)
    {
    var result=new Array();
    for (var i = 0, len = this.Data.length; i < len; i++)
    {
        if (i < dayCount)
        {
            result[i]=null;
            continue;
        }

        var sum = 0;
        for (var j = 0; j < dayCount; j++)
        {
            sum += this.Data[i - j].Vol;
        }
        result[i]=sum / dayCount;
    }
    return result;
    }

    this.GetAmountMA=function(dayCount)
    {
    var result=new Array();
    for (var i = 0, len = this.Data.length; i < len; i++)
    {
        if (i < dayCount)
        {
            result[i]=null;
            continue;
        }

        var sum = 0;
        for (var j = 0; j < dayCount; j++)
        {
            sum += this.Data[i - j].Amount;
        }
        result[i]=sum / dayCount;
    }
    return result;
    }

    //获取收盘价
    this.GetClose=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Close;
        }

        return result;
    }

    this.GetYClose=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].YClose;
        }

        return result;
    }

    this.GetHigh=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].High;
        }

        return result;
    }

    this.GetLow=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Low;
        }

        return result;
    }

    this.GetOpen=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Open;
        }

        return result;
    }

    this.GetVol=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Vol;
        }

        return result;
    }

    this.GetAmount=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Amount;
        }

        return result;
    }

    this.GetPosition=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Position;
        }

        return result;
    }

    this.GetLead=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Lead;
        }

        return result;
    }

    this.GetDate=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Date;
        }

        return result;
    }

    this.GetTime=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Time;
        }

        return result;
    }

    this.GetUp=function()   //上涨家数
    {
        var result=[];
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Up;
        }

        return result;
    }

    this.GetDown=function() //下跌家数
    {
        var result=[];
        for(var i in this.Data)
        {
            result[i]=this.Data[i].Down;
        }

        return result;
    }

    this.GetYear=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=parseInt(this.Data[i].Date/10000);
        }

        return result;
    }

    this.GetMonth=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=parseInt(this.Data[i].Date%10000/100);
        }

        return result;
    }

    //计算分钟
    this.GetMinutePeriodData=function(period)
    {
        if (DataPlus.GetMinutePeriodData) return DataPlus.GetMinutePeriodData(period,this.Data,this);

        var result = new Array();
        var periodDataCount = 5;
        if (period == 5)
            periodDataCount = 5;
        else if (period == 6)
            periodDataCount = 15;
        else if (period == 7)
            periodDataCount = 30;
        else if (period == 8)
            periodDataCount = 60;
        else if (period==11)
            periodDataCount=120;
        else if (period==12)
            periodDataCount=240;
        else if (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END)
        {
            periodDataCount=period-CUSTOM_MINUTE_PERIOD_START;
            return this.GetMinuteCustomPeriodData(periodDataCount);
        }
        else
            return this.Data;
        var bFirstPeriodData = false;
        var newData = null;
        var preTime=null;   //上一次的计算时间
        for (var i = 0; i < this.Data.length; )
        {
            bFirstPeriodData = true;
            for (var j = 0; j < periodDataCount && i < this.Data.length; ++i)
            {
                if (bFirstPeriodData)
                {
                    newData = new HistoryData();
                    result.push(newData);
                    bFirstPeriodData = false;
                }
                var minData = this.Data[i];
                if (minData == null)
                {
                    ++j;
                    continue;    
                } 
                if (minData.Time==925 && (preTime==null || preTime!=924))  //9:25, 9:30 不连续就不算个数
                {

                }
                else if (minData.Time == 930 && (preTime==null || preTime!=929) )
                {

                }
                else if (minData.Time == 1300 && (preTime==null || preTime!=1259) ) //1点的数据 如果不是连续的 就不算个数
                {

                }
                else
                    ++j;
                newData.Date = minData.Date;
                newData.Time = minData.Time;
                preTime=newData.Time;
                if (minData.Open==null || minData.Close==null)
                    continue;
                if (newData.Open==null || newData.Close==null)
                {
                    newData.Open=minData.Open;
                    newData.High=minData.High;
                    newData.Low=minData.Low;
                    newData.YClose=minData.YClose;
                    newData.Close=minData.Close;
                    newData.Vol=minData.Vol;
                    newData.Amount=minData.Amount;    
                    newData.FlowCapital=minData.FlowCapital;  
                }
                else
                {
                    if (newData.High<minData.High) 
                        newData.High=minData.High;
                    if (newData.Low>minData.Low) 
                        newData.Low=minData.Low;
                    newData.Close=minData.Close;
                    newData.Vol+=minData.Vol;
                    if (minData.Amount!=null) newData.Amount+=minData.Amount;
                    newData.FlowCapital=minData.FlowCapital;  
                }

                if (i+1 < this.Data.length) //判断下一个数据是否是不同日期的
                {
                    var nextItem=this.Data[i+1];
                    if (nextItem && nextItem.Date!=minData.Date)    //不同日期的, 周期结束
                    {
                        ++i;
                        break;
                    }
                }
            }
        }
        return result;
    }

    //自定义分钟
    this.GetMinuteCustomPeriodData=function(count)
    {
        var result = new Array();
        var periodDataCount = count;
        var bFirstPeriodData = false;
        var newData = null;
        for (var i = 0; i < this.Data.length; )
        {
            bFirstPeriodData = true;
            for (var j = 0; j < periodDataCount && i < this.Data.length; ++i, ++j)
            {
                if (bFirstPeriodData)
                {
                    newData = new HistoryData();
                    result.push(newData);
                    bFirstPeriodData = false;
                }
                var minData = this.Data[i];
                if (minData == null) continue;    
                
                newData.Date = minData.Date;
                newData.Time = minData.Time;
                if (minData.Open==null || minData.Close==null) continue;
                if (newData.Open==null || newData.Close==null)
                {
                    newData.Open=minData.Open;
                    newData.High=minData.High;
                    newData.Low=minData.Low;
                    newData.YClose=minData.YClose;
                    newData.Close=minData.Close;
                    newData.Vol=minData.Vol;
                    newData.Amount=minData.Amount;    
                    newData.FlowCapital=minData.FlowCapital;  
                }
                else
                {
                    if (newData.High<minData.High)  newData.High=minData.High;
                    if (newData.Low>minData.Low) newData.Low=minData.Low;
                    newData.Close=minData.Close;
                    newData.Vol+=minData.Vol;
                    if (minData.Amount!=null) newData.Amount+=minData.Amount;   //null数据不相加
                    newData.FlowCapital=minData.FlowCapital;  
                }
            }
        }
        return result;
    }
    //计算周,月,年
    this.GetDayPeriodData=function(period)
    {
        if (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END)
            return this.GetDayCustomPeriodData(period-CUSTOM_DAY_PERIOD_START);
        
        var isBit=MARKET_SUFFIX_NAME.IsBIT(this.Symbol);

        var result=new Array();
        var index=0;
        var startDate=0;
        var weekCount=2;
        var newData=null;
        for(var i in this.Data)
        {
            var isNewData=false;
            var dayData=this.Data[i];

            switch(period)
            {
                case 1: //周线
                    if (isBit)  var fridayDate=ChartData.GetSunday(dayData.Date); //数字货币全年的
                    else var fridayDate=ChartData.GetFirday(dayData.Date);
                    
                    if (fridayDate!=startDate)
                    {
                        isNewData=true;
                        startDate=fridayDate;
                    }
                    break;
                case 21: //双周
                    if (isBit)  var fridayDate=ChartData.GetSunday(dayData.Date); //数字货币全年的
                    else var fridayDate=ChartData.GetFirday(dayData.Date);

                    if (fridayDate!=startDate)
                    {
                        ++weekCount;
                        if (weekCount>=2) 
                        {
                            isNewData=true;
                            weekCount=0;
                        }
                        startDate=fridayDate;
                    }
                    break;
                case 2: //月线
                    if (parseInt(dayData.Date/100)!=parseInt(startDate/100))
                    {
                        isNewData=true;
                        startDate=dayData.Date;
                    }
                    break;
                case 3: //年线
                    if (parseInt(dayData.Date/10000)!=parseInt(startDate/10000))
                    {
                        isNewData=true;
                        startDate=dayData.Date;
                    }
                    break;
                case 9: //季线
                    var now=ChartData.GetQuarter(dayData.Date);
                    now=parseInt(dayData.Date/10000)*10+now;
                    var last=ChartData.GetQuarter(startDate);
                    last=parseInt(startDate/10000)*10+last;
                    if (now!=last)
                    {
                        isNewData=true;
                        startDate=dayData.Date;
                    }
                    break;
            }

            if (isNewData)
            {
                newData=new HistoryData();
                newData.Date=dayData.Date;
                result.push(newData);

                if (dayData.Open==null || dayData.Close==null) continue;

                newData.Open=dayData.Open;
                newData.High=dayData.High;
                newData.Low=dayData.Low;
                newData.YClose=dayData.YClose;
                newData.Close=dayData.Close;
                newData.Vol=dayData.Vol;
                newData.Amount=dayData.Amount;
                newData.FlowCapital=dayData.FlowCapital;
            }
            else
            {
                if (newData==null) continue;
                if (dayData.Open==null || dayData.Close==null) continue;

                if (newData.Open==null || newData.Close==null)
                {
                    newData.Open=dayData.Open;
                    newData.High=dayData.High;
                    newData.Low=dayData.Low;
                    newData.YClose=dayData.YClose;
                    newData.Close=dayData.Close;
                    newData.Vol=dayData.Vol;
                    newData.Amount=dayData.Amount;
                    newData.FlowCapital=dayData.FlowCapital;
                }
                else
                {
                    if (newData.High<dayData.High) newData.High=dayData.High;
                    if (newData.Low>dayData.Low) newData.Low=dayData.Low;

                    newData.Close=dayData.Close;
                    newData.Vol+=dayData.Vol;
                    if (dayData.Amount!=null) newData.Amount+=dayData.Amount;
                    newData.FlowCapital=dayData.FlowCapital;
                    newData.Date=dayData.Date;
                }
            }
        }

        return result;
    }

    this.GetDayCustomPeriodData=function(count)
    {
        var result = [];
        var periodDataCount = count;
        var bFirstPeriodData = false;
        var newData = null;
        for (var i = 0; i < this.Data.length; )
        {
            bFirstPeriodData = true;
            for (var j = 0; j < periodDataCount && i < this.Data.length; ++i, ++j)
            {
                if (bFirstPeriodData)
                {
                    newData = new HistoryData();
                    result.push(newData);
                    bFirstPeriodData = false;
                }
                var dayData = this.Data[i];
                if (dayData == null) continue;    
                
                newData.Date = dayData.Date;
                
                if (dayData.Open==null || dayData.Close==null) continue;
                if (newData.Open==null || newData.Close==null)
                {
                    newData.Open=dayData.Open;
                    newData.High=dayData.High;
                    newData.Low=dayData.Low;
                    newData.YClose=dayData.YClose;
                    newData.Close=dayData.Close;
                    newData.Vol=dayData.Vol;
                    newData.Amount=dayData.Amount;    
                    newData.FlowCapital=dayData.FlowCapital;  
                }
                else
                {
                    if (newData.High<dayData.High)  newData.High=dayData.High;
                    if (newData.Low>dayData.Low) newData.Low=dayData.Low;
                    newData.Close=dayData.Close;
                    newData.Vol+=dayData.Vol;
                    if (dayData.Amount!=null) newData.Amount+=dayData.Amount;
                    newData.FlowCapital=dayData.FlowCapital;  
                }
            }
        }
        return result;
    }

    //周期数据 1=周 2=月 3=年
    this.GetPeriodData=function(period)
    {
        if (MARKET_SUFFIX_NAME.IsBIT(this.Symbol))
        {
            if (period==5 || period==6 || period==7 || period==8 || (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END)) //分钟K线
            {
                var periodDataCount = 5;
                if (period == 5)
                    periodDataCount = 5;
                else if (period == 6)
                    periodDataCount = 15;
                else if (period == 7)
                    periodDataCount = 30;
                else if (period == 8)
                    periodDataCount = 60;
                else if (period==11)
                    periodDataCount = 120;
                else if (period==12)
                    periodDataCount = 240;
                else if (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END)
                    periodDataCount=period-CUSTOM_MINUTE_PERIOD_START;

                return this.GetMinuteCustomPeriodData(periodDataCount);
            }
        }


        if (period==1 || period==2 || period==3 || period==9 || period==21 || (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END)) return this.GetDayPeriodData(period);
        if (period==5 || period==6 || period==7 || period==8 || period==11 || period==12 || (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END)) return this.GetMinutePeriodData(period);
    }

    //复权  0 不复权 1 前复权 2 后复权
    this.GetRightDate=function(right)
    {
        var result=[];
        if (this.Data.length<=0) return result;

        if (right==1)
        {
            var index=this.Data.length-1;
            var seed=1; //复权系数
            var yClose=this.Data[index].YClose;

            result[index]=HistoryData.Copy(this.Data[index]);

            for(--index; index>=0; --index)
            {
                if (yClose!=this.Data[index].Close) break;
                result[index]=HistoryData.Copy(this.Data[index]);
                yClose=this.Data[index].YClose;
            }

            for(; index>=0; --index)
            {
                if(yClose!=this.Data[index].Close)
                    seed *= yClose/this.Data[index].Close;

                result[index]=HistoryData.CopyRight(this.Data[index],seed);

                yClose=this.Data[index].YClose;
            }
        }
        else if (right==2)
        {
            var index=0;
            var seed=1;
            var close=this.Data[index].Close;
            result[index]=HistoryData.Copy(this.Data[index]);

            for(++index;index<this.Data.length;++index)
            {
                if (close!=this.Data[index].YClose) break;
                result[index]=HistoryData.Copy(this.Data[index]);
                close=this.Data[index].Close;
            }

            for(;index<this.Data.length;++index)
            {
                if(close!=this.Data[index].YClose)
                    seed *= close/this.Data[index].YClose;

                result[index]=HistoryData.CopyRight(this.Data[index],seed);

                close=this.Data[index].Close;
            }
        }

        return result;
    }

    this.GetRightData=this.GetRightDate;

    //叠加数据和主数据拟合,去掉主数据没有日期的数据
    this.GetOverlayData=function(overlayData)
    {
        var result=[];

        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;

            if (j>=overlayData.length)
            {
                result[i]=new HistoryData();
                result[i].Date=date;
                ++i;
                continue;;
            }

            var overlayDate=overlayData[j].Date;

            if (overlayDate==date)
            {
                result[i]=new HistoryData();
                result[i].Date=overlayData[j].Date;
                result[i].YClose=overlayData[j].YClose;
                result[i].Open=overlayData[j].Open;
                result[i].High=overlayData[j].High;
                result[i].Low=overlayData[j].Low;
                result[i].Close=overlayData[j].Close;
                result[i].Vol=overlayData[j].Vol;
                result[i].Amount=overlayData[j].Amount;

                //涨跌家数数据
                result[i].Stop=overlayData[j].Stop;
                result[i].Up=overlayData[j].Up;
                result[i].Down=overlayData[j].Down;
                result[i].Unchanged=overlayData[j].Unchanged;

                ++j;
                ++i;
            }
            else if (overlayDate<date)
            {
                ++j;
            }
            else
            {
                result[i]=new HistoryData();
                result[i].Date=date;
                ++i;
            }
        }

        return result;
    }

    this.GetOverlayMinuteData=function(overlayData)
    {
        var result=[];

        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;
            var time=this.Data[i].Time;

            if (j>=overlayData.length)
            {
                result[i]=new HistoryData();
                result[i].Date=date;
                result[i].Time=time;
                ++i;
                continue;;
            }

            var overlayDate=overlayData[j].Date;
            var overlayTime=overlayData[j].Time;

            if (overlayDate==date && overlayTime==time)
            {
                result[i]=new HistoryData();
                result[i].Date=overlayData[j].Date;
                result[i].Time=overlayData[j].Time;
                result[i].YClose=overlayData[j].YClose;
                result[i].Open=overlayData[j].Open;
                result[i].High=overlayData[j].High;
                result[i].Low=overlayData[j].Low;
                result[i].Close=overlayData[j].Close;
                result[i].Vol=overlayData[j].Vol;
                result[i].Amount=overlayData[j].Amount;

                //涨跌家数数据
                result[i].Stop=overlayData[j].Stop;
                result[i].Up=overlayData[j].Up;
                result[i].Down=overlayData[j].Down;
                result[i].Unchanged=overlayData[j].Unchanged;

                ++j;
                ++i;
            }
            else if (overlayDate<date || (overlayDate==date && overlayTime<time))
            {
                ++j;
            }
            else
            {
                result[i]=new HistoryData();
                result[i].Date=date;
                result[i].Time=time;
                ++i;
            }
        }

        return result;
    }


    /*
        技术指标数据方法
    */
    //以主图数据 拟合,返回 SingleData 数组
    this.GetFittingData=function(overlayData)
    {
        var result=new Array();

        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;

            if (j>=overlayData.length)
            {
                result[i]=null;
                ++i;
                continue;;
            }

            var overlayDate=overlayData[j].Date;

            if (overlayDate==date)
            {
                var item=new SingleData();
                item.Date=overlayData[j].Date;
                item.Value=overlayData[j].Value;
                result[i]=item;
                ++j;
                ++i;
            }
            else if (overlayDate<date)
            {
                ++j;
            }
            else
            {
                result[i]=new SingleData();
                result[i].Date=date;
                ++i;
            }
        }

        return result;
    }

    // 缺省数据使用 emptyValue填充
    this.GetFittingData2=function(overlayData,emptyValue)
    {
        var result=new Array();

        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;

            if (j>=overlayData.length)
            {
                result[i]=new SingleData();
                result[i].Date=date;
                result[i].Value=emptyValue;
                ++i;
                continue;;
            }

            var overlayDate=overlayData[j].Date;

            if (overlayDate==date)
            {
                var item=new SingleData();
                item.Date=overlayData[j].Date;
                item.Value=overlayData[j].Value;
                result[i]=item;
                ++j;
                ++i;
            }
            else if (overlayDate<date)
            {
                ++j;
            }
            else
            {
                result[i]=new SingleData();
                result[i].Date=date;
                result[i].Value=emptyValue;
                ++i;
            }
        }

        return result;
    }

    //日期转化 对应数据索引
    this.GetDateIndex=function(data)
    {
        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;

            if (j>=data.length) break;

            var dateItem=data[j];

            if (dateItem.Date==date)
            {
                dateItem.Index=i;
                ++j;
            }
            else if (dateItem.Date<date)
            {
                ++j;
            }
            else
            {
                ++i;
            }
        }
    }

    //日期 时间转化 对应数据索引
    this.GetDateTimeIndex=function(data)
    {
        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;
            var time=this.Data[i].Time;

            if (j>=data.length) break;

            var dateTimeItem=data[j];

            if (dateTimeItem.Date==date && dateTimeItem.Time==time)
            {
                dateTimeItem.Index=i;
                ++j;
            }
            else if (dateTimeItem.Date<date || (dateTimeItem.Date==date && dateTimeItem.Time<time))
            {
                ++j;
            }
            else
            {
                ++i;
            }
        }
    }

    //  分钟数据拟合
    this.GetMinuteFittingData=function(overlayData)
    {
        var result=[];
        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;
            var time=this.Data[i].Time;

            if (j>=overlayData.length)
            {
                result[i]=null;
                ++i;
                continue;;
            }

            var overlayDate=overlayData[j].Date;
            var overlayTime=overlayData[j].Time;
            const overlayItem=overlayData[j];

            if (overlayDate==date && overlayTime==time)
            {
                var item=new SingleData();
                item.Date=overlayItem.Date;
                item.Time=overlayItem.Time;
                item.Value=overlayItem.Value;
                result[i]=item;
                ++j;
                ++i;
            }
            else if (overlayDate<date || (overlayDate==date && overlayTime<time))
            {
                ++j;
            }
            else
            {
                var item=new SingleData();
                item.Date=date;
                item.Time=time;
                result[i]=item;
                ++i;
            }
        }

        return result;
    }


    //把财报数据拟合到主图数据,返回 SingleData 数组
    this.GetFittingFinanceData=function(financeData)
    {
        var result=[];

        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;

            if (j+1<financeData.length)
            {
                if (financeData[j].Date<date && financeData[j+1].Date<=date)
                {
                    ++j;
                    continue;
                }
            }

            var item=new SingleData();
            item.Date=date;
            if (j<financeData.length)
            {
                item.Value=financeData[j].Value;
                item.FinanceDate=financeData[j].Date;   //财务日期 调试用
            }
            else
            {
                item.Value=null;
                item.FinanceDate=null;
            }
            result[i]=item;

            ++i;
        }

        return result;
    }

    //财务数据拟合到分钟数据上 返回 SingleData 数组
    this.GetMinuteFittingFinanceData=function(financeData)
    {
        var result=[];
        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;
            var time=this.Data[i].Time;

            if (j+1<financeData.length)
            {
                if (financeData[j].Date<date && financeData[j+1].Date<=date)
                {
                    ++j;
                    continue;
                }
            }

            var item=new SingleData();
            item.Date=date;
            item.Time=time;
            if (j<financeData.length)
            {
                item.Value=financeData[j].Value;
                item.FinanceDate=financeData[j].Date;   //财务日期 调试用
            }
            else
            {
                item.Value=null;
                item.FinanceDate=null;
            }
            result[i]=item;

            ++i;
        }

        return result;
    }

    //市值计算 financeData.Value 是股数
    this.GetFittingMarketValueData=function(financeData)
    {
        var result=[];

        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;
            var price=this.Data[i].Close;

            if (j+1<financeData.length)
            {
                if (financeData[j].Date<date && financeData[j+1].Date<=date)
                {
                    ++j;
                    continue;
                }
            }

            var item=new SingleData();
            item.Date=date;
            item.Value=financeData[j].Value*price;  //市值计算 收盘价*股数
            item.FinanceDate=financeData[j].Date;   //财务日期 调试用
            result[i]=item;

            ++i;
        }

        return result;
    }

    //月线数据拟合
    this.GetFittingMonthData=function(overlayData)
    {
        var result=new Array();

        var preDate=null;
        for(var i=0,j=0;i<this.Data.length;)
        {
            var date=this.Data[i].Date;

            if (j>=overlayData.length)
            {
                result[i]=null;
                ++i;
                continue;;
            }

            var overlayDate=overlayData[j].Date;

            if (overlayDate==date)
            {
                var item=new SingleData();
                item.Date=overlayData[j].Date;
                item.Value=overlayData[j].Value;
                item.Text=overlayData[j].Text;
                result[i]=item;
                ++j;
                ++i;
            }
            else if (preDate!=null && preDate<overlayDate && date>overlayDate)
            {
                var item=new SingleData();
                item.Date=date;
                item.OverlayDate=overlayData[j].Date;
                item.Value=overlayData[j].Value;
                item.Text=overlayData[j].Text;
                result[i]=item;
                ++j;
                ++i;
            }
            else if (overlayDate<date)
            {
                ++j;
            }
            else
            {
                result[i]=new SingleData();
                result[i].Date=date;
                ++i;
            }

            preDate=date;
        }

        return result;
    }

    this.GetValue=function()
    {
        var result=[];
        for(var i in this.Data)
        {
            if (this.Data[i] && this.Data[i].Value!=null)
            { 
                var item=this.Data[i].Value;
                if (!isNaN(item))
                    result[i]=item;
                else if (item instanceof Array)   //支持数组
                    result[i]=item;
                else
                    result[i]=null;
            }
            else
                result[i]=null;
        }

        return result;
    }

    this.GetObject=function()
    {
        var result=[];
        for(var i in this.Data)
        {
            if (this.Data[i] && this.Data[i].Value)
            { 
                var item=this.Data[i].Value;
                result[i]=item;
            }
            else
                result[i]=null;
        }

        return result;
    }

    this.GetPeriodSingleData=function(period)
    {
        var result=new Array();
        var index=0;
        var startDate=0;
        var newData=null;
        for(var i in this.Data)
        {
            var isNewData=false;
            var dayData=this.Data[i];
            if (dayData==null || dayData.Date==null) continue;

            switch(period)
            {
                case 1: //周线
                    var fridayDate=ChartData.GetFirday(dayData.Date);
                    if (fridayDate!=startDate)
                    {
                        isNewData=true;
                        startDate=fridayDate;
                    }
                    break;
                case 2: //月线
                    if (parseInt(dayData.Date/100)!=parseInt(startDate/100))
                    {
                        isNewData=true;
                        startDate=dayData.Date;
                    }
                    break;
                case 3: //年线
                    if (parseInt(dayData.Date/10000)!=parseInt(startDate/10000))
                    {
                        isNewData=true;
                        startDate=dayData.Date;
                    }
                    break;
                case 9: //季线
                    var now=ChartData.GetQuarter(dayData.Date);
                    now=parseInt(dayData.Date/10000)*10+now;
                    var last=ChartData.GetQuarter(startDate);
                    last=parseInt(startDate/10000)*10+last;
                    if (now!=last)
                    {
                        isNewData=true;
                        startDate=dayData.Date;
                    }
                    break;
            }

            if (isNewData)
            {
                newData=new SingleData();
                newData.Date=dayData.Date;
                newData.Value=dayData.Value;
                result.push(newData);
            }
            else
            {
                if (newData==null) continue;
                if (dayData.Value==null || isNaN(dayData.Value)) continue;
                if (newData.Value==null || isNaN(newData.Value)) newData.Value=dayData.Value;
            }
        }

        return result;
    }

    /*
        分钟数据方法
        this.GetClose()     每分钟价格
        this.GetVol()       每分钟成交量
    */

    //分钟均线
    this.GetMinuteAvPrice=function()
    {
        var result=new Array();
        for(var i in this.Data)
        {
            result[i]=this.Data[i].AvPrice;
        }

        return result;
    }

    this.MergeMinuteData=function(data) //合并数据
    {
        var sourceFirstItem=this.Data[0];
        var firstItemID=0;
        var firstItem=null;
        for(var i=0;i<data.length;++i)  //查找比原始数据起始位置大的数据位置
        {
            var item=data[i];
            if (item.Date>=sourceFirstItem.Date && item.Time>=sourceFirstItem.Time)
            {
                firstItemID=i;
                firstItem=item;
                break;
            }
        }

        if (firstItem==null) return false;

        var index=null;
        var bFind=false;    //第1个数据是否完全匹配
        for(var i=this.Data.length-1; i>=0;--i)
        {
            var date=this.Data[i].Date;
            var time=this.Data[i].Time;

            if (firstItem.Date>=date && firstItem.Time>=time)
            {
                index=i;
                if (firstItem.Date==date && firstItem.Time==time) bFind=true;
                break;
            }
        }

        if (index==null) return false;

        var j=index;        //原始数据插入位置
        var i=firstItemID;  //合并数据起始位置
        if (bFind==true)    //第1个数据匹配,覆盖
        {
            var item=data[i];
            if (j-1>0 && !item.YClose) item.YClose=this.Data[j-1].Close;   //前收盘如果没有就是上一记录的收盘
            var newItem=HistoryData.Copy(item);
            this.Data[j]=newItem;
            ++j;
            ++i;
        }
        else            //从下一个数据开始插入
        {
            ++j;
        }

        for(;i<data.length; )
        {
            var item=data[i];
            if (j>=this.Data.length-1)
            {
                if (j-1>0 && !item.YClose) item.YClose=this.Data[j-1].YClose;   //前收盘如果没有就是上一记录的收盘
                var newItem=HistoryData.Copy(item);
                this.Data[j]=newItem;
                ++j;
                ++i;
            }
            else
            {
                var oldItem=this.Data[j];
                if (oldItem.Date==item.Date && oldItem.Time==item.Time) //更新数据
                {
                    HistoryData.CopyTo(oldItem,item);
                    ++j;
                    ++i;
                }
                else
                {
                    ++j;
                }
            }
        }

        //JSConsole.Chart.Log('[ChartData::MergeMinuteData] ', this.Data, data);

        return true;
    }

    this.CoverTo=function(data, curPeriod, changePeriod) //把数据用data, 日期时间不变, curPeriod=当前周期  changePeriod=需要转换周期
    {
        var result=[];
        var tempItem=null;
        var isMinute=ChartData.IsMinutePeriod(curPeriod,true);
        var isMinute2=ChartData.IsMinutePeriod(changePeriod,true);
        var isMimToMin=isMinute && isMinute2;
        var isMinToDay=isMinute && !isMinute2;
        var isDayToDay=!isMinute && !isMinute2;

        for(var i=0,j=0; i<this.Data.length; ++i)
        {
            var item=this.Data[i];  //原始数据
            for(;j<data.length;++j)
            {
                var periodItem=data[j];

                if (isMimToMin)  //都是分钟数据
                {
                    if (periodItem.Date==item.Date && periodItem.Time>=item.Time)
                    {
                        tempItem=periodItem;
                        break;
                    }
                }
                else if (isMinToDay || isDayToDay)    //分钟 => 日线, 日线 => 日线
                {
                    if (periodItem.Date>=item.Date)
                    {
                        tempItem=periodItem;
                        break;
                    }
                }
            }
            
            var newItem=null;
            if (tempItem) 
            {
                newItem=HistoryData.Copy(tempItem);
                newItem.PDate=tempItem.Date;
                newItem.PTime=tempItem.Time;
            }
            else newItem=new HistoryData();
            
            newItem.Date=item.Date;
            if (isMimToMin && isMinToDay) newItem.Time=item.Time;
            result.push(newItem);
        }

        JSConsole.Chart.Log('[ChartData::CoverTo] result', result);
        return result;
    }

    this.GetRef=function(n)
    {
        let result=[];

        for(var i=0 ;i<this.Data.length; ++i )
        {
            result[i]=null;
            var itemDate=this.Data[i];  //原始数据

            if (i-n<0) 
            {
                var newData=new HistoryData();
                newData.Date=itemDate.Date;
                newData.Time=itemDate.Time;
                result[i]=newData;
                continue;
            }

            var itemData=this.Data[i-n];
            var newData=HistoryData.Copy(itemData);
            newData.Date=itemDate.Date;
            newData.Time=itemDate.Time;

            result[i]=newData;
        }

        return result;
    }
}

ChartData.GetFirday=function(value)
{
    var date=new Date(parseInt(value/10000),(value/100%100-1),value%100);
    var day=date.getDay();
    if (day==5) return value;

    var timestamp=date.getTime();
    if (day<5)
    {
        var prevTimestamp=(24*60*60*1000)*(5-day);
        timestamp+=prevTimestamp;
    }
    else
    {
        var prevTimestamp=(24*60*60*1000)*(day-5);
        timestamp-=prevTimestamp;
    }

    date.setTime(timestamp);
    var fridayDate= date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate();
    var week=date.getDay();
    return fridayDate;

}

ChartData.GetSunday=function(value)
{
    var date=new Date(parseInt(value/10000),(value/100%100-1),value%100);
    var day=date.getDay();
    if (day==0) return value;

    var timestamp=date.getTime();
    if (day>0)
    {
        var prevTimestamp=(24*60*60*1000)*(7-day);
        timestamp+=prevTimestamp;
    }

    date.setTime(timestamp);
    var sundayDate= date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate();
    var week=date.getDay();
    return sundayDate;
}

ChartData.GetQuarter=function(value)
{
    var month=parseInt(value%10000/100);
    if (month==1 || month==2 || month==3) return 1;
    else if (month==4 || month==5 || month==6) return 2;
    else if (month==7 || month==8 || month==9) return 3;
    else if (month==10 || month==11 || month==12) return 4;
    else return 0;
}

//是否是日线周期  0=日线 1=周线 2=月线 3=年线 9=季线 21=双周 [40001-50000) 自定义日线 (isIncludeBase 是否包含基础日线周期)
var CUSTOM_DAY_PERIOD_START=40000, CUSTOM_DAY_PERIOD_END=49999;
ChartData.IsDayPeriod=function(period, isIncludeBase)
{
    if (period==1 || period==2 || period==3 || period==9 || period==21) return true;
    if (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_END) return true;
    if (period==0 && isIncludeBase==true) return true;

    return false;
}

//是否是分钟周期 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 11=120分钟 12=240分钟 [20001-30000) 自定义分钟 (isIncludeBase 是否包含基础1分钟周期)
var CUSTOM_MINUTE_PERIOD_START=20000, CUSTOM_MINUTE_PERIOD_END=29999;
ChartData.IsMinutePeriod=function(period,isIncludeBase)
{
    if (period==5 || period==6 || period==7 || period==8 ||period==11 || period==12) return true;
    if (period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END) return true;
    if (period==4 && isIncludeBase==true) return true;

    return false;
}

//是否是秒周期 [30001-32000)
var CUSTOM_SECOND_PERIOD_START=30000, CUSTOM_SECOND_PERIOD_END=32000;
ChartData.IsSecondPeriod=function(period)
{
    if (period>CUSTOM_SECOND_PERIOD_START && period<=CUSTOM_SECOND_PERIOD_END) return true;
    return false;
}


//是否是分笔图 10=分笔
ChartData.IsTickPeriod=function(period)
{
    return period==10;
}

//获取周期名字
ChartData.GetPeriodName=function(period)
{
    var mapName=new Map(
    [
        [0, '日线'],[1, '周线'],[2, '月线'],[3, '年线'],[9, '季线'], [21,'双周'],
        [4, '1分'], [5, '5分'], [6, '15分'],[7, '30分'],[8, '60分'],[11, '2小时'],[12, '4小时'],
        [10, '分笔']
    ]);

    if (mapName.has(period)) return mapName.get(period);

    return '';
}


function TooltipData()              //提示信息
{
    this.ChartPaint;
    this.Data;
    this.Type=0;
}

function Rect(x,y,width,height)
{
    this.X=x,
    this.Y=y;
    this.Width=width;
    this.Height=height;
}

//图新画法接口类
function IChartPainting()
{
    this.Canvas;                        //画布
    this.ChartBorder;                   //边框信息
    this.ChartFrame;                    //框架画法
    this.Name;                          //名称
    this.ClassName='IChartPainting';    //类名
    this.Data=new ChartData();          //数据区

    this.NotSupportMessage=null;
    this.MessageFont=g_JSChartResource.Index.NotSupport.Font;
    this.MessageColor=g_JSChartResource.Index.NotSupport.TextColor;
    this.IsDrawFirst=false;
    this.IsShow=true;

    this.Draw=function()
    {

    }

    this.DrawNotSupportmessage=function()
    {
        this.Canvas.font=this.MessageFont;
        this.Canvas.fillStyle=this.MessageColor;

        var left=this.ChartBorder.GetLeft();
        var width=this.ChartBorder.GetWidth();
        var top=this.ChartBorder.GetTopEx();
        var height=this.ChartBorder.GetHeightEx();

        var x=left+width/2;
        var y=top+height/2;

        this.Canvas.textAlign="center";
        this.Canvas.textBaseline="middle";
        this.Canvas.fillText(this.NotSupportMessage,x,y);
    }

    this.GetTooltipData=function(x,y,tooltip)
    {
        return false;
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;

        if(!this.Data || !this.Data.Data) return range;

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null || isNaN(value)) continue;

            if (range.Max==null) range.Max=value;
            if (range.Min==null) range.Min=value;

            if (range.Max<value) range.Max=value;
            if (range.Min>value) range.Min=value;
        }

        return range;
    }

    this.GetDynamicFont=function(dataWidth) //根据宽度自动获取对应字体
    {
        var pixelTatio = GetDevicePixelRatio();
        var font;   
        if (dataWidth < 5) font =4*pixelTatio + 'px Arial';           //字体根据数据宽度动态调整
        else if (dataWidth < 7) font = 6*pixelTatio +'px Arial';
        else if (dataWidth < 9) font = 8*pixelTatio +'px Arial';
        else if (dataWidth < 11) font =10*pixelTatio +'px Arial';
        else if (dataWidth < 13) font =12*pixelTatio +'px Arial';
        else if (dataWidth < 15) font =14*pixelTatio + 'px Arial';
        else font =16*pixelTatio + 'px Arial';
        
        return font;
    }

    this.GetLockRect=function()
    {
        return this.ChartFrame.GetLockRect();
    }
}


//缩放因子
/*
var ZOOM_SEED=
[
    [49,10],	[46,9],		[43,8],
    [41,7.5],	[39,7],		[37,6],
    [31,5.5],	[27,5],		[23,4.5],
    [21,4],		[18,3.5],	[16,3],
    [13,2.5],	[11,2],		[8,1.5],
    [6,1],		[3,0.6],	[2.2,0.5],
    //太多了卡,
    //[1.1,0.3],
    //[0.9,0.2],	[0.7,0.15],
    //[0.6,0.12],	[0.5,0.1],	[0.4,0.08],
    //[0.3,0.06],	[0.2,0.04],	[0.1,0.02]
];
*/


var ZOOM_SEED=  //0=柱子宽度  1=间距
[
    [48,10],	[44,10], 
    [40,9],     [36,9],	
    [32,8],     [28,8],	
    [24,7],     [20,7], 
    [18,6],     [16,6],
    [14,5],     [12,5],
    [8,4], [6,4], [4,4], 

    [3,3],
    [3,1], [2,1], [1,1], [1,0],
];


//K线画法 支持横屏
function ChartKLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartKLine';    //类名
    this.Symbol;        //股票代码
    this.DrawType=0;    // 0=实心K线柱子  1=收盘价线 2=美国线 3=空心K线柱子 4=收盘价面积图
    this.CloseLineColor=g_JSChartResource.CloseLineColor;
    this.CloseLineAreaColor=g_JSChartResource.CloseLineAreaColor;
    this.UpColor=g_JSChartResource.UpBarColor;
    this.DownColor=g_JSChartResource.DownBarColor;
    this.UnchagneColor=g_JSChartResource.UnchagneBarColor;          //平盘
    this.ColorData;             //五彩K线颜色 >0:g_JSChartResource.UpBarColor 其他:g_JSChartResource.DownBarColor
    this.TradeData;             //交易系统 包含买卖数据{Buy:, Sell:}
    this.TradeIcon=g_JSChartResource.KLine.TradeIcon;
    this.TooltipRect=new Array();           //2位数组 0 数据序号 1 区域
    this.InfoTooltipRect=[];                //2维数组 0 数据,  1 区域

    this.IsShowMaxMinPrice=true;                 //是否显示最大最小值
    this.IsShowKTooltip=true;                    //是否显示K线tooltip
    this.TextFont=g_JSChartResource.KLine.MaxMin.Font;
    this.TextColor=g_JSChartResource.KLine.MaxMin.Color;

    this.InfoData;      //信息地雷 key=日期  value=信息数据
    this.InfoPosition=0;    // 0=K线上 1 底部

    this.PtMax;     //最大值的位置
    this.PtMin;     //最小值的位置

    this.TickSymbol='╳';    //分笔显示的图标
    this.TickFontName='arial';
    this.Period;            //周期

    this.DrawAKLine=function()  //美国线
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var upColor=this.UpColor;
        var downColor=this.DownColor;
        var unchagneColor=this.UnchagneColor; 

        var ptMax={X:null,Y:null,Value:null,Align:'left'};
        var ptMin={X:null,Y:null,Value:null,Align:'left'};
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yLow=this.ChartFrame.GetYFromData(data.Low);
            var yHigh=this.ChartFrame.GetYFromData(data.High);
            var yOpen=this.ChartFrame.GetYFromData(data.Open);
            var yClose=this.ChartFrame.GetYFromData(data.Close);

            if (ptMax.Value==null || ptMax.Value<data.High)     //求最大值
            {
                ptMax.X=x;
                ptMax.Y=yHigh;
                ptMax.Value=data.High;
                ptMax.Align=j<xPointCount/2?'left':'right';
            }

            if (ptMin.Value==null || ptMin.Value>data.Low)      //求最小值
            {
                ptMin.X=x;
                ptMin.Y=yLow;
                ptMin.Value=data.Low;
                ptMin.Align=j<xPointCount/2?'left':'right';
            }

            if (data.Open<data.Close) this.Canvas.strokeStyle=this.UpColor; //阳线
            else if (data.Open>data.Close) this.Canvas.strokeStyle=this.DownColor; //阳线
            else this.Canvas.strokeStyle=this.UnchagneColor; //平线

            if (this.ColorData) ///五彩K线颜色设置
            {
                if (i<this.ColorData.length)
                    upColor=downColor=unchagneColor=(this.ColorData[i]>0?this.UpColor:this.DownColor);
                else
                    upColor=downColor=unchagneColor=this.DownColor;
            }

            this.Canvas.beginPath();   //最高-最低
            if (isHScreen)
            {
                this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                this.Canvas.lineTo(yLow,ToFixedPoint(x));
            }
            else
            {
                this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                this.Canvas.lineTo(ToFixedPoint(x),yLow);
            }
            
            this.Canvas.stroke();

            if (dataWidth>=4)
            {
                this.Canvas.beginPath();    //开盘
                if (isHScreen)
                {
                    this.Canvas.moveTo(ToFixedPoint(yOpen),left);
                    this.Canvas.lineTo(ToFixedPoint(yOpen),x);
                }
                else
                {
                    this.Canvas.moveTo(left,ToFixedPoint(yOpen));
                    this.Canvas.lineTo(x,ToFixedPoint(yOpen));
                }
                this.Canvas.stroke();

                this.Canvas.beginPath();    //收盘
                if (isHScreen)
                {
                    this.Canvas.moveTo(ToFixedPoint(yClose),right);
                    this.Canvas.lineTo(ToFixedPoint(yClose),x);
                }
                else
                {
                    this.Canvas.moveTo(right,ToFixedPoint(yClose));
                    this.Canvas.lineTo(x,ToFixedPoint(yClose));
                }
                this.Canvas.stroke();
            }

            if(this.Data.DataType==0)
            {
                var infoItem={Xleft:left,XRight:right, YMax:yHigh, XCenter:x, YMin:yLow, DayData:data, Index:j};
                this.DrawInfo(infoItem);
            }
        }

        this.PtMax=ptMax;
        this.PtMin=ptMin;
    }

    this.DrawCloseArea=function()   //收盘价面积图
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var bFirstPoint=true;
        var firstPoint=null;
        this.Canvas.beginPath();
        this.Canvas.strokeStyle=this.CloseLineColor;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yClose=this.ChartFrame.GetYFromData(data.Close);

            if (bFirstPoint)
            {
                if (isHScreen) 
                {
                    this.Canvas.moveTo(yClose,x);
                    firstPoint={ X:yClose, Y:x };
                }
                else 
                {
                    this.Canvas.moveTo(x,yClose);
                    firstPoint={ X:x, Y:yClose };
                }
                bFirstPoint=false;
            }
            else
            {
                if (isHScreen) this.Canvas.lineTo(yClose,x);
                else this.Canvas.lineTo(x,yClose);
            }
        }

        if (bFirstPoint) return;

        this.Canvas.stroke();
        if (isHScreen)
        {
            this.Canvas.lineTo(this.ChartBorder.GetLeft(),x);
            this.Canvas.lineTo(this.ChartBorder.GetLeft(),firstPoint.Y);
        }
        else
        {
            this.Canvas.lineTo(x,this.ChartBorder.GetBottom());
            this.Canvas.lineTo(firstPoint.X,this.ChartBorder.GetBottom());
        }
        this.Canvas.closePath();
        if (Array.isArray(this.CloseLineAreaColor))
        {
            if (isHScreen)
            {
                let gradient = this.Canvas.createLinearGradient(this.ChartBorder.GetRightEx(),this.ChartBorder.GetTop(), this.ChartBorder.GetLeft(),this.ChartBorder.GetTop());
                gradient.addColorStop(0, this.CloseLineAreaColor[0]);
                gradient.addColorStop(1, this.CloseLineAreaColor[1]);
                this.Canvas.fillStyle=gradient;
            }
            else
            {
                let gradient = this.Canvas.createLinearGradient(firstPoint.X,this.ChartBorder.GetTopEx(), firstPoint.X,this.ChartBorder.GetBottom());
                gradient.addColorStop(0, this.CloseLineAreaColor[0]);
                gradient.addColorStop(1, this.CloseLineAreaColor[1]);
                this.Canvas.fillStyle=gradient;
            }
        }
        else
        {
            this.Canvas.fillStyle=this.CloseLineAreaColor;
        }
        this.Canvas.fill();
    }

    this.DrawCloseLine=function()   //收盘价线
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var bFirstPoint=true;
        this.Canvas.beginPath();
        this.Canvas.strokeStyle=this.CloseLineColor;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yClose=this.ChartFrame.GetYFromData(data.Close);

            if (bFirstPoint)
            {
                if (isHScreen) this.Canvas.moveTo(yClose,x);
                else this.Canvas.moveTo(x,yClose);
                bFirstPoint=false;
            }
            else
            {
                if (isHScreen) this.Canvas.lineTo(yClose,x);
                else this.Canvas.lineTo(x,yClose);
            }
        }

        if (bFirstPoint==false) this.Canvas.stroke();
    }

    this.DrawKBar=function()        //蜡烛头
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;

        if (isHScreen) 
        {
            xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
            chartright=this.ChartBorder.GetBottom();
        }

        var ptMax={X:null,Y:null,Value:null,Align:'left'};
        var ptMin={X:null,Y:null,Value:null,Align:'left'};
        
        var upColor=this.UpColor;
        var downColor=this.DownColor;
        var unchagneColor=this.UnchagneColor; 

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yLow=this.ChartFrame.GetYFromData(data.Low);
            var yHigh=this.ChartFrame.GetYFromData(data.High);
            var yOpen=this.ChartFrame.GetYFromData(data.Open);
            var yClose=this.ChartFrame.GetYFromData(data.Close);
            var y=yHigh;

            if (ptMax.Value==null || ptMax.Value<data.High)     //求最大值
            {
                ptMax.X=x;
                ptMax.Y=yHigh;
                ptMax.Value=data.High;
                ptMax.Align=j<xPointCount/2?'left':'right';
            }

            if (ptMin.Value==null || ptMin.Value>data.Low)      //求最小值
            {
                ptMin.X=x;
                ptMin.Y=yLow;
                ptMin.Value=data.Low;
                ptMin.Align=j<xPointCount/2?'left':'right';
            }

            if (this.ColorData) ///五彩K线颜色设置
            {
                if (i<this.ColorData.length)
                    upColor=downColor=unchagneColor=(this.ColorData[i]>0?this.UpColor:this.DownColor);
                else
                    upColor=downColor=unchagneColor=this.DownColor;
            }

            if (data.Open<data.Close)       //阳线
            {
                if (dataWidth>=4)
                {
                    this.Canvas.strokeStyle=upColor;
                    if (data.High>data.Close)   //上影线
                    {
                        this.Canvas.beginPath();
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):yClose),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):yClose));
                        }
                        this.Canvas.stroke();
                        y=yClose;
                    }
                    else
                    {
                        y=yClose;
                    }

                    this.Canvas.fillStyle=upColor;
                    if (isHScreen)
                    {
                        if (Math.abs(yOpen-y)<1)  
                        {
                            this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth));    //高度小于1,统一使用高度1
                        }
                        else 
                        {
                            if (this.DrawType==3) //空心柱
                            {
                                this.Canvas.beginPath();
                                this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth));
                                this.Canvas.stroke();
                            }
                            else
                            {
                                this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth));
                            }
                        }
                    }
                    else
                    {
                        if (Math.abs(yOpen-y)<1)  
                        {
                            this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1);    //高度小于1,统一使用高度1
                        }
                        else 
                        {
                            if (this.DrawType==3) //空心柱
                            {
                                this.Canvas.beginPath();
                                this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y));
                                this.Canvas.stroke();
                            }
                            else
                            {
                                this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yOpen)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yOpen-y)));
                            }
                        }
                    }

                    if (data.Open>data.Low) //下影线
                    {
                        this.Canvas.beginPath();
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow));
                        }
                        this.Canvas.stroke();
                    }
                }
                else
                {
                    this.Canvas.beginPath();
                    if (isHScreen)
                    {
                        this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                        this.Canvas.lineTo(yLow,ToFixedPoint(x));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                        this.Canvas.lineTo(ToFixedPoint(x),yLow);
                    }
                    this.Canvas.strokeStyle=upColor;
                    this.Canvas.stroke();
                }
            }
            else if (data.Open>data.Close)  //阴线
            {
                if (dataWidth>=4)
                {
                    this.Canvas.strokeStyle=downColor;
                    if (data.High>data.Close)   //上影线
                    {
                        this.Canvas.beginPath();
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yOpen),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yOpen));
                        }
                        this.Canvas.stroke();
                        y=yOpen;
                    }
                    else
                    {
                        y=yOpen
                    }

                    this.Canvas.fillStyle=downColor;
                    if (isHScreen)
                    {
                        if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth));    //高度小于1,统一使用高度1
                        else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yClose-y),ToFixedRect(dataWidth));
                    }
                    else
                    {
                        if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1);    //高度小于1,统一使用高度1
                        else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,yClose)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(yClose-y)));
                    }

                    if (data.Open>data.Low) //下影线
                    {
                        this.Canvas.beginPath();
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow));
                        }
                        this.Canvas.stroke();
                    }
                }
                else
                {
                    this.Canvas.beginPath();
                    if (isHScreen)
                    {
                        this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                        this.Canvas.lineTo(yLow,ToFixedPoint(x));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                        this.Canvas.lineTo(ToFixedPoint(x),yLow);
                    }
                    this.Canvas.strokeStyle=downColor;
                    this.Canvas.stroke();
                }
            }
            else // 平线
            {
                if (dataWidth>=4)
                {
                    this.Canvas.strokeStyle=unchagneColor;
                    this.Canvas.beginPath();
                    if (data.High>data.Close)   //上影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(y,ToFixedPoint(x));
                            this.Canvas.lineTo(yOpen,ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),y);
                            this.Canvas.lineTo(ToFixedPoint(x),yOpen);
                        }
                        y=yOpen;
                    }
                    else
                    {
                        y=yOpen;
                    }

                    if (isHScreen)
                    {
                        this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(left));
                        this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(right));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(left),ToFixedPoint(y));
                        this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(y));
                    }

                    if (data.Open>data.Low) //下影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow));
                        }
                    }

                    this.Canvas.stroke();
                }
                else
                {
                    this.Canvas.beginPath();
                    if (isHScreen)
                    {
                        this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                        this.Canvas.lineTo(yLow,ToFixedPoint(x));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                        this.Canvas.lineTo(ToFixedPoint(x),yLow);
                    }
                    this.Canvas.strokeStyle=unchagneColor;
                    this.Canvas.stroke();
                }
            }
            
            if (this.IsShowKTooltip && !isHScreen)    //添加tooltip区域
            {
                var yTop=Math.min(yOpen,yClose);
                var yBottom=Math.max(yOpen,yClose);
                if (Math.abs(yOpen-yClose)<5)   //高度太小了, 上下各+5px
                {
                    yTop=Math.min(yHigh,yTop-5);
                    yBottom=Math.max(yLow,yBottom+5);
                }
                var rect=new Rect(left,yTop,dataWidth,yBottom-yTop);
                //this.Canvas.fillStyle="rgb(0,0,100)";
                //this.Canvas.fillRect(rect.X,rect.Y,rect.Width,rect.Height);
                this.TooltipRect.push([i,rect]);    //[0]数据索引 [1]数据区域
            }

            if(this.Data.DataType==0)
            {
                var infoItem={Xleft:left,XRight:right, XCenter:x, YMax:yHigh, YMin:yLow, DayData:data, Index:j};
                this.DrawInfo(infoItem);
            }
        }

        this.PtMax=ptMax;
        this.PtMin=ptMin;
    }

    this.DrawTrade=function()       //交易系统
    {
        if (!this.TradeData) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;

        if (isHScreen) 
        {
            xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
            chartright=this.ChartBorder.GetBottom();
        }

        var sellData=this.TradeData.Sell;
        var buyData=this.TradeData.Buy;
        var arrowWidth=dataWidth;
        if (arrowWidth>10) arrowWidth=10;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var buy=false,sell=false;
            if (sellData && i<sellData.length) sell=sellData[i]>0;
            if (buyData && i<buyData.length) buy=buyData[i]>0;
            if (!sell && !buy) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yLow=this.ChartFrame.GetYFromData(data.Low);
            var yHigh=this.ChartFrame.GetYFromData(data.High);
            var yOpen=this.ChartFrame.GetYFromData(data.Open);
            var yClose=this.ChartFrame.GetYFromData(data.Close);
            var y=yHigh;

            if (buy)
            {
                this.Canvas.fillStyle=this.UpColor;
                this.Canvas.strokeStyle=this.UnchagneColor;
                this.Canvas.beginPath();
                if (isHScreen)
                {
                    this.Canvas.moveTo(yLow-1,x);
                    this.Canvas.lineTo(yLow-arrowWidth-1,x-arrowWidth/2);
                    this.Canvas.lineTo(yLow-arrowWidth-1,x+arrowWidth/2);
                }
                else
                {
                    this.Canvas.moveTo(x,yLow+1);
                    this.Canvas.lineTo(x-arrowWidth/2,yLow+arrowWidth+1);
                    this.Canvas.lineTo(x+arrowWidth/2,yLow+arrowWidth+1);
                }
                this.Canvas.closePath();
                this.Canvas.fill();
                this.Canvas.stroke();
            }

            if (sell)
            {
                this.Canvas.fillStyle=this.DownColor;
                this.Canvas.strokeStyle=this.UnchagneColor;
                this.Canvas.beginPath();
                if (isHScreen)
                {
                    this.Canvas.moveTo(yHigh+1,x);
                    this.Canvas.lineTo(yHigh+arrowWidth+1,x-arrowWidth/2);
                    this.Canvas.lineTo(yHigh+arrowWidth+1,x+arrowWidth/2);
                }
                else
                {
                    this.Canvas.moveTo(x,yHigh-1);
                    this.Canvas.lineTo(x-arrowWidth/2,yHigh-arrowWidth-1);
                    this.Canvas.lineTo(x+arrowWidth/2,yHigh-arrowWidth-1);
                }
                this.Canvas.closePath();
                this.Canvas.fill();
                this.Canvas.stroke();
            }
        }
    }

    this.DrawTick=function()        //分笔图
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var fontSize=parseInt(dataWidth);
        if (fontSize<=1) fontSize=2;
        else if (fontSize>=18) fontSize=18;
        var bFirstPoint=true;
        this.Canvas.beginPath();
        this.Canvas.font=fontSize*GetDevicePixelRatio()+'px '+this.TickFontName;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yClose=this.ChartFrame.GetYFromData(data.Close);

            if (data.Flag===0) this.Canvas.fillStyle=this.UpColor;
            else if (data.Flag==1) this.Canvas.fillStyle=this.DownColor;
            else this.Canvas.fillStyle=this.UnchagneColor;

            this.Canvas.textAlign='center'
            this.Canvas.textBaseline='middle';
            if (isHScreen)  this.Canvas.fillText(this.TickSymbol,yClose,x);
            else this.Canvas.fillText(this.TickSymbol,x,yClose);
        }
    }

    this.Draw=function()
    {
        this.TooltipRect=[];
        this.InfoTooltipRect=[];
        this.PtMax={X:null,Y:null,Value:null,Align:'left'}; //清空最大
        this.PtMin={X:null,Y:null,Value:null,Align:'left'}; //清空最小

        this.ChartFrame.ChartKLine={Max:null, Min:null };   //保存K线上 显示最大最小值坐标

        if (!this.IsShow) return;

        if (ChartData.IsTickPeriod(this.Period))    //分笔图
        {
            this.DrawTick();
            return;
        }

        if (this.DrawType==1) 
        {
            this.DrawCloseLine();
            return;
        }
        else if (this.DrawType==2)
        {
            this.DrawAKLine();
        }
        else if (this.DrawType==4)
        {
            this.DrawCloseArea();
        }
        else
        {
            this.DrawKBar();
        }

        if (this.TradeIcon) this.DrawTradeIcon()
        else this.DrawTrade();

        if (this.IsShowMaxMinPrice)     //标注最大值最小值
        {
            if (this.ChartFrame.IsHScreen===true) this.HScreenDrawMaxMinPrice(this.PtMax,this.PtMin);
            else this.DrawMaxMinPrice(this.PtMax,this.PtMin);
        }
    }

    this.DrawMaxMinPrice=function(ptMax,ptMin)
    {
        if (ptMax.X==null || ptMax.Y==null || ptMax.Value==null) return;
        if (ptMin.X==null || ptMin.Y==null || ptMin.Value==null) return;

        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        this.Canvas.font=this.TextFont;
        this.Canvas.fillStyle=this.TextColor;

        var ptTop=ptMax;
        if (ptMax.Y>ptMin.Y) ptTop=ptMin;
        this.Canvas.textAlign=ptTop.Align;
        this.Canvas.textBaseline='bottom';
        var left=ptTop.Align=='left'?ptTop.X:ptTop.X;
        var text=ptTop.Value.toFixed(defaultfloatPrecision);
        if (ptTop.Align=='left') text='←'+text;
        else text=text+'→';
        this.Canvas.fillText(text,left,ptTop.Y);
        this.ChartFrame.ChartKLine.Max={X:left, Y:ptTop.Y, Text: { BaseLine:'bottom'}};

        var ptBottom=ptMin;
        if (ptMin.Y<ptMax.Y) ptBottom=ptMax;
        this.Canvas.textAlign=ptBottom.Align;
        this.Canvas.textBaseline='top';
        var left=ptBottom.Align=='left'?ptBottom.X:ptBottom.X;
        var text=ptMin.Value.toFixed(defaultfloatPrecision);
        if (ptBottom.Align=='left') text='←'+text;
        else  text=text+'→';
        this.Canvas.fillText(text,left,ptBottom.Y);
        this.ChartFrame.ChartKLine.Min={X:left, Y:ptBottom.Y, Text:{ BaseLine:'top'}};
    }

    this.HScreenDrawMaxMinPrice=function(ptMax,ptMin)   //横屏模式下显示最大最小值
    {
        if (ptMax.X==null || ptMax.Y==null || ptMax.Value==null) return;
        if (ptMin.X==null || ptMin.Y==null || ptMin.Value==null) return;

        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        var xText=ptMax.Y;
        var yText=ptMax.X;
        this.Canvas.save(); 
        this.Canvas.translate(xText, yText);
        this.Canvas.rotate(90 * Math.PI / 180);

        this.Canvas.font=this.TextFont;
        this.Canvas.fillStyle=this.TextColor;
        this.Canvas.textAlign=ptMax.Align;
        this.Canvas.textBaseline='bottom';
        var text=ptMax.Value.toFixed(defaultfloatPrecision);
        if (ptMax.Align=='left') text='←'+text;
        else  text=text+'→';
        this.Canvas.fillText(text,0,0);
        this.Canvas.restore();

        
        var xText=ptMin.Y;
        var yText=ptMin.X;
        this.Canvas.save(); 
        this.Canvas.translate(xText, yText);
        this.Canvas.rotate(90 * Math.PI / 180);

        this.Canvas.font=this.TextFont;
        this.Canvas.fillStyle=this.TextColor;
        this.Canvas.textAlign=ptMin.Align;
        this.Canvas.textBaseline='top';
        var text=ptMin.Value.toFixed(defaultfloatPrecision);
        if (ptMin.Align=='left') text='←'+text;
        else text=text+'→';
        this.Canvas.fillText(text,0,0);
        this.Canvas.restore();
    }

    //画某一天的信息地雷
    this.DrawInfo=function(item)
    {
        if (!this.InfoData || this.InfoData.length<=0) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var bottom=this.ChartBorder.GetBottom();

        var infoData=this.InfoData.get(item.DayData.Date.toString());
        if (!infoData || infoData.Data.length<=0) return;

        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        var iconSize=dataWidth+distanceWidth;
        var minIconSize=18*pixelTatio;
        var bShowNum=true;
        if (iconSize<=15) bShowNum=false;
        if (iconSize<minIconSize) iconSize=minIconSize;
        
        var text='', title='';
        var mapImage=new Map();
        var iconTop=item.YMax+1*pixelTatio;
        var yOffset=0;
        for(var i in infoData.Data)
        {
            var infoItem=infoData.Data[i];
            var imageInfo=mapImage.get(infoItem.InfoType);
            if (!imageInfo)
            {
                var icon=JSKLineInfoMap.GetIconFont(infoItem.InfoType);
                this.Canvas.fillStyle=icon.Color;
                this.Canvas.font=iconSize+'px '+icon.Family;

                if (isHScreen)
                {
                    this.Canvas.textBaseline="middle";
                    this.Canvas.textAlign="left";
                    this.Canvas.fillText(icon.HScreenText,iconTop,item.XCenter,iconSize);

                    var iconRect=new Rect(item.XCenter-iconSize/2,iconTop-iconSize,iconSize,iconSize);
                    infoCache={ Data:new Array(infoItem), Rect:iconRect, Type:infoItem.InfoType, TextRect:{X:iconTop, Y:item.XCenter} };
                    mapImage.set(infoItem.InfoType,infoCache);

                    iconTop+=iconSize;
                }
                else
                {
                    this.Canvas.textBaseline="bottom";
                    this.Canvas.textAlign="center";
                    if (this.InfoPosition===1) 
                    {
                        var yBottom=bottom+yOffset;
                        this.Canvas.fillText(icon.Text,item.XCenter,yBottom,iconSize);
                        var iconRect=new Rect(item.XCenter-iconSize/2,yBottom-iconSize,iconSize,iconSize);
                        var infoCache={ Data:new Array(infoItem), Rect:iconRect, Type:infoItem.InfoType, TextRect:{X:item.XCenter, Y:yBottom} };
                        mapImage.set(infoItem.InfoType,infoCache);
                        yOffset-=iconSize;
                    }
                    else
                    {

                        this.Canvas.fillText(icon.Text,item.XCenter,iconTop,iconSize);
                        var iconRect=new Rect(item.XCenter-iconSize/2,iconTop-iconSize,iconSize,iconSize);
                        var infoCache={ Data:new Array(infoItem), Rect:iconRect, Type:infoItem.InfoType, TextRect:{X:item.XCenter, Y:iconTop} };
                        mapImage.set(infoItem.InfoType,infoCache);
                        iconTop-=iconSize;
                    }
                }
            }
            else
            {
                imageInfo.Data.push(infoItem);
            }
        }

        var numText;
        if (g_JSChartResource.KLine.NumIcon) 
        {
            if (isHScreen) numText=g_JSChartResource.KLine.NumIcon.HScreenText;
            else numText=g_JSChartResource.KLine.NumIcon.Text;
        }
        for(var item of mapImage)
        {
            var value=item[1];
            if (value.Data.length>=2 && numText && bShowNum) //太小了 就不显示了
            {
                var iconID=value.Data.length;
                if (iconID>=numText.length) iconID=0;
                this.Canvas.fillStyle=g_JSChartResource.KLine.NumIcon.Color;
                var text=numText[iconID];
                this.Canvas.fillText(text,value.TextRect.X,value.TextRect.Y,iconSize);
            }

            if (!isHScreen) this.InfoTooltipRect.push(value);   //横屏没有tooltip
        }
    }

    //画交易图标
    this.DrawTradeIcon=function()
    {
        if (!this.TradeData) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;

        if (isHScreen) 
        {
            xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
            chartright=this.ChartBorder.GetBottom();
        }

        var sellData=this.TradeData.Sell;
        var buyData=this.TradeData.Buy;
        var iconSize=dataWidth+distanceWidth;
        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        var iconSizeMax=24*pixelTatio,iconSizeMin=12*pixelTatio;
        if (iconSize<iconSizeMin) iconSize=iconSizeMin;
        else if (iconSize>iconSizeMax) iconSize=iconSizeMax;
        this.Canvas.font=iconSize+'px '+this.TradeIcon.Family;

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var buy=false,sell=false;
            if (sellData && i<sellData.length) sell=sellData[i]>0;
            if (buyData && i<buyData.length) buy=buyData[i]>0;
            if (!sell && !buy) continue;

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yLow=this.ChartFrame.GetYFromData(data.Low);
            var yHigh=this.ChartFrame.GetYFromData(data.High);
            var yOpen=this.ChartFrame.GetYFromData(data.Open);
            var yClose=this.ChartFrame.GetYFromData(data.Close);
            var y=yHigh;

            if (buy)
            {
                this.Canvas.fillStyle=this.TradeIcon.Buy.Color;
                
                if (isHScreen)
                {
                    this.Canvas.textAlign='right';
                    this.Canvas.textBaseline='middle';
                    this.Canvas.fillText(this.TradeIcon.Buy.HScreenText,yLow,x);
                }
                else
                {
                    this.Canvas.textAlign='center';
                    this.Canvas.textBaseline='top';
                    this.Canvas.fillText(this.TradeIcon.Buy.Text,x,yLow);
                }
            }

            if (sell)
            {
                this.Canvas.fillStyle=this.TradeIcon.Sell.Color;
                if (isHScreen)
                {
                    this.Canvas.textAlign='left';
                    this.Canvas.textBaseline='middle';
                    this.Canvas.fillText(this.TradeIcon.Sell.HScreenText,yHigh,x);
                }
                else
                {
                    this.Canvas.textAlign='center';
                    this.Canvas.textBaseline='bottom';
                    this.Canvas.fillText(this.TradeIcon.Sell.Text,x,yHigh);
                }
            }
        }
    }

    this.GetTooltipData=function(x,y,tooltip)
    {
        for(var i in this.InfoTooltipRect)
        {
            var item=this.InfoTooltipRect[i];
            if (!item.Rect) continue;
            var rect=item.Rect;
            this.Canvas.beginPath();
            this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height);
            if (this.Canvas.isPointInPath(x,y))
            {
                //JSConsole.Chart.Log('[ChartKLine::GetTooltipData]', item);
                tooltip.Data=item;
                tooltip.ChartPaint=this;
                tooltip.Type=1; //信息地雷
                return true;
            }
        }

        for(var i in this.TooltipRect)
        {
            var rect=this.TooltipRect[i][1];
            this.Canvas.beginPath();
            this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height);
            if (this.Canvas.isPointInPath(x,y))
            {
                var index=this.TooltipRect[i][0];
                tooltip.Data=this.Data.Data[index];
                tooltip.ChartPaint=this;
                tooltip.Type=0; //K线信息
                return true;
            }
        }
        return false;
    }

    //计算当天显示数据的最大最小值
    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Max=null;
        range.Min=null;
        if (this.DrawType==1 || this.DrawType==4 )    // 1=收盘价线 4=收盘价面积图
        {
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
            {
                var data=this.Data.Data[i];
                if (!IFrameSplitOperator.IsNumber(data.Close)) continue;

                if (range.Max==null) range.Max=data.Close;
                if (range.Min==null) range.Min=data.Close;

                if (range.Max<data.Close) range.Max=data.Close;
                if (range.Min>data.Close) range.Min=data.Close;
            }
        }
        else
        {
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
            {
                var data=this.Data.Data[i];
                if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

                
                if (range.Max==null) range.Max=data.High;
                if (range.Min==null) range.Min=data.Low;

                if (range.Max<data.High) range.Max=data.High;
                if (range.Min>data.Low) range.Min=data.Low;
            }
        }

        return range;
    }

    //获取所有的价格
    this.GetAllPrice=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var setPrice=new Set();
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            var value=data.Close;
            setPrice.add(value);
        }

        var aryPrice=[];
        for(var item of setPrice) 
        {
            aryPrice.push(item);
        }

        aryPrice.sort(function(a,b) { return a-b; });   //排序 升序

        return aryPrice;
    }
}

//K线叠加 支持横屏
var OVERLAY_STATUS_ID=
{
    STATUS_NONE_ID:0,           //空闲状态
    STATUS_REQUESTDATA_ID:1,    //请求数据
    STATUS_RECVDATA_ID:2,       //接收到数据
    STATUS_FINISHED_ID:3,       //数据下载完成
};

function ChartOverlayKLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartOverlayKLine';    //类名
    this.Color="rgb(65,105,225)";
    this.MainData;                  //主图K线数据
    this.SourceData;                //叠加的原始数据
    this.Title;
    this.DrawType=0;
    this.CustomDrawType=null;       //图形类型
    this.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID;
    this.IsDelete=false;            //是否已经删除

    this.SetOption=function(option)
    {
        if (!option) return;
        if (IFrameSplitOperator.IsNumber(option.DrawType)) this.CustomDrawType=option.DrawType;
    }

    this.DrawKBar=function(firstOpen)   //firstOpen 当前屏第1个显示数据
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var drawType=this.DrawType;
        if (this.CustomDrawType!=null) drawType=this.CustomDrawType;

        var isFristDraw=true;
        var firstOverlayOpen=null;

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (!data || data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            if (firstOverlayOpen==null) firstOverlayOpen=data.Open;

            if (isFristDraw)
            {
                this.Canvas.strokeStyle=this.Color;
                this.Canvas.fillStyle=this.Color;
                this.Canvas.beginPath();
                isFristDraw=false;
            }

            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yLow=this.ChartFrame.GetYFromData(data.Low/firstOverlayOpen*firstOpen);
            var yHigh=this.ChartFrame.GetYFromData(data.High/firstOverlayOpen*firstOpen);
            var yOpen=this.ChartFrame.GetYFromData(data.Open/firstOverlayOpen*firstOpen);
            var yClose=this.ChartFrame.GetYFromData(data.Close/firstOverlayOpen*firstOpen);
            var y=yHigh;

            if (data.Open<data.Close)       //阳线
            {
                if (dataWidth>=4)
                {
                    if (data.High>data.Close)   //上影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):yClose),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):yClose));
                        }
                        y=yClose;
                    }
                    else
                    {
                        y=yClose;
                    }

                    if (isHScreen)
                    {
                        if (Math.abs(yOpen-y)<1)  
                        {
                            this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth));    //高度小于1,统一使用高度1
                        }
                        else 
                        {
                            if (drawType==3) this.Canvas.rect(ToFixedPoint(y),ToFixedPoint(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth));   //空心柱
                            else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yOpen-y),ToFixedRect(dataWidth));
                        }
                    }
                    else
                    {
                        if (Math.abs(yOpen-y)<1)  
                        {
                            this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1);    //高度小于1,统一使用高度1
                        }
                        else 
                        {
                            if (drawType==3) this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y));   //空心柱
                            else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),ToFixedRect(yOpen-y));
                        }
                    }

                    if (data.Open>data.Low)
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(this.DrawType==3?Math.min(yClose,yOpen):y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(this.DrawType==3?Math.max(yClose,yOpen):y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow));
                        }
                    }
                }
                else
                {
                    if (isHScreen)
                    {
                        this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                        this.Canvas.lineTo(yLow,ToFixedPoint(x));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                        this.Canvas.lineTo(ToFixedPoint(x),yLow);
                    }
                }
            }
            else if (data.Open>data.Close)  //阴线
            {
                if (dataWidth>=4)
                {
                    if (data.High>data.Close)   //上影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yOpen),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yOpen));
                        }
                        y=yOpen;
                    }
                    else
                    {
                        y=yOpen
                    }

                    if (isHScreen)
                    {
                        if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),1,ToFixedRect(dataWidth));    //高度小于1,统一使用高度1
                        else this.Canvas.fillRect(ToFixedRect(y),ToFixedRect(left),ToFixedRect(yClose-y),ToFixedRect(dataWidth));
                    }
                    else
                    {
                        if (Math.abs(yClose-y)<1) this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),1);    //高度小于1,统一使用高度1
                        else this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(y),ToFixedRect(dataWidth),ToFixedRect(yClose-y));
                    }

                    if (data.Open>data.Low) //下影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow));
                        }
                    }
                }
                else
                {
                    if (isHScreen)
                    {
                        this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                        this.Canvas.lineTo(yLow,ToFixedPoint(x));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                        this.Canvas.lineTo(ToFixedPoint(x),yLow);
                    } 
                }
            }
            else // 平线
            {
                if (dataWidth>=4)
                {
                    if (data.High>data.Close)   //上影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(y,ToFixedPoint(x));
                            this.Canvas.lineTo(yOpen,ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),y);
                            this.Canvas.lineTo(ToFixedPoint(x),yOpen);
                        }

                        y=yOpen;
                    }
                    else
                    {
                        y=yOpen;
                    }

                    if (isHScreen)
                    {
                        this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(left));
                        this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(right));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(left),ToFixedPoint(y));
                        this.Canvas.lineTo(ToFixedPoint(right),ToFixedPoint(y));
                    }

                    if (data.Open>data.Low) //下影线
                    {
                        if (isHScreen)
                        {
                            this.Canvas.moveTo(ToFixedPoint(y),ToFixedPoint(x));
                            this.Canvas.lineTo(ToFixedPoint(yLow),ToFixedPoint(x));
                        }
                        else
                        {
                            this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(y));
                            this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(yLow));
                        }
                    }
                }
                else
                {
                    if (isHScreen)
                    {
                        this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                        this.Canvas.lineTo(yLow,ToFixedPoint(x));
                    }
                    else
                    {
                        this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                        this.Canvas.lineTo(ToFixedPoint(x),yLow);
                    }
                }
            }

            //添加tooltip区域
            {
                var yTop=Math.min(yLow,yHigh,yOpen,yClose);
                var yBottom=Math.max(yLow,yHigh,yOpen,yClose);
                var rect=new Rect(left,yTop,dataWidth,yBottom-yTop);
                //this.Canvas.fillStyle="rgb(0,0,100)";
                //this.Canvas.fillRect(rect.X,rect.Y,rect.Width,rect.Height);
                this.TooltipRect.push([i,rect]);    //[0]数据索引 [1]数据区域
            }
        }

        if (isFristDraw==false) this.Canvas.stroke();
    }

    this.DrawAKLine=function(firstOpen) //美国线
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var firstOverlayOpen=null;
        this.Canvas.strokeStyle=this.Color;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            if (firstOverlayOpen==null) firstOverlayOpen=data.Open;
            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yLow=this.ChartFrame.GetYFromData(data.Low/firstOverlayOpen*firstOpen);
            var yHigh=this.ChartFrame.GetYFromData(data.High/firstOverlayOpen*firstOpen);
            var yOpen=this.ChartFrame.GetYFromData(data.Open/firstOverlayOpen*firstOpen);
            var yClose=this.ChartFrame.GetYFromData(data.Close/firstOverlayOpen*firstOpen);

            this.Canvas.beginPath();   //最高-最低
            if (isHScreen)
            {
                this.Canvas.moveTo(yHigh,ToFixedPoint(x));
                this.Canvas.lineTo(yLow,ToFixedPoint(x));
            }
            else
            {
                this.Canvas.moveTo(ToFixedPoint(x),yHigh);
                this.Canvas.lineTo(ToFixedPoint(x),yLow);
            }
            
            this.Canvas.stroke();

            if (dataWidth>=4)
            {
                this.Canvas.beginPath();    //开盘
                if (isHScreen)
                {
                    this.Canvas.moveTo(ToFixedPoint(yOpen),left);
                    this.Canvas.lineTo(ToFixedPoint(yOpen),x);
                }
                else
                {
                    this.Canvas.moveTo(left,ToFixedPoint(yOpen));
                    this.Canvas.lineTo(x,ToFixedPoint(yOpen));
                }
                this.Canvas.stroke();

                this.Canvas.beginPath();    //收盘
                if (isHScreen)
                {
                    this.Canvas.moveTo(ToFixedPoint(yClose),right);
                    this.Canvas.lineTo(ToFixedPoint(yClose),x);
                }
                else
                {
                    this.Canvas.moveTo(right,ToFixedPoint(yClose));
                    this.Canvas.lineTo(x,ToFixedPoint(yClose));
                }
                this.Canvas.stroke();
            }
        }

    }

    this.DrawCloseLine=function(firstOpen)  //收盘价线
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var firstOverlayOpen=null;
        var bFirstPoint=true;
        this.Canvas.strokeStyle=this.Color;
        this.Canvas.beginPath();
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            if (firstOverlayOpen==null) firstOverlayOpen=data.Open;
            var left=xOffset;
            var right=xOffset+dataWidth;
            if (right>chartright) break;
            var x=left+(right-left)/2;
            var yClose=this.ChartFrame.GetYFromData(data.Close/firstOverlayOpen*firstOpen);

            if (bFirstPoint)
            {
                if (isHScreen) this.Canvas.moveTo(yClose,x);
                else this.Canvas.moveTo(x,yClose);
                bFirstPoint=false;
            }
            else
            {
                if (isHScreen) this.Canvas.lineTo(yClose,x);
                else this.Canvas.lineTo(x,yClose);
            }
        }

        if (bFirstPoint==false) this.Canvas.stroke();
    }

    this.Draw=function()
    {
        this.TooltipRect=[];
        this.InfoTooltipRect=[];
        if (!this.MainData || !this.Data) return;

        var xPointCount=this.ChartFrame.XPointCount;
        var firstOpen=null; //主线数据第1个开盘价
        for(var i=this.Data.DataOffset,j=0;i<this.MainData.Data.length && j<xPointCount;++i,++j)
        {
            var data=this.MainData.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;
            firstOpen=data.Open;
            break;
        }

        if (firstOpen==null) return;

        var drawType=this.DrawType;
        if (this.CustomDrawType!=null) drawType=this.CustomDrawType;

        if (drawType==1) this.DrawCloseLine(firstOpen);
        else if (drawType==2) this.DrawAKLine(firstOpen);
        else this.DrawKBar(firstOpen);
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Max=null;
        range.Min=null;

        if (!this.MainData || !this.Data) return range;

        var firstOpen=null; //主线数据第1个收盘价
        for(var i=this.Data.DataOffset,j=0;i<this.MainData.Data.length && j<xPointCount;++i,++j)
        {
            var data=this.MainData.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;
            firstOpen=data.Close;
            break;
        }

        if (firstOpen==null) return range;

        var firstOverlayOpen=null;
        var high,low;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var data=this.Data.Data[i];
            if (!data || data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;
            if (firstOverlayOpen==null) firstOverlayOpen=data.Open;

            high=data.High/firstOverlayOpen*firstOpen;
            low=data.Low/firstOverlayOpen*firstOpen;
            if (range.Max==null) range.Max=high;
            if (range.Min==null) range.Min=low;

            if (range.Max<high) range.Max=high;
            if (range.Min>low) range.Min=low;
        }

        return range;
    }

    this.GetTooltipData=function(x,y,tooltip)
    {
        for(var i in this.TooltipRect)
        {
            var rect=this.TooltipRect[i][1];
            this.Canvas.beginPath();
            this.Canvas.rect(rect.X,rect.Y,rect.Width,rect.Height);
            if (this.Canvas.isPointInPath(x,y))
            {
                var index=this.TooltipRect[i][0];
                tooltip.Data=this.Data.Data[index];
                tooltip.ChartPaint=this;
                return true;
            }
        }
        return false;
    }
}

//分钟成交量 支持横屏
function ChartMinuteVolumBar()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartMinuteVolumBar';    //类名
    this.UpColor = g_JSChartResource.UpBarColor;
    this.DownColor = g_JSChartResource.DownBarColor;
    this.YClose;    //前收盘

    this.Draw=function()
    {
        var isHScreen = (this.ChartFrame.IsHScreen === true);
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright = this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var yBottom=this.ChartFrame.GetYFromData(0);
        var yPrice=this.YClose; //上一分钟的价格
        var data=this.Data;
        for(var i=data.DataOffset,j=0;i<data.Data.length && j<xPointCount;++i,++j)
        {
            var item = data.Data[i];
            var vol=null;
            if (!item) continue;
            var price=null;
            if (item.Before) 
            {
                vol=item.Before.Vol;
                price=item.Before.Close;
            }
            else 
            {
                vol=item.Vol;
                price=item.Close;
            }
            if (!vol) continue;

            var y=this.ChartFrame.GetYFromData(vol);
            var x=this.ChartFrame.GetXFromIndex(i);
            if (x>chartright) break;

            //价格>=上一分钟价格 红色 否则绿色
            this.Canvas.strokeStyle = price >= yPrice ? this.UpColor:this.DownColor;
            this.Canvas.beginPath();
            if (isHScreen)
            {
                this.Canvas.moveTo(y,ToFixedPoint(x));
                this.Canvas.lineTo(yBottom,ToFixedPoint(x));
            }
            else
            {
                this.Canvas.moveTo(ToFixedPoint(x),y);
                this.Canvas.lineTo(ToFixedPoint(x),yBottom);
            }
            this.Canvas.stroke();
            if (price) yPrice=price;
        }
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=0;
        range.Max=null;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var item = this.Data.Data[i];
            if (!item || !item.Vol) continue;
            if (range.Max == null) range.Max = item.Vol;
            if (range.Max < item.Vol) range.Max = item.Vol;
        }

        return range;
    }
}

function ChartErrorMessage()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartErrorMessage';    //类名

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (this.NotSupportMessage) this.DrawNotSupportmessage();
    }
}

//线段 支持横屏
function ChartLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartLine';    //类名
    this.Color="rgb(255,193,37)";   //线段颜色
    this.LineWidth;                 //线段宽度
    this.DrawType=0;                //画图方式  0=无效数平滑  1=无效数不画断开
    this.IsDotLine=false;           //虚线

    this.Draw=function()
    {
        if (!this.IsShow)
            return;
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        switch(this.DrawType)
        {
            case 0:
                return this.DrawLine();
            case 1: 
                return this.DrawStraightLine();
        }
    }

    this.DrawLine=function()
    {
        var bHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (bHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        
        this.Canvas.save();
        if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio();
        var bFirstPoint=true;
        var drawCount=0;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            if (bFirstPoint)
            {
                this.Canvas.strokeStyle=this.Color;
                this.Canvas.beginPath();
                if (bHScreen) this.Canvas.moveTo(y,x);  //横屏坐标轴对调
                else this.Canvas.moveTo(x,y);
                bFirstPoint=false;
            }
            else
            {
                if (bHScreen) this.Canvas.lineTo(y,x);
                else this.Canvas.lineTo(x,y);
            }

            ++drawCount;
        }

        if (drawCount>0) this.Canvas.stroke();
        this.Canvas.restore();
    }

    //无效数不画
    this.DrawStraightLine=function()
    {
        var bHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (bHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var lockRect=this.GetLockRect();
        if (lockRect)
        {
            if (bHScreen) chartright=lockRect.Top;
            else chartright=lockRect.Left;
        }

        this.Canvas.save();
        if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio();
        this.Canvas.strokeStyle=this.Color;
        if (this.IsDotLine) this.Canvas.setLineDash([3,5]); //画虚线

        var bFirstPoint=true;
        var drawCount=0;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) 
            {
                if (drawCount>0) this.Canvas.stroke();
                bFirstPoint=true;
                drawCount=0;
                continue;
            }

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            if (bFirstPoint)
            {
                this.Canvas.beginPath();
                if (bHScreen) this.Canvas.moveTo(y,x);  //横屏坐标轴对调
                else this.Canvas.moveTo(x,y);
                bFirstPoint=false;
            }
            else
            {
                if (bHScreen) this.Canvas.lineTo(y,x);
                else this.Canvas.lineTo(x,y);
            }

            ++drawCount;
        }

        if (drawCount>0) this.Canvas.stroke();
        this.Canvas.restore();
    }
}

//彩色线段
function ChartPartLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartPartLine';     //类名
    this.LineWidth;                     //线段宽度            

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        this.DrawLine();
    }

    this.DrawLine=function()
    {
        var bHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (bHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        
        this.Canvas.save();
        if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio();
        var bFirstPoint=true;
        var drawCount=0;
        var lastColor;
        var lastPoint={X:null,Y:null};
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var item=this.Data.Data[i];
            if (item==null) continue;
            var value=item.Value;
            if (value==null) continue;

            var color=item.RGB;
            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            if (color!=lastColor)
            {
                if (lastColor && drawCount>0) this.Canvas.stroke();

                drawCount=0;
                lastColor=color;
                this.Canvas.strokeStyle=color;
                this.Canvas.beginPath();

                if (lastPoint.X!=null && lastPoint.Y!=null) //接着上一个点连线
                {
                    if (bHScreen) this.Canvas.moveTo(lastPoint.Y,lastPoint.X);  //横屏坐标轴对调
                    else this.Canvas.moveTo(lastPoint.X,lastPoint.Y);

                    if (bHScreen) this.Canvas.lineTo(y,x);
                    else this.Canvas.lineTo(x,y);

                    ++drawCount;
                }
                else
                {
                    if (bHScreen) this.Canvas.moveTo(y,x);  //横屏坐标轴对调
                    else this.Canvas.moveTo(x,y);
                }
            }
            else
            {
                if (bHScreen) this.Canvas.lineTo(y,x);
                else this.Canvas.lineTo(x,y);
                ++drawCount;
            }

            lastPoint.X=x;
            lastPoint.Y=y;
        }

        if (drawCount>0) this.Canvas.stroke();
        this.Canvas.restore();
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var item = this.Data.Data[i];
            if (!item || !item.Value) continue;
            if (range.Max == null || range.Max<item.Value) range.Max = item.Value;
            if (range.Min == null || range.Min>item.Value) range.Min = item.Value;
        }

        return range;
    }
}


//POINTDOT 圆点 支持横屏
function ChartPointDot()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartPointDot';    //类名
    this.Color="rgb(255,193,37)";   //线段颜色
    this.Radius=1;                  //点半径

    this.Draw=function()
    {
        if (!this.IsShow) return;

        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        var bHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (bHScreen===true) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        this.Canvas.save();
        this.Canvas.fillStyle=this.Color;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            this.Canvas.beginPath();
            if (bHScreen) this.Canvas.arc(y, x, this.Radius, 0, Math.PI*2, true);
            else this.Canvas.arc(x, y, this.Radius, 0, Math.PI*2, true);
            this.Canvas.closePath();
            this.Canvas.fill();
        }

        this.Canvas.restore();
    }
}

//通达信语法  STICK 支持横屏
function ChartStick()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Color="rgb(255,193,37)";   //线段颜色
    this.LineWidth;                 //线段宽度
    this.ClassName='ChartStick';

    this.DrawLine=function()
    {
        if (!this.Data || !this.Data.Data) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen===true) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        this.Canvas.save();
        if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio();
        var bFirstPoint=true;
        var drawCount=0;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            if (bFirstPoint)
            {
                this.Canvas.strokeStyle=this.Color;
                this.Canvas.beginPath();
                if (isHScreen) this.Canvas.moveTo(y,x);
                else this.Canvas.moveTo(x,y);
                bFirstPoint=false;
            }
            else
            {
                if (isHScreen) this.Canvas.lineTo(y,x);
                else this.Canvas.lineTo(x,y);
            }

            ++drawCount;
        }

        if (drawCount>0) this.Canvas.stroke();
        this.Canvas.restore();
    }

    this.DrawStick=function()
    {
        if (!this.Data || !this.Data.Data) return;
        var bHScreen=(this.ChartFrame.IsHScreen===true);
        var chartright=this.ChartBorder.GetRight();
        if (bHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var yBottom=this.ChartBorder.GetBottom();
        var xLeft=this.ChartBorder.GetLeft();

        this.Canvas.save();
        this.Canvas.strokeStyle=this.Color;
        if (this.LineWidth) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio();
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            this.Canvas.beginPath();
            if (bHScreen)
            {
                this.Canvas.moveTo(xLeft,x);  
                this.Canvas.lineTo(y,x);
                this.Canvas.stroke();
            }
            else
            {
                var xFix=parseInt(x.toString())+0.5;
                this.Canvas.moveTo(xFix,y);  
                this.Canvas.lineTo(xFix,yBottom);
            }
            this.Canvas.stroke();
        }

        this.Canvas.restore();
    }

    this.Draw=function()
    {
        if (!this.IsShow) return;

        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        this.DrawStick();
    }
}

//通达信语法 LINESTICK 支持横屏
function ChartLineStick()
{
    this.newMethod=ChartStick;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartLineStick';

    this.Draw=function()
    {
        if (!this.IsShow) return;

        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        this.DrawStick();
        this.DrawLine();
    }
}

//通达信语法 VOLSTICK 支持横屏
function ChartVolStick()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.UpColor=g_JSChartResource.UpBarColor;
    this.DownColor=g_JSChartResource.DownBarColor;
    this.HistoryData;               //历史数据
    this.KLineDrawType=0;
    this.ClassName='ChartVolStick';

    this.Draw=function()
    {
        if (this.ChartFrame.IsHScreen===true) 
        {
            this.HScreenDraw();
            return;
        }

        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;
        var lockRect=this.GetLockRect();
        if (lockRect) chartright=lockRect.Left;

        var yBottom=this.ChartFrame.GetYFromData(0);

        if (dataWidth>=4)
        {
            yBottom=ToFixedRect(yBottom);
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var value=this.Data.Data[i];
                var kItem=this.HistoryData.Data[i];
                if (value==null || kItem==null) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                if (right>chartright) break;

                var y=this.ChartFrame.GetYFromData(value);
                var bUp=false;
                if (kItem.Close>=kItem.Open)
                {
                    this.Canvas.fillStyle=this.UpColor;
                    bUp=true;
                }
                else
                {
                    this.Canvas.fillStyle=this.DownColor;
                }
                
                var height=ToFixedRect(yBottom-y);//高度调整为整数
                y=yBottom-height;
                if (bUp && (this.KLineDrawType==1 || this.KLineDrawType==2 || this.KLineDrawType==3)) //空心柱子
                {
                    this.Canvas.strokeStyle=this.UpColor;
                    this.Canvas.beginPath();
                    this.Canvas.rect(ToFixedPoint(left),ToFixedPoint(y),ToFixedRect(dataWidth),height);
                    this.Canvas.stroke();
                }
                else
                {
                    this.Canvas.fillRect(ToFixedRect(left),y,ToFixedRect(dataWidth),height);
                }
            }
        }
        else    //太细了直接话线
        {
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var value=this.Data.Data[i];
                var kItem=this.HistoryData.Data[i];
                if (value==null || kItem==null) continue;

                var y=this.ChartFrame.GetYFromData(value);
                var x=this.ChartFrame.GetXFromIndex(j);
                if (x>chartright) break;

                if (kItem.Close>=kItem.Open)
                    this.Canvas.strokeStyle=this.UpColor;
                else
                    this.Canvas.strokeStyle=this.DownColor;

                var x=this.ChartFrame.GetXFromIndex(j);
                this.Canvas.beginPath();
                this.Canvas.moveTo(ToFixedPoint(x),y);
                this.Canvas.lineTo(ToFixedPoint(x),yBottom);
                this.Canvas.stroke();
            }
        }
    }

    this.HScreenDraw=function() //横屏画法
    {
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
        var chartBottom=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var lockRect=this.GetLockRect();
        if (lockRect) chartBottom=lockRect.Top;

        var yBottom=this.ChartFrame.GetYFromData(0);

        if (dataWidth>=4)
        {
            yBottom=ToFixedRect(yBottom);
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var value=this.Data.Data[i];
                var kItem=this.HistoryData.Data[i];
                if (value==null || kItem==null) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                if (right>chartBottom) break;

                var y=this.ChartFrame.GetYFromData(value);
                var bUp=false;
                if (kItem.Close>=kItem.Open)
                {
                    bUp=true;
                    this.Canvas.fillStyle=this.UpColor;
                }
                else
                {
                    this.Canvas.fillStyle=this.DownColor;
                }
                
                var height=ToFixedRect(y-yBottom);  //高度调整为整数
                if (bUp && (this.KLineDrawType==1 || this.KLineDrawType==2 || this.KLineDrawType==3)) //空心柱子
                {
                    this.Canvas.strokeStyle=this.UpColor;
                    this.Canvas.beginPath();
                    this.Canvas.rect(ToFixedPoint(yBottom),ToFixedPoint(left),height,ToFixedRect(dataWidth));
                    this.Canvas.stroke();
                }
                else
                {
                    this.Canvas.fillRect(yBottom,ToFixedRect(left),height,ToFixedRect(dataWidth));
                }
            }
        }
        else    //太细了直接话线
        {
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var value=this.Data.Data[i];
                var kItem=this.HistoryData.Data[i];
                if (value==null || kItem==null) continue;

                var y=this.ChartFrame.GetYFromData(value);
                var x=this.ChartFrame.GetXFromIndex(j);
                if (x>chartBottom) break;

                if (kItem.Close>kItem.Open)
                    this.Canvas.strokeStyle=this.UpColor;
                else
                    this.Canvas.strokeStyle=this.DownColor;

                var x=this.ChartFrame.GetXFromIndex(j);
                this.Canvas.beginPath();
                this.Canvas.moveTo(y,ToFixedPoint(x));
                this.Canvas.lineTo(yBottom,ToFixedPoint(x));
                this.Canvas.stroke();
            }
        }
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=0;
        range.Max=null;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (range.Max==null) range.Max=value;

            if (range.Max<value) range.Max=value;
        }

        return range;
    }
}



//线段 多数据(一个X点有多条Y数据) 支持横屏
function ChartLineMultiData()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartLineMultiData';    //类名
    this.Color="rgb(255,193,37)"; //线段颜色

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        var bFirstPoint=true;
        var drawCount=0;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var aryValue=this.Data.Data[i];
            if (aryValue==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            if (x>chartright) break;

            for(var index in aryValue)
            {
                var value =aryValue[index].Value;
                
                var y=this.ChartFrame.GetYFromData(value);

                if (bFirstPoint)
                {
                    this.Canvas.strokeStyle=this.Color;
                    this.Canvas.beginPath();
                    if (isHScreen) this.Canvas.moveTo(y,x);
                    else this.Canvas.moveTo(x,y);
                    bFirstPoint=false;
                }
                else
                {
                    if (isHScreen) this.Canvas.lineTo(y,x);
                    else this.Canvas.lineTo(x,y);
                }

                ++drawCount;
            }
        }

        if (drawCount>0)
            this.Canvas.stroke();
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;

        if(!this.Data || !this.Data.Data) return range;

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var aryValue=this.Data.Data[i];
            if (aryValue==null) continue;

            for(var index in aryValue)
            {
                var value=aryValue[index].Value;
                if (range.Max==null) range.Max=value;
                if (range.Min==null) range.Min=value;

                if (range.Max<value) range.Max=value;
                if (range.Min>value) range.Min=value;
            }
        }

        return range;
    }
}

//柱子 支持横屏
function ChartStickLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartStickLine';    //类名
    this.Color="rgb(255,193,37)";               //线段颜色
    this.LineWidth=2*GetDevicePixelRatio();     //线段宽度
    this.BarType=0; //柱子类型 0=实心 1=空心

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        if (isHScreen) xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;

        this.Canvas.save();
        var bFillBar=false;
        var bFillKLine=false;
        if (this.LineWidth==50) //宽度==50 跟K线宽度保持一致
        {
            if (dataWidth>=4)
            {
                bFillKLine=true; 
                this.Canvas.fillStyle=this.Color; 
                this.Canvas.strokeStyle=this.Color;
            }
            else    //太细了 画竖线
            {
                this.Canvas.lineWidth=GetDevicePixelRatio();
                this.Canvas.strokeStyle=this.Color;
            }  
        }
        else if (this.LineWidth<100)
        {
            var LineWidth=this.LineWidth;
            if (dataWidth<=4) LineWidth=GetDevicePixelRatio();
            else if (dataWidth<LineWidth) LineWidth=parseInt(dataWidth);
            this.Canvas.lineWidth=LineWidth;
            this.Canvas.strokeStyle=this.Color;
        }
        else
        {
            bFillBar=true;
            this.Canvas.fillStyle=this.Color;
            var fixedWidth=2*GetDevicePixelRatio();
        }

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var price=value.Value;
            var price2=value.Value2;
            if (price2==null) price2=0;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(price);
            var y2=this.ChartFrame.GetYFromData(price2);

            if (x>chartright) break;

            if (bFillBar)
            {
                if (isHScreen)
                {
                    var left=xOffset-fixedWidth;
                    this.Canvas.fillRect(Math.min(y,y2),left,Math.abs(y-y2),dataWidth+distanceWidth+fixedWidth*2);
                }
                else
                {
                    var left=xOffset-fixedWidth;
                    var barWidth=dataWidth+distanceWidth+fixedWidth*2;
                    if (left+barWidth>chartright) barWidth=chartright-left; //不要超过右边框子
                    this.Canvas.fillRect(ToFixedRect(left),ToFixedRect(Math.min(y,y2)),ToFixedRect(barWidth),ToFixedRect(Math.abs(y-y2)));
                }
            }
            else if (bFillKLine)
            {
                if (this.BarType==1)    //实心
                {
                    if (isHScreen)
                    {
                        this.Canvas.beginPath();
                        this.Canvas.fillRect(ToFixedRect(Math.min(y,y2)),ToFixedRect(xOffset),ToFixedRect(Math.abs(y-y2)),ToFixedRect(dataWidth));
                        this.Canvas.stroke();
                    }
                    else
                    {
                        this.Canvas.beginPath();
                        this.Canvas.rect(ToFixedRect(xOffset),ToFixedRect(Math.min(y,y2)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(y-y2)));
                        this.Canvas.stroke();
                    }
                }
                else
                {
                    if (isHScreen) 
                        this.Canvas.fillRect(ToFixedRect(Math.min(y,y2)),ToFixedRect(xOffset),ToFixedRect(Math.abs(y-y2)),ToFixedRect(dataWidth));
                    else 
                        this.Canvas.fillRect(ToFixedRect(xOffset),ToFixedRect(Math.min(y,y2)),ToFixedRect(dataWidth),ToFixedRect(Math.abs(y-y2)));
                }
            }
            else
            {
                if (isHScreen)
                {
                    this.Canvas.beginPath();
                    this.Canvas.moveTo(y,ToFixedPoint(x));
                    this.Canvas.lineTo(y2,ToFixedPoint(x));
                    this.Canvas.stroke();
                }
                else
                {
                    var xFix=parseInt(x.toString())+0.5;
                    this.Canvas.beginPath();
                    this.Canvas.moveTo(xFix,y);  
                    this.Canvas.lineTo(xFix,y2);
                    this.Canvas.stroke();
                }
            }
        }

        this.Canvas.restore();
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;

        if(!this.Data || !this.Data.Data) return range;

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var data=this.Data.Data[i];
            if (data == null) continue;
            var value2=data.Value2;
            if (value2==null) value2=0;
            if (data==null || isNaN(data.Value) ||isNaN(value2)) continue;

            var valueMax=Math.max(data.Value,value2);
            var valueMin=Math.min(data.Value,value2);
            
            if (range.Max==null) range.Max=valueMax;
            if (range.Min==null) range.Min=valueMin;

            if (range.Max<valueMax) range.Max=valueMax;
            if (range.Min>valueMin) range.Min=valueMin;
        }

        return range;
    }
}

function ChartText()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartText';    //类名
    this.TextFont="14px 微软雅黑";

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;

        for(var i in this.Data.Data)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var price=value.Value;
            var position=value.Position;

            if (position=='Left')
            {
                var x=this.ChartFrame.GetXFromIndex(0);
                var y=this.ChartFrame.GetYFromData(price);

                if (x>chartright) continue;

                this.Canvas.textAlign='left';
                this.Canvas.textBaseline='middle';
                this.Canvas.fillStyle=value.Color;
                this.Canvas.font=this.TextFont;
                this.Canvas.fillText(value.Message,x,y);
            }

        }

    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;

        if(!this.Data || !this.Data.Data) return range;

        for(var i in this.Data.Data)
        {
            var data=this.Data.Data[i];
            if (data==null || isNaN(data.Value)) continue;

            var value=data.Value;
            
            if (range.Max==null) range.Max=value;
            if (range.Min==null) range.Min=value;

            if (range.Max<value) range.Max=value;
            if (range.Min>value) range.Min=value;
        }

        return range;
    }
}

/*
    文字输出 支持横屏
    数组不为null的数据中输出 this.Text文本
*/
function ChartSingleText()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartSingleText';    //类名
    this.Color="rgb(255,193,37)";           //线段颜色
    this.TextFont="14px 微软雅黑";           //线段宽度
    this.Text;
    this.TextAlign='left';
    this.Direction=0;       //0=middle 1=bottom 2=top
    this.YOffset=0;
    this.Position;          //指定输出位置

    this.IconFont;          //Iconfont

    this.Draw=function()
    {
        if (!this.IsShow) return;

        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (this.Position) 
        {
            this.DrawPosition();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true)
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        var top=this.ChartBorder.GetTopEx();
        var bottom=this.ChartBorder.GetBottomEx();
        if (isHScreen) 
        {
            chartright=this.ChartBorder.GetBottom();
            top=this.ChartBorder.GetRightEx();
            bottom=this.ChartBorder.GetLeftEx();
        }
        var xPointCount=this.ChartFrame.XPointCount;

        var isArrayText=Array.isArray(this.Text);
        var pixelTatio = GetDevicePixelRatio();
        
        if (this.Direction==1) this.Canvas.textBaseline='bottom';
        else if (this.Direction==2) this.Canvas.textBaseline='top';
        else this.Canvas.textBaseline='middle';

        if (this.IconFont)
        {
            this.Color=this.IconFont.Color;
            this.Text=this.IconFont.Text;

            var iconSize=dataWidth;
            var minIconSize=12*pixelTatio;
            var maxIconSize=24*pixelTatio;
            if (iconSize<minIconSize) iconSize=minIconSize;
            else if (iconSize>maxIconSize) iconSize=maxIconSize;
            this.Canvas.font=iconSize+'px '+this.IconFont.Family;
        }
        else
        {
            this.TextFont=this.GetDynamicFont(dataWidth*2*pixelTatio);
            this.Canvas.font=this.TextFont;
        }

        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            this.Canvas.textAlign=this.TextAlign;
            this.Canvas.fillStyle=this.Color;
            
            if (this.YOffset>0 && this.Direction>0)
            {
                var yPrice=y;

                this.Canvas.save(); 
                this.Canvas.setLineDash([5,10]);
                this.Canvas.strokeStyle=this.Color;
                this.Canvas.beginPath();
                if (isHScreen)
                {
                    if (this.Direction==1) 
                    {
                        y=top-this.YOffset*pixelTatio;
                        yPrice+=5*pixelTatio;
                    }
                    else 
                    {
                        y=bottom+this.YOffset*pixelTatio;
                        yPrice-=5*pixelTatio;
                    }
                    this.Canvas.moveTo(ToFixedPoint(yPrice),ToFixedPoint(x));
                    this.Canvas.lineTo(ToFixedPoint(y),ToFixedPoint(x));
                }
                else
                {
                    if (this.Direction==1) 
                    {
                        y=top+this.YOffset*pixelTatio;
                        yPrice+=5*pixelTatio;
                    }
                    else 
                    {
                        y=bottom-this.YOffset*pixelTatio;
                        yPrice-=5*pixelTatio;
                    }

                    this.Canvas.moveTo(ToFixedPoint(x),ToFixedPoint(yPrice));
                    this.Canvas.lineTo(ToFixedPoint(x),ToFixedPoint(y));
                }
                this.Canvas.stroke();
                this.Canvas.restore();
            }

            if (isArrayText)
            {
                var text=this.Text[i];
                if (!text) continue;
                this.DrawText(text,x,y,isHScreen);
            }
            else
            {
                //JSConsole.Chart.Log('[ChartSingleText::Draw] ',this.Direction,this.Text)
                this.DrawText(this.Text,x,y,isHScreen);
            }
        }
    }

    this.DrawPosition=function()    //绘制在指定位置上
    {
        if (!this.Text) return;
        var isHScreen=(this.ChartFrame.IsHScreen===true)
        if (isHScreen)
        {
            var x=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidthEx()*this.Position.Y;
            var y=this.ChartBorder.GetTop()+this.ChartBorder.GetHeight()*this.Position.X;
        }
        else
        {
            var x=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*this.Position.X;
            var y=this.ChartBorder.GetTopEx()+this.ChartBorder.GetHeight()*this.Position.Y;
        }

        if (isHScreen) 
        {
            chartright=this.ChartBorder.GetBottom();
            top=this.ChartBorder.GetRightEx();
            bottom=this.ChartBorder.GetLeftEx();
        }

        this.Canvas.fillStyle=this.Color;
        if (this.Position.Type==0) this.Canvas.textAlign='left';
        else if (this.Position.Type==2) this.Canvas.textAlign='center';
        else this.Canvas.textAlign='right';
        this.DrawText(this.Text,x,y,isHScreen);

    }

    this.DrawText=function(text,x,y,isHScreen)
    {
        if (isHScreen)
        {
            this.Canvas.save(); 
            this.Canvas.translate(y, x);
            this.Canvas.rotate(90 * Math.PI / 180);
            this.Canvas.fillText(text,0,0);
            this.Canvas.restore();
        }
        else
        {
            this.Canvas.fillText(text,x,y);
            /*
            var textWidth=this.Canvas.measureText(text).width;
            var rectSize=textWidth*2;
            var xRect,yRect;
            if (this.Direction==1)  yRect=y-rectSize;   //底部
            else if (this.Direction==2) yRect=y;    //顶部
            else yRect=y-rectSize/2; //居中

            if (this.TextAlign=='left') xRect=x;
            else if (this.TextAlign=='right') xRect=x-rectSize;
            else xRect=x-rectSize/2;

            this.Canvas.fillStyle=this.Color;
            this.Canvas.fillRect(xRect,yRect,rectSize,rectSize);

            this.Canvas.fillStyle='rgb(255,255,255)';
            this.Canvas.fillText(text,x,y);
            */
            
        }
    }
}

//直线 水平直线 只有1个数据
function ChartStraightLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartStraightLine';    //类名
    this.Color="rgb(255,193,37)";   //线段颜色

    this.Draw=function()
    {
        if (!this.Data || !this.Data.Data) return;
        if (this.Data.Data.length!=1) return;

        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;

        var yValue=this.Data.Data[0];
        var y=this.ChartFrame.GetYFromData(yValue);
        var xLeft=this.ChartFrame.GetXFromIndex(0);
        var xRight=this.ChartFrame.GetXFromIndex(xPointCount-1);

        var yFix=parseInt(y.toString())+0.5;
        this.Canvas.beginPath();
        this.Canvas.moveTo(xLeft,yFix);
        this.Canvas.lineTo(xRight,yFix);
        this.Canvas.strokeStyle=this.Color;
        this.Canvas.stroke();
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;

        if (!this.Data || !this.Data.Data) return range;
        if (this.Data.Data.length!=1) return range;

        range.Min=this.Data.Data[0];
        range.Max=this.Data.Data[0];

        return range;
    }
}

/*  
    水平面积 只有1个数据
    Data 数据结构 
    Value, Value2  区间最大最小值
    Color=面积的颜色
    Title=标题 TitleColor=标题颜色
    支持横屏
*/
function ChartStraightArea() 
{
    this.newMethod = IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartStraightArea';    //类名
    this.Color = "rgb(255,193,37)";   //线段颜色
    this.Font ='11px 微软雅黑';

    this.Draw = function () 
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.Data || !this.Data.Data) return;

        if (this.ChartFrame.IsHScreen===true)
        {
            this.HScreenDraw();
            return;
        }

        var dataWidth = this.ChartFrame.DataWidth;
        var distanceWidth = this.ChartFrame.DistanceWidth;
        var chartright = this.ChartBorder.GetRight();
        var bottom = this.ChartBorder.GetBottom();
        var left = this.ChartBorder.GetLeft();
        var xPointCount = this.ChartFrame.XPointCount;

        var xRight = this.ChartFrame.GetXFromIndex(xPointCount - 1);

        for(let i in this.Data.Data)
        {
            let item=this.Data.Data[i];
            if (item==null || isNaN(item.Value) || isNaN(item.Value2)) continue;
            if (item.Color==null) continue;

            let valueMax=Math.max(item.Value,item.Value2);
            let valueMin=Math.min(item.Value,item.Value2);

            var yTop=this.ChartFrame.GetYFromData(valueMax);
            var yBottom=this.ChartFrame.GetYFromData(valueMin);

            this.Canvas.fillStyle = item.Color;
            this.Canvas.fillRect(ToFixedRect(left), ToFixedRect(yTop), ToFixedRect(xRight - left), ToFixedRect(yBottom - yTop));

            if(item.Title && item.TitleColor)
            {
              this.Canvas.textAlign = 'right';
              this.Canvas.textBaseline = 'middle';
              this.Canvas.fillStyle = item.TitleColor;
              this.Canvas.font = this.Font;
              let y = yTop + (yBottom - yTop)/2;
              this.Canvas.fillText(item.Title, xRight, y);
            }
        }
    }

    this.HScreenDraw=function()
    {
        var bottom = this.ChartBorder.GetBottom();
        var top=this.ChartBorder.GetTop();
        var height=this.ChartBorder.GetHeight();

        for(let i in this.Data.Data)
        {
            let item=this.Data.Data[i];
            if (item==null || isNaN(item.Value) || isNaN(item.Value2)) continue;
            if (item.Color==null) continue;

            let valueMax=Math.max(item.Value,item.Value2);
            let valueMin=Math.min(item.Value,item.Value2);

            var yTop=this.ChartFrame.GetYFromData(valueMax);
            var yBottom=this.ChartFrame.GetYFromData(valueMin);

            this.Canvas.fillStyle = item.Color;
            this.Canvas.fillRect(ToFixedRect(yBottom), ToFixedRect(top), ToFixedRect(yTop-yBottom),ToFixedRect(height));

            if(item.Title && item.TitleColor)
            {
                var xText=yTop + (yBottom - yTop)/2;
                var yText=bottom;
                this.Canvas.save(); 
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180);

                this.Canvas.textAlign = 'right';
                this.Canvas.textBaseline = 'middle';
                this.Canvas.fillStyle = item.TitleColor;
                this.Canvas.font = this.Font;
                this.Canvas.fillText(item.Title, 0, -2);

                this.Canvas.restore();
            }
        }
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;

        if (!this.Data || !this.Data.Data) return range;
        
        for (let i in this.Data.Data)
        {
          let item = this.Data.Data[i];
            if (item==null || isNaN(item.Value) || isNaN(item.Value2)) continue;

            let valueMax=Math.max(item.Value,item.Value2);
            let valueMin=Math.min(item.Value,item.Value2);

            if (range.Max==null) range.Max=valueMax;
            if (range.Min==null) range.Min=valueMin;

            if (range.Max<valueMax) range.Max=valueMax;
            if (range.Min>valueMin) range.Min=valueMin;
        }

        return range;
    }
}

//分钟线 支持横屏
function ChartMinutePriceLine()
{
    this.newMethod=ChartLine;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartMinutePriceLine';    //类名
    this.YClose;
    this.IsDrawArea=true;    //是否画价格面积图
    this.AreaColor='rgba(0,191,255,0.1)';
    this.SourceData;
    this.IsShowLead=false;
    this.LeadData;
    this.UpColor=g_JSChartResource.UpBarColor;
    this.DownColor=g_JSChartResource.DownBarColor;

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (!this.IsShow) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen===true) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var minuteCount=this.ChartFrame.MinuteCount;
        var bottom=this.ChartBorder.GetBottomEx();
        var left=this.ChartBorder.GetLeftEx();
        var data=this.Data;
        var isBeforeData=false;
        if (this.SourceData) 
        {
            data=this.SourceData;
            isBeforeData=true;
        }

        if (this.IsShowLead) 
            this.DrawLead();

        if (!data) return;

        var bFirstPoint=true;
        var ptFirst={}; //第1个点
        var drawCount=0;
        for(var i=data.DataOffset,j=0;i<data.Data.length && j<xPointCount;++i,++j)
        {
            var value=null;
            if (isBeforeData)
            {
                var item=data.Data[i];
                if (item.Before) value=item.Before.Close;
                else value=item.Close;
            }
            else
            {
                value=data.Data[i];
            }
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (bFirstPoint)
            {
                this.Canvas.strokeStyle=this.Color;
                this.Canvas.beginPath();
                if (isHScreen) this.Canvas.moveTo(y,x);
                else this.Canvas.moveTo(x,y);
                bFirstPoint=false;
                ptFirst={X:x,Y:y};
            }
            else
            {
                if (isHScreen) this.Canvas.lineTo(y,x);
                else this.Canvas.lineTo(x,y);
            }

            ++drawCount;

            if (drawCount>=minuteCount) //上一天的数据和这天地数据线段要断开
            {
                bFirstPoint=true;
                this.Canvas.stroke();
                if (this.IsDrawArea)   //画面积
                {
                    if (isHScreen)
                    {
                        this.Canvas.lineTo(left,x);
                        this.Canvas.lineTo(left,ptFirst.X);
                    }
                    else
                    {
                        this.Canvas.lineTo(x,bottom);
                        this.Canvas.lineTo(ptFirst.X,bottom);
                    }
                    this.Canvas.fillStyle=this.AreaColor;
                    this.Canvas.fill();
                }
                drawCount=0;
            }
        }

        if (drawCount>0)
        {
            this.Canvas.stroke();
            if (this.IsDrawArea)   //画面积
            {
                if (isHScreen)
                {
                    this.Canvas.lineTo(left,x);
                    this.Canvas.lineTo(left,ptFirst.X);
                }
                else
                {
                    this.Canvas.lineTo(x,bottom);
                    this.Canvas.lineTo(ptFirst.X,bottom);
                }
                this.Canvas.fillStyle=this.AreaColor;
                this.Canvas.fill();
            }
        }
    }

    //画领先指标
    this.DrawLead=function()
    {
        if (!this.LeadData) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        //if (isHScreen) return;
        //var dataWidth=this.ChartFrame.DataWidth;
        //var distanceWidth=this.ChartFrame.DistanceWidth;
        //var chartright=this.ChartBorder.GetRight();
       
        var xPointCount=this.ChartFrame.XPointCount;
        var minuteCount=this.ChartFrame.MinuteCount;
        var bottom=this.ChartBorder.GetBottomEx();
        var top=this.ChartBorder.GetTopEx();
        if (isHScreen===true) top=this.ChartBorder.GetRightEx();

        if (xPointCount>minuteCount) return;

        var aryLead=[]; //{X: Value:}
        var max=null, min=null;
        var yCenter=this.ChartFrame.GetYFromData(this.YClose);
        var leadHeight=(yCenter-top)/3;
        var data=this.LeadData;
        for(var i=data.DataOffset,j=0;i<data.Data.length && j<xPointCount;++i,++j)
        {
            var value=data.Data[i];
            if (!IFrameSplitOperator.IsNumber(value)) continue;
            if (value==0) continue;
            var x=this.ChartFrame.GetXFromIndex(j);

            if (max==null || max<value) max=value;
            if (min==null || min>value) min=value;

            aryLead.push({X:x, Value:value})
        }

        if (aryLead.length<=0) return;
        var maxValue=Math.max(Math.abs(max),Math.abs(min));

        for(var i in aryLead)
        {
            var item=aryLead[i];
            if (item.Value>0) this.Canvas.strokeStyle=this.UpColor;
            else this.Canvas.strokeStyle=this.DownColor;

            var y=yCenter-(item.Value*leadHeight/maxValue);
            var x=ToFixedPoint(item.X);
            this.Canvas.beginPath();
            if (isHScreen===true)
            {
                this.Canvas.moveTo(yCenter,x);
                this.Canvas.lineTo(y,x);
            }
            else
            { 
                this.Canvas.moveTo(x,yCenter);
                this.Canvas.lineTo(x,y);
            }
            this.Canvas.stroke();
        }
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        if (this.YClose==null) return range;
        if (!this.IsShow) return range;

        range.Min=this.YClose;
        range.Max=this.YClose;
        var data=this.Data;
        var isBeforeData=false;
        if (this.SourceData) 
        {
            data=this.SourceData;
            isBeforeData=true;
        }

        for(var i=data.DataOffset,j=0;i<data.Data.length && j<xPointCount;++i,++j)
        {
            var value=null;
            if (isBeforeData)
            {
                var item=data.Data[i];
                if (item.Before) value=item.Before.Close;
                else value=item.Close;
            }
            else
            {
                value=data.Data[i];
            }
            if (value==null) continue;

            if (range.Max==null) range.Max=value;
            if (range.Min==null) range.Min=value;

            if (range.Max<value) range.Max=value;
            if (range.Min>value) range.Min=value;
        }

        if (range.Max==this.YClose && range.Min==this.YClose)
        {
            range.Max=this.YClose+this.YClose*0.1;
            range.Min=this.YClose-this.YClose*0.1;
            return range;
        }

        var distance=Math.max(Math.abs(this.YClose-range.Max),Math.abs(this.YClose-range.Min));
        range.Max=this.YClose+distance;
        range.Min=this.YClose-distance;

        return range;
    }
}

//分钟线叠加 支持横屏
function ChartOverlayMinutePriceLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Color="rgb(65,105,225)";
    this.MainData;                  //主图数据
    this.MainYClose;                //主图股票的前收盘价
    this.SourceData;                //原始数据
    this.BeforeDataCount=0;
    this.IsBeforeData=false;

    this.ClassName="ChartOverlayMinutePriceLine";
    this.Title;
    this.Symbol;                    //叠加的股票代码
    this.YClose;               //叠加的股票前收盘
    this.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID;

    this.Draw=function()
    {
        if (!this.Data) return;
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen===true) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var minuteCount=this.ChartFrame.MinuteCount;

        var bFirstPoint=true;
        var drawCount=0;
        var xOffset=0;
        if (this.IsBeforeData) xOffset=1;   //集合竞价模式从第2个数据开始 第1个数据是9:25数据
        for(var i=this.Data.DataOffset+xOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i].Close;
            if (value==null) continue;
            var showValue=value/this.YClose*this.MainYClose;

            var x=this.ChartFrame.GetXFromIndex(j+this.BeforeDataCount);
            var y=this.ChartFrame.GetYFromData(showValue);

            if (bFirstPoint)
            {
                this.Canvas.strokeStyle=this.Color;
                this.Canvas.beginPath();
                if (isHScreen) this.Canvas.moveTo(y,x);
                else this.Canvas.moveTo(x,y);
                bFirstPoint=false;
            }
            else
            {
                if (isHScreen) this.Canvas.lineTo(y,x);
                else this.Canvas.lineTo(x,y);
            }

            ++drawCount;

            if (drawCount>=minuteCount) //上一天的数据和这天地数据线段要断开
            {
                bFirstPoint=true;
                this.Canvas.stroke();
                drawCount=0;
            }
        }

        if (drawCount>0) this.Canvas.stroke();
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        if (this.YClose==null) return range;

        range.Min=this.MainYClose;
        range.Max=this.MainYClose;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i].Close;
            if (value==null) continue;
            var value=value/this.YClose*this.MainYClose;
            if (range.Max==null) range.Max=value;
            if (range.Min==null) range.Min=value;

            if (range.Max<value) range.Max=value;
            if (range.Min>value) range.Min=value;
        }

        if (range.Max==this.MainYClose && range.Min==this.MainYClose)
        {
            range.Max=this.MainYClose+this.MainYClose*0.1;
            range.Min=this.MainYClose-this.MainYClose*0.1;
            return range;
        }

        var distance=Math.max(Math.abs(this.MainYClose-range.Max),Math.abs(this.MainYClose-range.Min));
        range.Max=this.MainYClose+distance;
        range.Min=this.MainYClose-distance;

        //JSConsole.Chart.Log(`[ChartOverlayMinutePriceLine::GetMaxMin] max=${range.Max} min=${range.Min}`);
        return range;
    }
}

//分钟信息地雷 支持横屏
function ChartMinuteInfo()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName="ChartMinuteInfo";
    this.Data=new Map()  //Map key=date-time, value=[{Date, Time, Title, Type, ID:}]
    this.SourceData;
    this.ChartMinutePrice;
    this.YClose;
    this.HQChartBorder;

    this.TextColor=g_JSChartResource.MinuteInfo.TextColor;
    this.Font=g_JSChartResource.MinuteInfo.Font;
    this.PointColor=g_JSChartResource.MinuteInfo.PointColor;
    this.LineColor=g_JSChartResource.MinuteInfo.LineColor;
    this.TextBGColor=g_JSChartResource.MinuteInfo.TextBGColor;
    this.PixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
    this.TextHeight=20;

    this.TextRectCache=[];
    this.InfoDrawCache=[];
    this.FrameBottom;
    this.FrameTop;
    this.FrameLeft;
    this.FrameRight;
    this.YOffset=5;
    this.IsHScreen=false;
    this.IsDrawFull=false;   //是否全屏画

    this.SetOption=function(option)
    {
        if (option.TextColor) this.TextColor=option.TextColor;
        if (option.TextBGColor) this.TextBGColor=option.TextBGColor;
        if (option.Font) this.Font=option.Font;
        if (option.PointColor) this.PointColor=option.PointColor;
        if (option.LineColor) this.LineColor=option.LineColor;
        if (option.TextHeight>0) this.TextHeight=option.TextHeight;
        if (option.IsDrawFull==true) this.IsDrawFull=true;
    }

    this.Draw=function()
    {
        if (!this.ChartMinutePrice) return;
        if (!this.Data || this.Data.size<=0) return;

        this.TextRectCache=[];
        this.InfoDrawCache=[];
        this.PixelTatio=GetDevicePixelRatio();
        this.YOffset=5*this.PixelTatio;
        this.IsHScreen=(this.ChartFrame.IsHScreen===true);
        
        var xPointCount=this.ChartFrame.XPointCount;
        var minuteCount=this.ChartFrame.MinuteCount;

        this.FrameBottom=this.ChartBorder.GetBottom();
        if (this.IsDrawFull && this.HQChartBorder) this.FrameBottom=this.HQChartBorder.GetBottom();
        this.FrameTop=this.ChartBorder.GetTop();
        this.FrameLeft=this.ChartBorder.GetLeft();
        this.FrameRight=this.ChartBorder.GetRight();
        if (this.IsHScreen)
        {
            this.FrameRight=this.ChartBorder.GetBottom();
            this.FrameLeft=this.ChartBorder.GetTop();
            this.FrameBottom=this.ChartBorder.GetLeft();
            this.FrameTop=this.ChartBorder.GetRight();
        }

        this.YClose=this.ChartMinutePrice.YClose;

        var data=this.ChartMinutePrice.Data;
        var isBeforeData=false;
        if (this.ChartMinutePrice.SourceData) 
        {
            data=this.ChartMinutePrice.SourceData;
            isBeforeData=true;
        }

        for(var i=data.DataOffset,j=0;i<data.Data.length && j<xPointCount;++i,++j)
        {
            var item=this.SourceData.Data[i];
            if (isBeforeData && item.Before) continue;
            if (!item) continue;

            var dateTime=item.DateTime;
            if (!this.Data.has(dateTime)) continue;
            if (this.IsHScreen) 
                this.CalcuateInfoHScreenPosition(this.Data.get(dateTime), j, item);
            else 
                this.CalcuateInfoPosition(this.Data.get(dateTime), j, item);
        }

        for(var i in this.InfoDrawCache)
        {
            var item=this.InfoDrawCache[i];
            this.DrawInfoLines(item);
        }

        for(var i in this.InfoDrawCache)
        {
            var item=this.InfoDrawCache[i];
            this.DrawInfoText(item);
        }

        this.TextRectCache=[];
        this.InfoDrawCache=[];
    }

    this.CalcuateInfoPosition=function(infoItem, index, minuteItem)
    {
        if (!infoItem  || !infoItem.Data || infoItem.Data.length<=0) return;

        var showItem=infoItem.Data[0];
        this.Canvas.font = this.Font;
        var textWidth=this.Canvas.measureText(showItem.Title).width+4*this.PixelTatio;
        var textHeight=this.TextHeight*this.PixelTatio;

        var x=this.ChartFrame.GetXFromIndex(index);
        var y;
        if (IFrameSplitOperator.IsNumber(showItem.Price)) y=this.ChartFrame.GetYFromData(showItem.Price);
        else y=this.ChartFrame.GetYFromData(minuteItem.Close);
        x=ToFixedPoint(x);

        var isDrawLeft=x<(this.FrameLeft+Math.abs(this.FrameLeft-this.FrameRight)/2);

        var ARRAY_OFFSET=[2,4,6,8,3,5,7];
        var offset=textHeight+ARRAY_OFFSET[index%ARRAY_OFFSET.length]*this.PixelTatio;
        var yData= 
        { 
            Y: 
            [
                {Value:y+(textHeight+this.YOffset), Offset: offset},
                {Value:y-(2*textHeight+this.YOffset), Offset:-offset}
            ]
        };

        if (minuteItem.Close<this.YClose) 
            yData.Y=yData.Y.reverse();
        
        var rtBorder={X:x, Y:null, Width:textWidth,Height:textHeight};
        if (!isDrawLeft) rtBorder.X-=rtBorder.Width;

        this.FixTextRect(rtBorder,yData);
        var InfoDrawItem={ Border:rtBorder, Start:{X:x,Y:y}, IsLeft:isDrawLeft, Title:showItem.Title };
        if (showItem.Color) InfoDrawItem.Color=showItem.Color;
        if (showItem.BGColor) InfoDrawItem.BGColor=showItem.BGColor;

        this.InfoDrawCache.push(InfoDrawItem);
        this.TextRectCache.push(rtBorder);
    }

    this.CalcuateInfoHScreenPosition=function(infoItem, index, minuteItem)
    {
        if (!infoItem  || !infoItem.Data || infoItem.Data.length<=0) return;

        var showItem=infoItem.Data[0];
        this.Canvas.font = this.Font;
        var textHeight=this.Canvas.measureText(showItem.Title).width+4*this.PixelTatio;
        var textWidth=this.TextHeight*this.PixelTatio;

        var y=this.ChartFrame.GetXFromIndex(index);
        var x;
        if (IFrameSplitOperator.IsNumber(showItem.Price)) x=this.ChartFrame.GetYFromData(showItem.Price);
        else x=this.ChartFrame.GetYFromData(minuteItem.Close);
        y=ToFixedPoint(y);

        var isDrawLeft=y<(this.FrameLeft+Math.abs(this.FrameLeft-this.FrameRight)/2);

        var ARRAY_OFFSET=[2,4,6,8,3,5,7];
        var offset=textWidth+ARRAY_OFFSET[index%ARRAY_OFFSET.length]*this.PixelTatio;
        var xData= 
        { 
            X: 
            [
                {Value:x+(textWidth+this.YOffset), Offset: offset},
                {Value:x-(2*textWidth+this.YOffset), Offset:-offset}
            ]
        };

        if (minuteItem.Close>this.YClose) 
            xData.X=xData.X.reverse();
        
        var rtBorder={X:null, Y:y, Width:textWidth,Height:textHeight};
        if (!isDrawLeft) rtBorder.Y-=rtBorder.Height;
        
        this.FixHScreenTextRect(rtBorder,xData);
        var InfoDrawItem={ Border:rtBorder, Start:{X:x,Y:y}, IsLeft:isDrawLeft, Title:showItem.Title };

        this.InfoDrawCache.push(InfoDrawItem);
        this.TextRectCache.push(rtBorder);
    }

    this.DrawInfoLines=function(item)
    {
        var rtBorder=item.Border;
        var isDrawLeft=item.IsLeft;
        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ToFixedPoint(item.Start.X),item.Start.Y);
        if (isDrawLeft) 
        {
            this.Canvas.lineTo(ToFixedPoint(item.Start.X),rtBorder.Y);
        }
        else 
        {
            if (this.IsHScreen) this.Canvas.lineTo(rtBorder.X,rtBorder.Y+rtBorder.Height);
            else this.Canvas.lineTo(ToFixedPoint(item.Start.X),rtBorder.Y);
        }
        this.Canvas.stroke();

        this.Canvas.fillStyle = this.PointColor;
        this.Canvas.beginPath();
        this.Canvas.arc(item.Start.X,item.Start.Y, 5, 0, 2 * Math.PI);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    this.DrawInfoText=function(item)
    {
        var rtBorder=item.Border;
        var x=rtBorder.X, y=rtBorder.Y;
        if (item.BGColor) this.Canvas.fillStyle=item.BGColor
        else this.Canvas.fillStyle=this.TextBGColor;
        this.Canvas.fillRect(x, y, rtBorder.Width,rtBorder.Height);

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.rect(x,y,rtBorder.Width,rtBorder.Height);
        this.Canvas.stroke();

        if (this.IsHScreen)
        {
            this.Canvas.save(); 
            this.Canvas.translate(rtBorder.X,rtBorder.Y);
            this.Canvas.rotate(90 * Math.PI / 180);
            x=0;y=0;
        }

        this.Canvas.textAlign = 'left'
        this.Canvas.textBaseline = 'middle';
        if (item.Color) this.Canvas.fillStyle=item.Color;
        else this.Canvas.fillStyle = this.TextColor;
        this.Canvas.font = this.Font;
        if (this.IsHScreen) this.Canvas.fillText(item.Title, x+2*this.PixelTatio, y-rtBorder.Width/2);
        else this.Canvas.fillText(item.Title, x+2*this.PixelTatio, y+rtBorder.Height/2);

        if (this.IsHScreen) this.Canvas.restore();
    }

    this.FixTextRect=function(rect,yData) 
    {
        for(var k in yData.Y)
        {
            var yItem=yData.Y[k];
            rect.Y=yItem.Value;

            var y;
            for(var j=0;j<10;++j)
            {
                var isOverlap=false;
                for(var i in this.TextRectCache)
                {
                    var item=this.TextRectCache[i];
                    if (this.IsOverlap(item, rect))
                    {
                        isOverlap=true;
                        break;
                    }
                }

                if (isOverlap==false) return;

                y=rect.Y;
                y+=yItem.Offset;
                if (y+rect.Height>this.FrameBottom || y<this.FrameTop) break;

                rect.Y=y;
            }
        }
    }

    this.FixHScreenTextRect=function(rect,xData) 
    {
        for(var k in xData.X)
        {
            var xItem=xData.X[k];
            rect.X=xItem.Value;

            var x;
            for(var j=0;j<10;++j)
            {
                var isOverlap=false;
                for(var i in this.TextRectCache)
                {
                    var item=this.TextRectCache[i];
                    if (this.IsOverlap(item, rect))
                    {
                        isOverlap=true;
                        break;
                    }
                }

                if (isOverlap==false) return;

                x=rect.X;
                x+=xItem.Offset;
                if (x+rect.Width<this.FrameBottom || x>this.FrameTop) break;

                rect.X=x;
            }
        }
    }

    this.IsOverlap=function(rc1, rc2)
    {
        if (rc1.X + rc1.Width  > rc2.X &&rc2.X + rc2.Width  > rc1.X &&rc1.Y + rc1.Height > rc2.Y &&rc2.Y + rc2.Height > rc1.Y)
            return true;
        else
            return false;
    }

    this.GetMaxMin=function()
    {
        var range={Min:null, Max:null};
        return range;
    }
}

//MACD森林线 支持横屏
function ChartMACD()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName="ChartMACD";
    this.UpColor=g_JSChartResource.UpBarColor;
    this.DownColor=g_JSChartResource.DownBarColor;

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (this.ChartFrame.IsHScreen===true)
        {
            this.HScreenDraw();
            return;
        }

        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;
        var lockRect=this.GetLockRect();
        if (lockRect) chartright=lockRect.Left;

        var bFirstPoint=true;
        var drawCount=0;
        var yBottom=this.ChartFrame.GetYFromData(0);
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            var xFix=parseInt(x.toString())+0.5;    //毛边修正
            this.Canvas.beginPath();
            this.Canvas.moveTo(xFix,yBottom);
            this.Canvas.lineTo(xFix,y);

            if (value>=0) this.Canvas.strokeStyle=this.UpColor;
            else this.Canvas.strokeStyle=this.DownColor;
            this.Canvas.stroke();
            this.Canvas.closePath();
        }
    }

    this.HScreenDraw=function()
    {
        var chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;
        var lockRect=this.GetLockRect();
        if (lockRect) chartright=lockRect.Top;

        var yBottom=this.ChartFrame.GetYFromData(0);
        
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;

            var x=this.ChartFrame.GetXFromIndex(j);
            var y=this.ChartFrame.GetYFromData(value);

            if (x>chartright) break;

            this.Canvas.beginPath();
            this.Canvas.moveTo(yBottom,ToFixedPoint(x));
            this.Canvas.lineTo(y,ToFixedPoint(x));

            if (value>=0) this.Canvas.strokeStyle=this.UpColor;
            else this.Canvas.strokeStyle=this.DownColor;
            this.Canvas.stroke();
            this.Canvas.closePath();
        }
    }
}

//柱子
function ChartBar()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName="ChartBar";
    this.UpBarColor=g_JSChartResource.UpBarColor;
    this.DownBarColor=g_JSChartResource.DownBarColor;

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        var xPointCount=this.ChartFrame.XPointCount;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;

        var bFirstPoint=true;
        var drawCount=0;
        var yBottom=this.ChartFrame.GetYFromData(0);
        if (dataWidth>=4)
        {
            yBottom=ToFixedRect(yBottom);   //调整为整数
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var value=this.Data.Data[i];
                if (value==null || value==0) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                if (right>chartright) break;

                var x=this.ChartFrame.GetXFromIndex(j);
                var y=this.ChartFrame.GetYFromData(value);


                if (value>0) this.Canvas.fillStyle=this.UpBarColor;
                else this.Canvas.fillStyle=this.DownBarColor;

                //高度调整为整数
                var height=ToFixedRect(Math.abs(yBottom-y));
                if(yBottom-y>0) y=yBottom-height;
                else y=yBottom+height;
                this.Canvas.fillRect(ToFixedRect(left),y,ToFixedRect(dataWidth),height);
            }
        }
        else    //太细了 直接画柱子
        {
            for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var value=this.Data.Data[i];
                if (value==null || value==0) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                if (right>chartright) break;

                var x=this.ChartFrame.GetXFromIndex(j);
                var y=this.ChartFrame.GetYFromData(value);

                if (value>0) this.Canvas.strokeStyle=this.UpBarColor;
                else this.Canvas.strokeStyle=this.DownBarColor;

                this.Canvas.beginPath();
                this.Canvas.moveTo(ToFixedPoint(x),y);
                this.Canvas.lineTo(ToFixedPoint(x),yBottom);
                this.Canvas.stroke();
            }
        }
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=0;
        range.Max=null;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (range.Max==null) range.Max=value;
            if (range.Max<value) range.Max=value;
        }

        return range;
    }
}
// 面积图
function ChartBand()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;
    this.IsDrawFirst = true;

    this.ClassName="ChartBand";
    this.FirstColor = g_JSChartResource.Index.LineColor[0];
    this.SecondColor = g_JSChartResource.Index.LineColor[1];

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var xPointCount=this.ChartFrame.XPointCount;
        var xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
        var x = 0;
        var y = 0;
        var y2 = 0;
        var firstlinePoints = [];
        var secondlinePoints = [];
        var lIndex = 0;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
        {
            var value=this.Data.Data[i];
            if (value==null || value.Value==null || value.Value2 == null) continue;
            x=this.ChartFrame.GetXFromIndex(j);
            y=this.ChartFrame.GetYFromData(value.Value);
            y2 = this.ChartFrame.GetYFromData(value.Value2);
            firstlinePoints[lIndex] = {x:x,y:y};
            secondlinePoints[lIndex] = {x:x,y:y2};
            lIndex++;
        }
        if (firstlinePoints.length > 1)
        {
            this.Canvas.save();
            this.Canvas.beginPath();
            for (var i = 0; i < firstlinePoints.length; ++i)
            {
                if (i == 0)
                    this.Canvas.moveTo(firstlinePoints[i].x, firstlinePoints[i].y);
                else
                    this.Canvas.lineTo(firstlinePoints[i].x, firstlinePoints[i].y);
            }  
            for (var j = secondlinePoints.length-1; j >= 0; --j)
            {
                this.Canvas.lineTo(secondlinePoints[j].x, secondlinePoints[j].y);
            }  
            this.Canvas.closePath();
            this.Canvas.clip();
            this.Canvas.beginPath();
            this.Canvas.moveTo(firstlinePoints[0].x, this.ChartBorder.GetBottom());
            for (var i = 0; i < firstlinePoints.length; ++i)
            {
                this.Canvas.lineTo(firstlinePoints[i].x, firstlinePoints[i].y);
            }
            this.Canvas.lineTo(firstlinePoints[firstlinePoints.length-1].x, this.ChartBorder.GetBottom());
            this.Canvas.closePath();
            this.Canvas.fillStyle = this.FirstColor;
            this.Canvas.fill();
            this.Canvas.beginPath();
            this.Canvas.moveTo(secondlinePoints[0].x, this.ChartBorder.GetBottom());
            for (var i = 0; i < secondlinePoints.length; ++i)
            {
                this.Canvas.lineTo(secondlinePoints[i].x, secondlinePoints[i].y);
            }
            this.Canvas.lineTo(secondlinePoints[secondlinePoints.length-1].x, this.ChartBorder.GetBottom());
            this.Canvas.closePath();
            this.Canvas.fillStyle = this.SecondColor;
            this.Canvas.fill();
            this.Canvas.restore();
        }
    }
    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={};
        range.Min=null;
        range.Max=null;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null || value.Value==null || value.Value2 == null) continue;
            var maxData = value.Value>value.Value2?value.Value:value.Value2;
            var minData = value.Value<value.Value2?value.Value:value.Value2;
            if (range.Max==null) 
                range.Max = maxData;
            else if (range.Max < maxData)
                range.Max = maxData;
            
            if (range.Min==null)
                range.Min = minData;
            else if (range.Min > minData)
                range.Min = minData; 
        }

        return range;
    }
}

// 通道面积图 支持横屏
function ChartChannel()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;
    this.IsDrawFirst = true;

    this.ClassName="ChartChannel";
    this.IsHScreen=false;   //是否是横屏
    this.PointCount=0;
    this.DataWidth=0;
    this.DistanceWidth=0;
    this.ChartRight=0;      //可以绘制的最右边
    this.LineColor='RGB(255,0,0)';
    this.LineDotted=[3,3];
    this.AreaColor='RGB(255,222,173)';
    this.LineWidth=1;

    this.CalculateData=function()   //把数据通过nul值分割开, 并计算坐标
    {
        var data=[];
        var lineData=[];
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<this.PointCount;++i,++j)
        {
            var item=this.Data.Data[i];
            if (!item || !IFrameSplitOperator.IsNumber(item.Value) || !IFrameSplitOperator.IsNumber(item.Value2))
            {
                if (lineData.length>0) 
                {
                    data.push(lineData);
                    lineData=[];    //创建新的一组数据
                }

                continue;
            }

            var x=this.ChartFrame.GetXFromIndex(j);
            if (x>this.ChartRight) break;
            var y=this.ChartFrame.GetYFromData(item.Value);
            var y2=this.ChartFrame.GetYFromData(item.Value2);

            lineData.push({X:x, Y:y,Y2:y2});
        }
        
        if (lineData.length>0) data.push(lineData);
        return data;
    }

    this.DrawArea=function(lineData)
    {
        if (lineData.length<=0) return;
        this.Canvas.beginPath();

        var drawCount=0;
        var firstItem=lineData[0];
        if (this.IsHScreen) this.Canvas.moveTo(firstItem.Y,firstItem.X);
        else this.Canvas.moveTo(firstItem.X,firstItem.Y);
        for(var i=1;i<lineData.length;++i)
        {
            var item=lineData[i];
            if (this.IsHScreen) this.Canvas.lineTo(item.Y,item.X);
            else this.Canvas.lineTo(item.X,item.Y);
            ++drawCount;
        }

        for(var i=lineData.length-1;i>=0;--i)
        {
            var item=lineData[i];
            if (this.IsHScreen) this.Canvas.lineTo(item.Y2,item.X);
            else this.Canvas.lineTo(item.X,item.Y2);
            ++drawCount;
        }

        this.Canvas.closePath();
        this.Canvas.fillStyle = this.AreaColor;
        this.Canvas.fill();
    }

    this.DrawLine=function(lineData)
    {
        this.Canvas.strokeStyle=this.LineColor;
        for(var k=0;k<2;++k)
        {
            var bFirstPoint=true;
            var drawCount=0;
            for(var i=0;i<lineData.length;++i)
            {
                var item=lineData[i];

                if (bFirstPoint)
                {
                    this.Canvas.beginPath();
                    if (this.IsHScreen) this.Canvas.moveTo(k===0?item.Y:item.Y2,item.X);
                    else this.Canvas.moveTo(item.X, k===0?item.Y:item.Y2 );
                    bFirstPoint=false;
                }
                else
                {
                    if (this.IsHScreen) this.Canvas.lineTo(k===0?item.Y:item.Y2,item.X);
                    else this.Canvas.lineTo(item.X,k===0?item.Y:item.Y2);
                }

                ++drawCount;
            }
            if (drawCount>0) this.Canvas.stroke();
        }
    }

    this.Draw=function()
    {
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        this.IsHScreen=(this.ChartFrame.IsHScreen===true);
        this.PointCount=this.ChartFrame.XPointCount;
        this.DataWidth=this.ChartFrame.DataWidth;
        this.DistanceWidth=this.ChartFrame.DistanceWidth;
        if (this.IsHScreen) this.ChartRight=this.ChartBorder.GetBottom();
        else this.ChartRight=this.ChartBorder.GetRight();

        var drawData=this.CalculateData();
        if (!drawData || drawData.length<=0) return;

        this.Canvas.save();
        this.Canvas.lineWidth=this.LineWidth*GetDevicePixelRatio();
        this.Canvas.setLineDash(this.LineDotted);   //虚线
        for(var i=0;i<drawData.length;++i)
        {
            var lineData=drawData[i];
            this.DrawArea(lineData);
            this.DrawLine(lineData);
        }
        this.Canvas.restore();
    }

    this.GetMaxMin=function()
    {
        var xPointCount=this.ChartFrame.XPointCount;
        var range={Min:null, Max:null};
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null || value.Value==null || value.Value2 == null) continue;
            var maxData = value.Value>value.Value2?value.Value:value.Value2;
            var minData = value.Value<value.Value2?value.Value:value.Value2;
            if (range.Max==null) 
                range.Max = maxData;
            else if (range.Max < maxData)
                range.Max = maxData;
            
            if (range.Min==null)
                range.Min = minData;
            else if (range.Min > minData)
                range.Min = minData; 
        }

        return range;
    }
}

//填充背景 TODO:横屏
function ChartBackground()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Color=null;
    this.ColorAngle=0;  //0 竖向 1 横向

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (!this.Color) return;
        if (this.Color.length<=0) return;

        if (this.Color.length==2)
        {
            if (this.ColorAngle==0)
            {
                var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() };
                var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() };
            }
            else
            {
                var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() };
                var ptEnd={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() };
            }

            let gradient = this.Canvas.createLinearGradient(ptStart.X,ptStart.Y, ptEnd.X,ptEnd.Y);
            gradient.addColorStop(0, this.Color[0]);
            gradient.addColorStop(1, this.Color[1]);
            this.Canvas.fillStyle=gradient;
        }
        else if (this.Color.length==1)
        {
            this.Canvas.fillStyle=this.Color[0];
        }
        else
        {
            return;
        }

        var left=this.ChartBorder.GetLeft();
        var top=this.ChartBorder.GetTopEx();
        var width=this.ChartBorder.GetWidth();
        var height=this.ChartBorder.GetHeightEx();
        this.Canvas.fillRect(left, top,width, height);
    }
}

//画矩形
function ChartRectangle()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Color=[];
    this.Rect;
    this.BorderColor=g_JSChartResource.FrameBorderPen;

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (!this.Color || !this.Rect) return;
        if (this.Color.length<=0) return;

        this.Canvas.strokeStyle=this.BorderColor;
        var bFill=false;
        if (this.Color.length==2)
        {
            /*  TODO 渐变下次做吧
            if (this.ColorAngle==0)
            {
                var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() };
                var ptEnd={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetBottomEx() };
            }
            else
            {
                var ptStart={ X:this.ChartBorder.GetLeft(), Y:this.ChartBorder.GetTopEx() };
                var ptEnd={ X:this.ChartBorder.GetRight(), Y:this.ChartBorder.GetTopEx() };
            }

            let gradient = this.Canvas.createLinearGradient(ptStart.X,ptStart.Y, ptEnd.X,ptEnd.Y);
            gradient.addColorStop(0, this.Color[0]);
            gradient.addColorStop(1, this.Color[1]);
            this.Canvas.fillStyle=gradient;
            */

            this.Canvas.fillStyle=this.Color[0];
            bFill=true;
        }
        else if (this.Color.length==1)
        {
            if (this.Color[0])
            {
                this.Canvas.fillStyle=this.Color[0];
                bFill=true;
            }
        }
        else
        {
            return;
        }

        var chartWidth=this.ChartBorder.GetWidth();
        var chartHeight=this.ChartBorder.GetHeightEx();
        var left=this.Rect.Left/1000*chartWidth;
        var top=this.Rect.Top/1000*chartHeight;
        var right=this.Rect.Right/1000*chartWidth;
        var bottom=this.Rect.Bottom/1000*chartHeight;

        left=this.ChartBorder.GetLeft()+left
        top=this.ChartBorder.GetTopEx()+top;
        right=this.ChartBorder.GetLeft()+right;
        bottom=this.ChartBorder.GetTopEx()+bottom;
        var width=Math.abs(left-right);
        var height=Math.abs(top-bottom);
        if (bFill) this.Canvas.fillRect(left, top,width, height);
        this.Canvas.rect(ToFixedPoint(left), ToFixedPoint(top),ToFixedRect(width), ToFixedRect(height));
        this.Canvas.stroke();
    }
}

// 文字+线段输出
function ChartTextLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Text;  //Text=内容 Color
    this.Line;  //Type=线段类型 0=不画 1=直线 2=虚线, Color
    this.Price;

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (!this.Text || !this.Line || !IFrameSplitOperator.IsNumber(this.Price)) return;

        this.IsHScreen=(this.ChartFrame.IsHScreen===true);
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var bottom=this.ChartBorder.GetBottomEx();
        var top=this.ChartBorder.GetTopEx();
        var y=this.ChartFrame.GetYFromData(this.Price);
        var textWidth=0;
        if (this.Text.Title)
        {
            var x=left+2*GetDevicePixelRatio();
            var yText=y;
            this.Canvas.textAlign = 'left';
            this.Canvas.textBaseline = 'middle';
            var offsetY=8*GetDevicePixelRatio();
            if (y-offsetY<top) 
            {
                this.Canvas.textBaseline='top';
                yText=top;
            }
            else if (y+offsetY>bottom) 
            {
                this.Canvas.textBaseline='bottom';
                yText=bottom;
            }
            
            this.Canvas.fillStyle = this.Text.Color;
            this.Canvas.font = this.Text.Font;
            this.Canvas.fillText(this.Text.Title, x, yText);
            textWidth=this.Canvas.measureText(this.Text.Title).width+4*GetDevicePixelRatio();
        }

        if (this.Line.Type>0)
        {
            if (this.Line.Type==2)  //虚线
            {
                this.Canvas.save();
                this.Canvas.setLineDash([3,5]);   //虚线
            }

            var x=left+textWidth;
            this.Canvas.strokeStyle=this.Line.Color;
            this.Canvas.beginPath();
            this.Canvas.moveTo(x,ToFixedPoint(y));
            this.Canvas.lineTo(right,ToFixedPoint(y));
            this.Canvas.stroke();

            if (this.Line.Type==2)
            {
                this.Canvas.restore();
            }
        }

    }

    this.GetMaxMin=function()
    {
        var range={Min:null, Max:null};
        if (IFrameSplitOperator.IsNumber(this.Price))
        {
            range.Min=this.Price;
            range.Max=this.Price;
        }
        
        return range;
    }
}

// 线段集合  支持横屏
function ChartMultiLine()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;
    
    this.Lines=[];   // [ {Point:[ {Index, Value }, ], Color: }, ] 
    this.IsHScreen=false;

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (!this.Data || this.Data.length<=0) return;

        this.IsHScreen=(this.ChartFrame.IsHScreen===true);
        var xPointCount=this.ChartFrame.XPointCount;
        var offset=this.Data.DataOffset;

        var drawLines=[];
        for(var i in this.Lines)
        {
            var line=this.Lines[i];
            var drawPoints={ Point:[], Color:line.Color };
            if (line.BGColor) drawPoints.BGColor=line.BGColor;
            for(var j in line.Point)
            {
                var point=line.Point[j];
                if (!IFrameSplitOperator.IsNumber(point.Index)) continue;

                var index=point.Index-offset;
                if (index>=0 && index<xPointCount)
                {
                    var x=this.ChartFrame.GetXFromIndex(index);
                    var y=this.ChartFrame.GetYFromData(point.Value);
                    drawPoints.Point.push({X:x, Y:y, End:false});
                }
                else
                {
                    if (drawPoints.Point.length>0) drawPoints.Point[drawPoints.Point.length-1].End=true;  //点断开
                }
            }

            if (drawPoints.Point.length>=2) drawLines.push(drawPoints)
        }

        for(var i in drawLines)
        {
            var item=drawLines[i];
            this.DrawLine(item);
        }
    }

    this.DrawLine=function(line)
    {
        if (line.BGColor)   //背景色
        {
            this.Canvas.fillStyle=line.BGColor;
            for(var i in line.Point)
            {
                var item=line.Point[i];
                if (i==0)
                {
                    this.Canvas.beginPath();
                    if (this.IsHScreen) this.Canvas.moveTo(item.Y,item.X);
                    else this.Canvas.moveTo(item.X,item.Y);
                }
                else
                {
                    if (this.IsHScreen) this.Canvas.lineTo(item.Y,item.X);
                    else this.Canvas.lineTo(item.X,item.Y);
                }
            }
            this.Canvas.closePath();
            this.Canvas.fill();
        }

        this.Canvas.strokeStyle=line.Color;
        var drawCount=0;
        for(var i in line.Point)
        {
            var item=line.Point[i];
            if (drawCount==0)
            {
                this.Canvas.beginPath();
                if (this.IsHScreen) this.Canvas.moveTo(item.Y,item.X);
                else this.Canvas.moveTo(item.X,item.Y);
                ++drawCount;
            }
            else
            {
                if (this.IsHScreen) this.Canvas.lineTo(item.Y,item.X);
                else this.Canvas.lineTo(item.X,item.Y);
                ++drawCount;
            }

            if (item.End==true) //点断了 要重新画
            {
                if (drawCount>0) this.Canvas.stroke();
                drawCount=0;
            }
        }

        if (drawCount>0) this.Canvas.stroke();
    }

    this.GetMaxMin=function()
    {
        var range={ Min:null, Max:null };
        var xPointCount=this.ChartFrame.XPointCount;
        var start=this.Data.DataOffset;
        var end=start+xPointCount;

        for(var i in this.Lines)
        {
            var line=this.Lines[i];
            for(var j in line.Point)
            {
                var point=line.Point[j];
                if (point.Index>=start && point.Index<end)
                {
                    if (range.Max==null) range.Max=point.Value;
                    else if (range.Max<point.Value) range.Max=point.Value;
                    if (range.Min==null) range.Min=point.Value;
                    else if (range.Min>point.Value) range.Min=point.Value;
                }
            }
        }

        return range;
    }
}

// 多文本集合 支持横屏
function ChartMultiText()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Texts=[];  //[ {Index:, Value:, Text:, Color:, Font: , Baseline:} ]
    this.Font=g_JSChartResource.DefaultTextFont;
    this.Color=g_JSChartResource.DefaultTextColor;
    this.IsHScreen=false;   //是否横屏

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (!this.Data || this.Data.length<=0) return;
        if (!this.Texts) return;

        this.IsHScreen=(this.ChartFrame.IsHScreen===true);
        var xPointCount=this.ChartFrame.XPointCount;
        var offset=this.Data.DataOffset;
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();

        if (this.IsHScreen)
        {
            left=this.ChartBorder.GetTop();
            right=this.ChartBorder.GetBottom();
        }

        for(var i in this.Texts)
        {
            var item=this.Texts[i];

            if (!item.Text) continue;
            if (!IFrameSplitOperator.IsNumber(item.Index)) continue;

            var index=item.Index-offset;
            if (index>=0 && index<xPointCount)
            {
                var x=this.ChartFrame.GetXFromIndex(index);
                var y=this.ChartFrame.GetYFromData(item.Value);

                if (item.Color)  this.Canvas.fillStyle = item.Color;
                else this.Canvas.fillStyle = this.Color;
                if (item.Font) this.Canvas.font = item.Font;
                else this.Canvas.font=this.Font;

                var textWidth=this.Canvas.measureText(item.Text).width;
                this.Canvas.textAlign='center';
                if (x+textWidth/2>=right) 
                {
                    this.Canvas.textAlign='right';
                    x=right;
                }
                else if (x-textWidth/2<left)
                {
                    this.Canvas.textAlign = 'left';
                    x=left;
                }
                if (item.Baseline==1) this.Canvas.textBaseline='top';
                else if (item.Baseline==2) this.Canvas.textBaseline='bottom';
                else this.Canvas.textBaseline = 'middle';

                if (this.IsHScreen)
                {
                    this.Canvas.save(); 
                    this.Canvas.translate(y, x);
                    this.Canvas.rotate(90 * Math.PI / 180);
                    this.Canvas.fillText(item.Text,0,0);
                    this.Canvas.restore();
                }
                else
                {
                    this.Canvas.fillText(item.Text, x, y);
                }
            }
        }
    }

    this.GetMaxMin=function()
    {
        var range={ Min:null, Max:null };
        if (!this.Texts) return range;

        var xPointCount=this.ChartFrame.XPointCount;
        var start=this.Data.DataOffset;
        var end=start+xPointCount;

        for(var i in this.Texts)
        {
            var item=this.Texts[i];
            if (item.Index>=start && item.Index<end)
            {
                if (range.Max==null) range.Max=item.Value;
                else if (range.Max<item.Value) range.Max=item.Value;
                if (range.Min==null) range.Min=item.Value;
                else if (range.Min>item.Value) range.Min=item.Value;
            }
        }

        return range;
    }
}

// 图标集合 支持横屏
function ChartMultiSVGIcon()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Icon;  //[ {Index:, Value:, Symbol:, Color:, Baseline:} ]
    this.Family;
    this.Color=g_JSChartResource.DefaultTextColor;
    this.IsHScreen=false;

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (!this.Data || this.Data.length<=0) return;
        if (!this.Family || !this.Icon) return;

        this.IsHScreen=(this.ChartFrame.IsHScreen===true);
        var xPointCount=this.ChartFrame.XPointCount;
        var offset=this.Data.DataOffset;
        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        this.DataWidth=this.ChartFrame.DataWidth;
        this.DistanceWidth=this.ChartFrame.DistanceWidth;
        if (this.IsHScreen)
        {
            left=this.ChartBorder.GetTop();
            right=this.ChartBorder.GetBottom();
        }

        var fontSize=(this.DataWidth+this.DistanceWidth)-2;
        if (fontSize<8) fontSize=8;
        this.Canvas.font=fontSize+'px '+this.Family;

        for(var i in this.Icon)
        {
            var item=this.Icon[i];
            if (!IFrameSplitOperator.IsNumber(item.Index)) continue;

            var index=item.Index-offset;
            if (index>=0 && index<xPointCount)
            {
                var x=this.ChartFrame.GetXFromIndex(index);
                var y=this.ChartFrame.GetYFromData(item.Value);

                if (item.Color)  this.Canvas.fillStyle = item.Color;
                else this.Canvas.fillStyle = this.Color;

                var textWidth=this.Canvas.measureText(item.Text).width;
                this.Canvas.textAlign='center';
                if (x+textWidth/2>=right) 
                {
                    this.Canvas.textAlign='right';
                    x+=this.DataWidth/2;
                }
                else if (x-textWidth/2<left)
                {
                    this.Canvas.textAlign = 'left';
                    x-=this.DataWidth/2;
                }
                if (item.Baseline==1) this.Canvas.textBaseline='top';
                else if (item.Baseline==2) this.Canvas.textBaseline='bottom';
                else this.Canvas.textBaseline = 'middle';
                if (this.IsHScreen)
                {
                    this.Canvas.save(); 
                    this.Canvas.translate(y, x);
                    this.Canvas.rotate(90 * Math.PI / 180);
                    this.Canvas.fillText(item.Symbol,0,0);
                    this.Canvas.restore();
                }
                else
                {
                    this.Canvas.fillText(item.Symbol, x, y);
                }
            }
        }
    }

    this.GetMaxMin=function()
    {
        var range={ Min:null, Max:null };
        if (!this.Texts) return range;

        var xPointCount=this.ChartFrame.XPointCount;
        var start=this.Data.DataOffset;
        var end=start+xPointCount;

        for(var i in this.Icon)
        {
            var item=this.Icon[i];
            if (item.Index>=start && item.Index<end)
            {
                if (range.Max==null) range.Max=item.Value;
                else if (range.Max<item.Value) range.Max=item.Value;
                if (range.Min==null) range.Min=item.Value;
                else if (range.Min>item.Value) range.Min=item.Value;
            }
        }

        return range;
    }
}

//锁  支持横屏
function ChartLock()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName="ChartLock";
    this.WidthDiv = 0.2;  // 框子宽度占比
    this.LockCount = 20; // 锁最新的几个数据
    this.BGColor = g_JSChartResource.LockBGColor;
    this.TextColor = g_JSChartResource.LockTextColor;
    this.Font = g_JSChartResource.DefaultTextFont;
    this.Title = '🔒开通权限';
    this.LockRect=null; //上锁区域
    this.LockID;        //锁ID
    this.Callback;      //回调
    this.IndexName;     //指标名字
    this.MinWidth=null;  //最小宽度

    this.Draw=function(isDraw)
    {
        this.LockRect=null;
        if (this.NotSupportMessage)
        {
            this.DrawNotSupportmessage();
            return;
        }

        if (this.ChartFrame.IsHScreen===true)
        {
            this.HScreenDraw(isDraw);
            return;
        }

        var xOffset = this.ChartBorder.GetRight();
        var lOffsetWidth = 0;
        if (this.ChartFrame.Data != null)
        {
            var dataWidth=this.ChartFrame.DataWidth;
            var distanceWidth=this.ChartFrame.DistanceWidth;
            xOffset=this.ChartBorder.GetLeft()+distanceWidth/2.0+2.0;
            var chartright=this.ChartBorder.GetRight();
            var xPointCount=this.ChartFrame.XPointCount;
            for(var i=this.ChartFrame.Data.DataOffset,j=0;i<this.ChartFrame.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var data=this.ChartFrame.Data.Data[i];
                if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                if (right>chartright) break;
            }
            lOffsetWidth = (dataWidth + distanceWidth) * this.LockCount;    
        }
        if (lOffsetWidth == 0)
        {
            lOffsetWidth = (xOffset - this.ChartBorder.GetLeft()) * this.WidthDiv;
        }
        var lLeft = xOffset - lOffsetWidth;
        if (lLeft < this.ChartBorder.GetLeft())
            lLeft = this.ChartBorder.GetLeft();
        var lHeight = this.ChartBorder.GetBottom() - this.ChartBorder.GetTop();
        var lWidth = this.ChartBorder.GetRight() - lLeft;

        if (this.MinWidth>10 && lWidth<this.MinWidth) 
        {
            lWidth=this.MinWidth;
            lLeft=this.ChartBorder.GetRight()-lWidth;
            if (lLeft < this.ChartBorder.GetLeft()) lLeft = this.ChartBorder.GetLeft();
        }

        if (isDraw)
        {
            this.Canvas.fillStyle = this.BGColor;
            this.Canvas.fillRect(lLeft, this.ChartBorder.GetTop(), lWidth, lHeight);
            var xCenter = lLeft + lWidth / 2;
            var yCenter = this.ChartBorder.GetTop() + lHeight / 2;
            this.Canvas.textAlign = 'center';
            this.Canvas.textBaseline = 'middle';
            this.Canvas.fillStyle = this.TextColor;
            this.Canvas.font = this.Font;
            this.Canvas.fillText(this.Title, xCenter, yCenter);
        }

        this.LockRect={Left:lLeft,Top:this.ChartBorder.GetTop(),Width:lWidth,Heigh:lHeight};    //保存上锁区域
    }

    this.HScreenDraw=function(isDraw)
    {
        var xOffset = this.ChartBorder.GetBottom();

        var lOffsetWidth = 0;
        if (this.ChartFrame.Data != null)
        {
            var dataWidth=this.ChartFrame.DataWidth;
            var distanceWidth=this.ChartFrame.DistanceWidth;
            xOffset=this.ChartBorder.GetTop()+distanceWidth/2.0+2.0;
            var chartright=this.ChartBorder.GetBottom();
            var xPointCount=this.ChartFrame.XPointCount;
            //求最后1个数据的位置
            for(var i=this.ChartFrame.Data.DataOffset,j=0;i<this.ChartFrame.Data.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
            {
                var data=this.ChartFrame.Data.Data[i];
                if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

                var left=xOffset;
                var right=xOffset+dataWidth;
                if (right>chartright) break;
            }
            lOffsetWidth = (dataWidth + distanceWidth) * this.LockCount;    
        }
        if (lOffsetWidth == 0)
        {
            lOffsetWidth = (xOffset - this.ChartBorder.GetTop()) * this.WidthDiv;
        }

        var lLeft = xOffset - lOffsetWidth;
        if (lLeft < this.ChartBorder.GetTop()) lLeft = this.ChartBorder.GetTop();
        var lHeight =  this.ChartBorder.GetRight()-this.ChartBorder.GetLeft();
        var lWidth = this.ChartBorder.GetBottom() - lLeft;
        this.Canvas.fillStyle = this.BGColor;
        this.Canvas.fillRect(this.ChartBorder.GetLeft(), lLeft,lHeight,lWidth);

        var xCenter = this.ChartBorder.GetLeft() + lHeight / 2;
        var yCenter = lLeft + lWidth / 2;
        this.Canvas.save(); 
        this.Canvas.translate(xCenter, yCenter);
        this.Canvas.rotate(90 * Math.PI / 180);
        this.Canvas.textAlign = 'center';
        this.Canvas.textBaseline = 'middle';
        this.Canvas.fillStyle = this.TextColor;
        this.Canvas.font = this.Font;
        this.Canvas.fillText(this.Title, 0, 0);
        this.Canvas.restore();

        this.LockRect={Left:this.ChartBorder.GetLeft(),Top:lLeft,Width:lHeight,Heigh:lWidth};    //保存上锁区域
    }

    //x,y是否在上锁区域
    this.GetTooltipData=function(x,y,tooltip)
    {
        if (this.LockRect==null) return false;

        this.Canvas.beginPath();
        this.Canvas.rect(this.LockRect.Left,this.LockRect.Top,this.LockRect.Width,this.LockRect.Heigh);
        if (this.Canvas.isPointInPath(x,y))
        {
            tooltip.Data={ ID:this.LockID, Callback:this.Callback, IndexName:this.IndexName };
            tooltip.ChartPaint=this;
            return true;
        }
        
        return false;
    }
}

//买卖盘
function ChartBuySell()
{
    this.newMethod=ChartSingleText;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName="ChartBuySell";
    this.TextFont=g_JSChartResource.KLineTrain.Font;                //"bold 14px arial";           //买卖信息字体
    this.LastDataIcon=g_JSChartResource.KLineTrain.LastDataIcon; //{Color:'rgb(0,0,205)',Text:'↓'};
    this.BuyIcon=g_JSChartResource.KLineTrain.BuyIcon; //{Color:'rgb(0,0,205)',Text:'B'};
    this.SellIcon=g_JSChartResource.KLineTrain.SellIcon; //{Color:'rgb(0,0,205)',Text:'S'};
    this.BuySellData=new Map();   //{Date:日期*10000+时间, Op:买/卖 0=buy 1=sell}
    this.LastData={}; //当前屏最后一个数据
    this.IconFont=g_JSChartResource.KLineTrain.IconFont;

    this.Draw=function()
    {
        if (!this.Data || !this.Data.Data) return;

        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var dataWidth=this.ChartFrame.DataWidth;
        var distanceWidth=this.ChartFrame.DistanceWidth;
        var chartright=this.ChartBorder.GetRight();
        if (isHScreen===true) chartright=this.ChartBorder.GetBottom();
        var xPointCount=this.ChartFrame.XPointCount;

        if (this.IconFont)
        {
            var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
            var iconSize=dataWidth+distanceWidth;
            var minIconSize=18*pixelTatio;
            if (iconSize<minIconSize) iconSize=minIconSize;
            this.Canvas.font=iconSize+'px '+this.IconFont.Family;
        }
        else
        {
            this.Canvas.font=this.TextFont;
        }
        
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var value=this.Data.Data[i];
            if (value==null) continue;
            if (x>chartright) break;

            this.LastData={ID:j,Data:value};
            
            var key=value.Date*10000;
            if (IFrameSplitOperator.IsNumber(value.Time)) key+=value.Time;
            if (!this.BuySellData.has(key)) continue;
            var bsItem=this.BuySellData.get(key);
            var x=this.ChartFrame.GetXFromIndex(j);
            var yHigh=this.ChartFrame.GetYFromData(value.High);
            var yLow=this.ChartFrame.GetYFromData(value.Low);

            if (bsItem.Op==0)   //买 标识在最低价上
            {
                this.Canvas.textAlign='center';
                this.Canvas.textBaseline='top';
                if (this.IconFont)
                {
                    this.Canvas.fillStyle=this.IconFont.Buy.Color
                    this.DrawText(this.IconFont.Buy.Text,x,yLow,isHScreen);
                }
                else
                {
                    this.Canvas.fillStyle=this.BuyIcon.Color;
                    this.DrawText(this.BuyIcon.Text,x,yLow,isHScreen);
                }
            }
            else    //卖 标识在最高价上
            {
                this.Canvas.textAlign='center';
                this.Canvas.textBaseline='bottom';
                if (this.IconFont)
                {
                    this.Canvas.fillStyle=this.IconFont.Sell.Color
                    this.DrawText(this.IconFont.Sell.Text,x,yHigh,isHScreen);
                }
                else
                {
                    this.Canvas.fillStyle=this.SellIcon.Color;
                    this.DrawText(this.SellIcon.Text,x,yHigh,isHScreen);
                }
            }
        }

        //最后一个位置
        var x=this.ChartFrame.GetXFromIndex(this.LastData.ID);
        var yHigh=this.ChartFrame.GetYFromData(this.LastData.Data.High);
        this.Canvas.textAlign='center';
        this.Canvas.textBaseline='bottom';
        if (this.IconFont)
        {
            this.Canvas.fillStyle=this.IconFont.Last.Color
            this.DrawText(this.IconFont.Last.Text,x,yHigh,isHScreen);
        }
        else
        {
            this.Canvas.fillStyle=this.LastDataIcon.Color;
            this.Canvas.font=this.TextFont;
            this.DrawText(this.LastDataIcon.Text,x,yHigh,isHScreen);
        }
    }
}

/*
    饼图
*/
function ChartPie()
{   
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Radius = 100; //半径默认值
    this.Distance = 30; //指示线超出圆饼的距离
    this.txtLine = 20; // 文本下划线
    this.paddingX = 20 / 3;// 设置文本的移动
    
    // return;
    this.Draw=function()
    {
        if (!this.Data || !this.Data.Data || !(this.Data.Data.length>0)) return this.DrawEmptyData();

        let left=this.ChartBorder.GetLeft();
        let right=this.ChartBorder.GetRight();
        let top=this.ChartBorder.GetTop();
        let bottom=this.ChartBorder.GetBottom();
        let width=this.ChartBorder.GetWidth();
        let height=this.ChartBorder.GetHeight();

        if(isNaN(this.Radius)){
            let str = this.Radius.replace("%","");
            str = str/100;
            if(width >= height){
                this.Radius = str*height;
            }
            if(width < height) this.Radius = str*width;
        }


        this.Canvas.save();
        this.Canvas.translate(width/2,height/2);

        let totalValue=0;   //求和
        for(let i in this.Data.Data)
        {
            totalValue += this.Data.Data[i].Value;
        }
        let start = 0;
        let end = 0;
        //画饼图
        for(let i in this.Data.Data)
        {
            let item =this.Data.Data[i];
            let rate=(item.Value/totalValue).toFixed(2); //占比
            //JSConsole.Chart.Log('[ChartPie::Draw]', i, rate, item);

            // 绘制扇形
            this.Canvas.beginPath();
            this.Canvas.moveTo(0,0);

            end += rate*2*Math.PI;//终止角度
            this.Canvas.strokeStyle = "white";
            this.Canvas.fillStyle = item.Color;
            this.Canvas.arc(0,0,this.Radius,start,end);
            this.Canvas.fill();
            this.Canvas.closePath();
            this.Canvas.stroke();
            
            // 绘制直线
            this.Canvas.beginPath();
            this.Canvas.strokeStyle = item.Color;
            this.Canvas.moveTo(0,0);
            let x = (this.Radius + this.Distance)*Math.cos(end- (end-start)/2);
            let y = (this.Radius + this.Distance)*Math.sin(end - (end-start)/2);
            this.Canvas.lineTo(x,y);
            // JSConsole.Chart.Log(x,y,"xy")
            
            // 绘制横线
            let txtLine = this.txtLine;
            let paddingX = this.paddingX;
            this.Canvas.textAlign = 'left';
            if( end - (end-start)/2 < 1.5*Math.PI && end - (end-start)/2 > 0.5*Math.PI ){
                
                txtLine = - this.txtLine;
                paddingX = - this.paddingX;
                this.Canvas.textAlign = 'right';
            }
            this.Canvas.lineTo( x + txtLine, y );
            this.Canvas.stroke();

             // 写文字
             if(item.Text){
                 this.Canvas.fillText( item.Text, x + txtLine + paddingX, y );
             }else{
                 let text = `${item.Name}:${item.Value}`;
                 this.Canvas.fillText( text, x + txtLine + paddingX, y );
             }
            

            start += rate*2*Math.PI;//起始角度
        }

        this.Canvas.restore();
    }

    //空数据
    this.DrawEmptyData=function()
    {
        JSConsole.Chart.Log('[ChartPie::DrawEmptyData]')
    }
}


/*
    雷达图
*/
function ChartRadar()
{
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.BorderPoint=[];    //边框点
    this.DataPoint=[];      //数据点
    this.CenterPoint={};
    this.StartAngle=0;
    this.Color='rgb(198,198,198)';
    this.AreaColor='rgba(242,154,118,0.4)';    //面积图颜色
    this.AreaLineColor='rgb(242,154,118)';
    this.TitleFont=24*GetDevicePixelRatio()+'px 微软雅黑';
    this.TitleColor='rgb(102,102,102)';
    this.BGColor = ['rgb(255,255,255)', 'rgb(224,224,224)']//背景色

    this.DrawBorder=function()  //画边框
    {
        if (this.BorderPoint.length<=0) return;

        this.Canvas.font=this.TitleFont;
        this.Canvas.strokeStyle = this.Color;
        const aryBorder=[1,0.8,0.6,0.4,0.2];
        for (let j in aryBorder)
        {
            var rate = aryBorder[j];
            var isFirstDraw=true;
            for(let i in this.BorderPoint)
            {
                var item=this.BorderPoint[i];
                item.X = this.CenterPoint.X + item.Radius * Math.cos(item.Angle * Math.PI / 180) * rate;
                item.Y = this.CenterPoint.Y + item.Radius * Math.sin(item.Angle * Math.PI / 180) * rate;
                if (isFirstDraw)
                {
                    this.Canvas.beginPath();
                    this.Canvas.moveTo(item.X,item.Y);
                    isFirstDraw=false;
                }
                else
                {
                    this.Canvas.lineTo(item.X,item.Y);
                }
            }

            this.Canvas.closePath();
            this.Canvas.stroke();
            this.Canvas.fillStyle = this.BGColor[j%2==0?0:1];
            this.Canvas.fill();
        }

        this.Canvas.beginPath();
        for(let i in this.BorderPoint)
        {
            var item=this.BorderPoint[i];
            item.X = this.CenterPoint.X + item.Radius * Math.cos(item.Angle * Math.PI / 180);
            item.Y = this.CenterPoint.Y + item.Radius * Math.sin(item.Angle * Math.PI / 180);
            this.Canvas.moveTo(this.CenterPoint.X,this.CenterPoint.Y);
            this.Canvas.lineTo(item.X,item.Y);
            this.DrawText(item);
        }
        this.Canvas.stroke();
    }

    this.DrawArea=function()
    {
        if (!this.DataPoint || this.DataPoint.length<=0) return;

        this.Canvas.fillStyle = this.AreaColor;
        this.Canvas.strokeStyle = this.AreaLineColor;
        this.Canvas.beginPath();
        var isFirstDraw=true;
        for(let i in this.DataPoint)
        {
            var item=this.DataPoint[i];
            if (isFirstDraw)
            {
                this.Canvas.beginPath();
                this.Canvas.moveTo(item.X,item.Y);
                isFirstDraw=false;
            }
            else
            {
                this.Canvas.lineTo(item.X,item.Y);
            }
        }

        this.Canvas.closePath();
        this.Canvas.fill();
        this.Canvas.stroke();
    }

    this.DrawText=function(item)
    {
        if (!item.Text) return;
          
        //JSConsole.Chart.Log(item.Text, item.Angle);
        this.Canvas.fillStyle = this.TitleColor;
        var xText = item.X, yText = item.Y;

        //显示每个角度的位置
        if (item.Angle > 0 && item.Angle < 45) {
            this.Canvas.textAlign = 'left';
            this.Canvas.textBaseline = 'middle';
            xText += 2;
        }
        else if (item.Angle >= 0 && item.Angle < 90) {
            this.Canvas.textAlign = 'left';
            this.Canvas.textBaseline = 'top';
            xText += 2;
        }
        else if (item.Angle >= 90 && item.Angle < 135) {
            this.Canvas.textAlign = 'right';
            this.Canvas.textBaseline = 'top';
            xText -= 2;
        }
        else if (item.Angle >= 135 && item.Angle < 180) {
            this.Canvas.textAlign = 'right';
            this.Canvas.textBaseline = 'top';
            xText -= 2;
        }
        else if (item.Angle >= 180 && item.Angle < 225) {
            this.Canvas.textAlign = 'right';
            this.Canvas.textBaseline = 'middle';
            xText -= 2;
        }
        else if (item.Angle >= 225 && item.Angle <= 270) {
            this.Canvas.textAlign = 'center';
            this.Canvas.textBaseline = 'bottom';
        }
        else if (item.Angle > 270 && item.Angle < 315) {
            this.Canvas.textAlign = 'left';
            this.Canvas.textBaseline = 'bottom';
            xText += 2;
        }
        else {
            this.Canvas.textAlign = 'left';
            this.Canvas.textBaseline = 'middle';
            xText += 2;
        }

        this.Canvas.fillText(item.Text, xText, yText);
    }

    this.Draw=function()
    {
        this.BorderPoint=[];
        this.DataPoint=[];
        this.CenterPoint={};
        if (!this.Data || !this.Data.Data || !(this.Data.Data.length>0))
            this.CalculatePoints(null);
        else 
            this.CalculatePoints(this.Data.Data);

        this.DrawBorder();
        this.DrawArea();
    }

    this.CalculatePoints=function(data)
    {
        let left=this.ChartBorder.GetLeft();
        let right=this.ChartBorder.GetRight();
        let top=this.ChartBorder.GetTop();
        let bottom=this.ChartBorder.GetBottom();
        let width=this.ChartBorder.GetWidth();
        let height=this.ChartBorder.GetHeight();

        let ptCenter={X:left+width/2, Y:top+height/2};  //中心点
        let radius=Math.min(width/2,height/2)-2         //半径
        let count=Math.max(5,data?data.length:0);
        let averageAngle=360/count;
        for(let i=0;i<count;++i)
        {
            let ptBorder = { Index: i, Radius: radius, Angle: i * averageAngle + this.StartAngle };
            let angle = ptBorder.Angle;

            if (data && i<data.length)
            {
                var item=data[i];
                let ptData={Index:i,Text:item.Text};
                ptBorder.Text=item.Name;
                if (!item.Value)
                {
                    ptData.X=ptCenter.X;
                    ptData.Y=ptCenter.Y;
                } 
                else
                {
                    var value=item.Value;
                    if (value>=1) value=1;
                    var dataRadius=radius*value;
                    ptData.X=ptCenter.X+dataRadius*Math.cos(angle*Math.PI/180);
                    ptData.Y=ptCenter.Y+dataRadius*Math.sin(angle*Math.PI/180);
                }

                this.DataPoint.push(ptData);
            }

            this.BorderPoint.push(ptBorder);
        }

        this.CenterPoint=ptCenter;
    }

    //空数据
    this.DrawEmptyData=function()
    {
        JSConsole.Chart.Log('[ChartPie::DrawEmptyData]')
    }
}

/*
    中国地图
*/


function ChartChinaMap()
{   
    this.newMethod=IChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ImageData=null;
    this.Left;
    this.Top;
    this.Width;
    this.Height;
    this.ImageWidth;
    this.ImageHeight;

    this.DefaultColor=[217,222,239];

    this.Color=
    [
        {Name:'海南',       Color:'rgb(217,222,223)'},
        {Name:'内蒙古',     Color:'rgb(217,222,225)'},
        {Name:'新疆',       Color:'rgb(217,222,226)'},
        {Name:'青海',       Color:'rgb(217,222,227)'},
        {Name:'西藏',       Color:'rgb(217,222,228)'},
        {Name:'云南',       Color:'rgb(217,222,229)'},
        {Name:'黑龙江',     Color:'rgb(217,222,230)'},
        {Name:'吉林',       Color:'rgb(217,222,231)'},
        {Name:'辽宁',       Color:'rgb(217,222,232)'},
        {Name:'河北',       Color:'rgb(217,222,233)'},
        {Name:'山东',       Color:'rgb(217,222,234)'},
        {Name:'江苏',       Color:'rgb(217,222,235)'},
        {Name:'浙江',       Color:'rgb(217,222,236)'},
        {Name:'福建',       Color:'rgb(217,222,237)'},
        {Name:'广东',       Color:'rgb(217,222,238)'},
        {Name:'广西',       Color:'rgb(217,222,239)'},
        {Name:'贵州',       Color:'rgb(217,222,240)'},
        {Name:'湖南',       Color:'rgb(217,222,241)'},
        {Name:'江西',       Color:'rgb(217,222,242)'},
        {Name:'安徽',       Color:'rgb(217,222,243)'},
        {Name:'湖北',       Color:'rgb(217,222,244)'},
        {Name:'重庆',       Color:'rgb(217,222,245)'},
        {Name:'四川',       Color:'rgb(217,222,246)'},
        {Name:'甘肃',       Color:'rgb(217,222,247)'},
        {Name:'陕西',       Color:'rgb(217,222,248)'},
        {Name:'山西',       Color:'rgb(217,222,249)'},
        {Name:'河南',       Color:'rgb(217,222,250)'}
    ];

    this.Draw=function()
    {
        let left=this.ChartBorder.GetLeft()+1;
        let right=this.ChartBorder.GetRight()-1;
        let top=this.ChartBorder.GetTop()+1;
        let bottom=this.ChartBorder.GetBottom()-1;
        let width=this.ChartBorder.GetWidth()-2;
        let height=this.ChartBorder.GetHeight()-2;

        let imageWidth=CHINA_MAP_IMAGE.width;
        let imageHeight=CHINA_MAP_IMAGE.height;

        let drawImageWidth=imageWidth;
        let drawImageHeight=imageHeight;

        if (height<drawImageHeight || width<drawImageWidth) 
        {
            this.ImageData=null;
            return;
        }

        if (this.Left!=left || this.Top!=top || this.Width!=width || this.Height!=height || this.ImageWidth!=imageWidth || this.ImageHeight!=imageHeight)
        {
            this.ImageData=null;

            this.ImageWidth=imageWidth;
            this.ImageHeight=imageHeight;
            this.Left=left;
            this.Top=top;
            this.Width=width;
            this.Height=height;

            JSConsole.Chart.Log(imageWidth,imageHeight);
        }
        
        if (this.ImageData==null)
        {
            this.Canvas.drawImage(CHINA_MAP_IMAGE,0,0,imageWidth,imageHeight,left,top,drawImageWidth,drawImageHeight);
            this.ImageData=this.Canvas.getImageData(left,top,drawImageWidth,drawImageHeight);

            let defaultColorSet=new Set();  //默认颜色填充的色块
            let colorMap=new Map();         //定义颜色填充的色块

            let nameMap=new Map();
            if (this.Data.length>0)
            {
                for(let i in this.Data)
                {
                    let item=this.Data[i];
                    nameMap.set(item.Name,item.Color)
                }
            }

            JSConsole.Chart.Log(this.Data);
            for(let i in this.Color)
            {
                let item=this.Color[i];
                if (nameMap.has(item.Name))
                {
                    colorMap.set(item.Color,nameMap.get(item.Name));
                }
                else
                {
                    defaultColorSet.add(item.Color);
                }
            }

            var color;
            for (let i=0;i<this.ImageData.data.length;i+=4)
            {
                color='rgb('+ this.ImageData.data[i] + ',' + this.ImageData.data[i+1] + ',' + this.ImageData.data[i+2] + ')';

                if (defaultColorSet.has(color))
                {
                    this.ImageData.data[i]=this.DefaultColor[0];
                    this.ImageData.data[i+1]=this.DefaultColor[1];
                    this.ImageData.data[i+2]=this.DefaultColor[2];
                }
                else if (colorMap.has(color))
                {
                    let colorValue=colorMap.get(color);
                    this.ImageData.data[i]=colorValue[0];
                    this.ImageData.data[i+1]=colorValue[1];
                    this.ImageData.data[i+2]=colorValue[2];
                }
            }
            this.Canvas.clearRect(left,top,drawImageWidth,drawImageHeight);
            this.Canvas.putImageData(this.ImageData,left,top,0,0,drawImageWidth,drawImageHeight);
        }
        else
        {
            this.Canvas.putImageData(this.ImageData,left,top,0,0,drawImageWidth,drawImageHeight);
        }
    }
}

/*
    扩展图形
*/

function IExtendChartPainting()
{
    this.Canvas;                        //画布
    this.ChartBorder;                   //边框信息
    this.ChartFrame;                    //框架画法
    this.Name;                          //名称
    this.Data;                          //数据区
    this.IsDynamic=false;
    this.IsAnimation=false;             //是否是动画
    this.ClassName='IExtendChartPainting';
    this.SizeChange=true;               //大小是否改变
    this.IsEraseBG=false;               //是否每次画的时候需要擦除K线图背景
    this.DrawAfterTitle=false;          //是否在动态标题画完以后再画,防止动态标题覆盖

    //上下左右间距
    this.Left=5;
    this.Right=5;
    this.Top=5;
    this.Bottom=5;

    this.Draw=function()
    {

    }

    //设置参数接口
    this.SetOption=function(option)
    {

    }

}

//K线Tooltip, 显示在左边或右边
function KLineTooltipPaint()
{
    this.newMethod=IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.IsDynamic=true;
    this.IsEraseBG=true;
    this.DrawAfterTitle=true;
    this.ClassName='KLineTooltipPaint';
    this.BorderColor=g_JSChartResource.TooltipPaint.BorderColor;    //边框颜色
    this.BGColor=g_JSChartResource.TooltipPaint.BGColor;            //背景色
    this.TitleColor=g_JSChartResource.TooltipPaint.TitleColor;      //标题颜色

    this.Left=1*GetDevicePixelRatio();
    this.Top=5*GetDevicePixelRatio();;

    this.Width=50;
    this.Height=100;
    this.LineHeight=15*GetDevicePixelRatio(); //行高

    this.Font=[g_JSChartResource.TooltipPaint.TitleFont];

    this.HQChart;
    this.KLineTitlePaint;
    this.IsHScreen=false;   //是否横屏
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;

    this.GetLeft=function()
    {
        if (this.IsHScreen) return this.ChartBorder.GetRightEx()-this.Height-this.Top;
        return this.ChartBorder.GetLeft()+this.Left;
    }

    this.GetTop=function()
    {
        if (this.IsHScreen) return this.ChartBorder.GetTop();
        return this.ChartBorder.GetTopEx()+this.Top;
    }

    this.IsEnableDraw=function()
    {
        if (!this.HQChart || !this.HQChart.TitlePaint || !this.HQChart.TitlePaint[0]) return false;
        if (this.HQChart.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
        {
            if (this.HQChart.TouchStatus.CorssCursorShow==false) return false;
        }
        else if (!this.HQChart.IsOnTouch) 
        {
            return false;
        }

        if (this.HQChart.CurrentChartDrawPicture) return false;   //画图工具操作的时候 不显示

        return true;
    }

    this.Draw=function()
    {
        if (!this.IsEnableDraw()) return;

        this.KLineTitlePaint=this.HQChart.TitlePaint[0];
        var klineData=this.KLineTitlePaint.GetCurrentKLineData();
        if (!klineData) return;

        var lineCount=8;    //显示函数
        if (this.ClassName==='MinuteTooltipPaint') 
        {
            if (MARKET_SUFFIX_NAME.IsChinaFutures(this.HQChart.Symbol)) lineCount=8;    //期货多一个持仓量
            else lineCount=7;
        }
        else 
        {
            if (klineData.Time!=null && !isNaN(klineData.Time) && klineData.Time>0) lineCount=9; //分钟K线多一列时间
            if (MARKET_SUFFIX_NAME.IsSHSZStockA(this.HQChart.Symbol) && klineData.FlowCapital>0) ++lineCount;
        }

        //this.TitleColor=this.KLineTitlePaint.UnchagneColor;
        this.IsHScreen=this.ChartFrame.IsHScreen===true;
        this.Canvas.font=this.Font[0];
        var defaultfloatPrecision=GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
        var maxText=' 擎: 9999.99亿 ';
        if (defaultfloatPrecision>=5) maxText=` 擎: ${99.99.toFixed(defaultfloatPrecision)} `;  //小数位数太多了
        this.Width=this.Canvas.measureText(maxText).width;
        this.Height=this.LineHeight*lineCount+2*GetDevicePixelRatio()*2;

        this.DrawBG();
        this.DrawTooltipData(klineData);
        this.DrawBorder();
    }

    this.DrawTooltipData=function(item)
    {
        //JSConsole.Chart.Log('[KLineTooltipPaint::DrawKLineData] ', item);

        var defaultfloatPrecision=GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
        var left=this.GetLeft()+2*GetDevicePixelRatio();
        var top=this.GetTop()+3*GetDevicePixelRatio();
        
        if (this.IsHScreen)
        {
            this.Canvas.save(); 
            var x=this.GetLeft()+this.Height, y=this.GetTop();

            this.Canvas.translate(x, y);
            this.Canvas.rotate(90 * Math.PI / 180);

            //x, y 作为原点
            left=2*GetDevicePixelRatio();
            top=3*GetDevicePixelRatio();
        }

        this.Canvas.textBaseline="top";
        this.Canvas.textAlign="left";
        this.Canvas.font=this.Font[0];
        var labelWidth=this.Canvas.measureText('擎: ').width;

        var text=IFrameSplitOperator.FormatDateString(item.Date);
        this.Canvas.fillStyle=this.TitleColor;
        this.Canvas.fillText(text, left,top);

        var period=this.HQChart.Period;
        if (ChartData.IsMinutePeriod(period,true) && item.Time)
        {
            top+=this.LineHeight;  
            text=IFrameSplitOperator.FormatTimeString(item.Time);
            this.Canvas.fillText(text, left,top);
        }
        else if (ChartData.IsSecondPeriod(period) && item.Time)
        {
            top+=this.LineHeight;  
            text=IFrameSplitOperator.FormatTimeString(item.Time,'HH:MM:SS');
            this.Canvas.fillText(text, left,top);
        }

        top+=this.LineHeight;  
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-Open',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var color=this.KLineTitlePaint.GetColor(item.Open,item.YClose);
        text=item.Open.toFixed(defaultfloatPrecision);
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        top+=this.LineHeight;   
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-High',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var color=this.KLineTitlePaint.GetColor(item.High,item.YClose);
        var text=item.High.toFixed(defaultfloatPrecision);
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        top+=this.LineHeight;
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-Low',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var color=this.KLineTitlePaint.GetColor(item.Low,item.YClose);
        var text=item.Low.toFixed(defaultfloatPrecision);
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        top+=this.LineHeight;
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-Close',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var color=this.KLineTitlePaint.GetColor(item.Close,item.YClose);
        var text=item.Close.toFixed(defaultfloatPrecision);
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        top+=this.LineHeight;
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-Increase',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        if (item.YClose>0)
        {
            var value=(item.Close-item.YClose)/item.YClose*100;
            var color = this.KLineTitlePaint.GetColor(value, 0);
            var text = value.toFixed(2)+'%';
        }
        else
        {
            var text='--.--';
            var color=this.KLineTitlePaint.GetColor(0, 0);
        }
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        this.Canvas.fillStyle=this.TitleColor;
        top+=this.LineHeight;
        text=g_JSChartLocalization.GetText('Tooltip-Vol',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var text=IFrameSplitOperator.FromatIntegerString(item.Vol,2,this.LanguageID);
        this.Canvas.fillText(text,left+labelWidth,top);

        if (IFrameSplitOperator.IsNumber(item.Amount))
        {
            top+=this.LineHeight;
            text=g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID);
            this.Canvas.fillText(text, left,top);
            var text=IFrameSplitOperator.FormatValueString(item.Amount,2,this.LanguageID);
            this.Canvas.fillText(text,left+labelWidth,top);
        }

        //换手率
        if (MARKET_SUFFIX_NAME.IsSHSZStockA(this.HQChart.Symbol) && item.FlowCapital>0)
        {
            top+=this.LineHeight;
            text=g_JSChartLocalization.GetText('Tooltip-Exchange',this.LanguageID);
            this.Canvas.fillText(text, left,top);
            var value=item.Vol/item.FlowCapital*100;
            var text=value.toFixed(2)+'%';
            this.Canvas.fillText(text,left+labelWidth,top);
        }

        if (this.IsHScreen) this.Canvas.restore();
    }

    this.DrawBorder=function()
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var left=this.GetLeft();
        var top=this.GetTop();
        this.Canvas.strokeStyle=this.BorderColor;
        if (isHScreen)  this.Canvas.strokeRect(ToFixedPoint(left),ToFixedPoint(top),this.Height,this.Width);
        else this.Canvas.strokeRect(ToFixedPoint(left),ToFixedPoint(top),this.Width,this.Height);
    }

    this.DrawBG=function()
    {
        var isHScreen=(this.ChartFrame.IsHScreen===true);
        var left=this.GetLeft();
        var top=this.GetTop();
        this.Canvas.fillStyle=this.BGColor;
        if (isHScreen)   this.Canvas.fillRect(left,top,this.Height,this.Width);
        else  this.Canvas.fillRect(left,top,this.Width,this.Height);
    }

    //设置参数接口
    this.SetOption=function(option)
    {
        if (option.LineHeight>0) this.LineHeight=option.LineHeight*GetDevicePixelRatio();
        if (option.BGColor) this.BGColor=option.BGColor;
        if (option.LanguageID>0) this.LanguageID=option.LanguageID;
    }
}

function MinuteTooltipPaint()
{
    this.newMethod=KLineTooltipPaint;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='MinuteTooltipPaint';
    this.Left=1*GetDevicePixelRatio();
    this.Top=1*GetDevicePixelRatio();
    this.YClose;

    this.GetTop=function()
    {
        if (this.IsHScreen) return this.ChartBorder.GetTop();
        return this.ChartBorder.GetTop()+this.Top;
    }

    this.DrawTooltipData=function(item)
    {
        //JSConsole.Chart.Log('[KLineTooltipPaint::DrawKLineData] ', item);
        var defaultfloatPrecision=GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
        var left=this.GetLeft()+2*GetDevicePixelRatio();
        var top=this.GetTop()+3*GetDevicePixelRatio();
        this.YClose=this.KLineTitlePaint.YClose;
        var upperSymbol=this.HQChart.Symbol.toUpperCase();
        
        if (this.IsHScreen)
        {
            this.Canvas.save(); 
            var x=this.GetLeft()+this.Height, y=this.GetTop();

            this.Canvas.translate(x, y);
            this.Canvas.rotate(90 * Math.PI / 180);

            //x, y 作为原点
            left=2*GetDevicePixelRatio();
            top=3*GetDevicePixelRatio();
        }

        this.Canvas.textBaseline="top";
        this.Canvas.textAlign="left";
        this.Canvas.font=this.Font[0];
        var labelWidth=this.Canvas.measureText('擎: ').width;

        var aryDateTime=item.DateTime.split(' ');
        if (aryDateTime && aryDateTime.length==2)
        {
            var text=IFrameSplitOperator.FormatDateString(aryDateTime[0]);
            this.Canvas.fillStyle=this.TitleColor;
            this.Canvas.fillText(text, left,top);

            top+=this.LineHeight;  
            text=IFrameSplitOperator.FormatTimeString(aryDateTime[1]);
            this.Canvas.fillText(text, left,top);
        }

        var close=item.Close;
        var increase=item.Increase;
        var vol=item.Vol;
        var amount=item.Amount;
        if (item.Before)    //读取盘前数据
        {
            close=item.Before.Close;
            increase=item.Before.Increase;
            vol=item.Before.Vol;
            amount=item.Before.Amount;
        }

        //最新价格
        top+=this.LineHeight;
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-Price',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var color=this.KLineTitlePaint.GetColor(close,this.YClose);
        var text=close.toFixed(defaultfloatPrecision);
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        var isShowAvPrice=true;
        if (item.Before) isShowAvPrice=false;   //集合竞价均价
        else if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) isShowAvPrice=false;     //外汇没有均价
        else if (MARKET_SUFFIX_NAME.IsET(upperSymbol) && !MARKET_SUFFIX_NAME.IsETShowAvPrice(upperSymbol)) isShowAvPrice=false;

        //均价
        if (isShowAvPrice && IFrameSplitOperator.IsNumber(item.AvPrice))   
        {
            top+=this.LineHeight;
            this.Canvas.fillStyle=this.TitleColor;
            text=g_JSChartLocalization.GetText('Tooltip-AvPrice',this.LanguageID);
            this.Canvas.fillText(text, left,top);
            var color=this.KLineTitlePaint.GetColor(item.AvPrice,this.YClose);
            var text=item.AvPrice.toFixed(defaultfloatPrecision);
            this.Canvas.fillStyle=color;
            this.Canvas.fillText(text,left+labelWidth,top);
        }

        //涨幅
        top+=this.LineHeight;
        this.Canvas.fillStyle=this.TitleColor;
        text=g_JSChartLocalization.GetText('Tooltip-Increase',this.LanguageID);
        this.Canvas.fillText(text, left,top);
        var value=(close-this.YClose)/this.YClose*100;
        var color = this.KLineTitlePaint.GetColor(value, 0);
        var text = value.toFixed(2)+'%';
        this.Canvas.fillStyle=color;
        this.Canvas.fillText(text,left+labelWidth,top);

        //成交量
        if (IFrameSplitOperator.IsNumber(vol))
        {
            this.Canvas.fillStyle=this.TitleColor;
            top+=this.LineHeight;
            text=g_JSChartLocalization.GetText('Tooltip-Vol',this.LanguageID);
            this.Canvas.fillText(text, left,top);
            var text=IFrameSplitOperator.FromatIntegerString(vol,2,this.LanguageID);
            this.Canvas.fillText(text,left+labelWidth,top);
        }

        //成交金额
        if (IFrameSplitOperator.IsNumber(amount))
        {
            top+=this.LineHeight;
            text=g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID);
            this.Canvas.fillText(text, left,top);
            var text=IFrameSplitOperator.FormatValueString(amount,2,this.LanguageID);
            this.Canvas.fillText(text,left+labelWidth,top);
        }

        if (IFrameSplitOperator.IsNumber(item.Position))
        {
            top+=this.LineHeight;
            text=g_JSChartLocalization.GetText('Tooltip-Position',this.LanguageID);
            this.Canvas.fillText(text, left,top);
            var text=IFrameSplitOperator.FormatValueString(item.Position,2,this.LanguageID);
            this.Canvas.fillText(text,left+labelWidth,top);
        }

        if (this.IsHScreen) this.Canvas.restore();
    }
}

//股票信息
function StockInfoExtendChartPaint()
{
    this.newMethod=IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Left=80;
    this.Right=1;
    this.Top=1;
    this.Bottom=1;

    this.BorderColor=g_JSChartResource.FrameBorderPen;

    this.Symbol;
    this.Name;

    this.TitleFont=["14px 微软雅黑"];

    this.Draw=function()
    {
        var left=this.ChartBorder.GetRight()+this.Left;
        var right=this.ChartBorder.GetChartWidth()-this.Right;
        var y=this.Top+18;
        var middle=left+(right-left)/2;

        if (this.Symbol && this.Name)
        {
            this.Canvas.font=this.TitleFont[0];

            this.Canvas.textAlign="right";
            this.Canvas.textBaseline="bottom";
            this.Canvas.fillText(this.Symbol,middle-2,y);

            this.Canvas.textAlign="left";
            this.Canvas.fillText(this.Name,middle+2,y);
        }
        ;
        this.Canvas.strokeStyle=this.BorderColor;
        this.Canvas.moveTo(left,y);
        this.Canvas.lineTo(right,y);
        this.Canvas.stroke();

        y+=30;

        this.DrawBorder();
    }

    this.DrawBorder=function()
    {
        var left=this.ChartBorder.GetRight()+this.Left;
        var right=this.ChartBorder.GetChartWidth()-this.Right;
        var top=this.Top;
        var bottom=this.ChartBorder.GetChartHeight()-this.Bottom;

        this.Canvas.strokeStyle=this.BorderColor;
        this.Canvas.strokeRect(left,top,(right-left),(bottom-top));
    }
}

//筹码分布
function StockChip()
{
    this.newMethod=IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Name='筹码分布';
    this.ClassName='StockChip';

    this.HQChart;
    this.PenBorder=g_JSChartResource.FrameBorderPen;    //边框
    this.ColorProfit='rgb(255,0,0)';            //盈利的线段
    this.ColorNoProfit='rgb(90,141,248)';       //非盈利
    this.ColorAveragePrice='rgb(0,139,0)';   //平均价线
    this.ColorBG='rgb(190,190,190)';           //筹码背景线段颜色

    this.PixelRatio=GetDevicePixelRatio();
    this.ShowType=0;    //0=所有筹码  1=周期前  2=周期内
    this.IsDynamic=true;
    this.ClientRect={};
    this.Font=g_JSChartResource.TitleFont;
    this.InfoColor='rgb(0,0,0)';
    this.DayInfoColor='rgb(255,255,255)';
    this.LineHeight=16;
    this.Left=50*this.PixelRatio;         //左边间距
    this.IsShowX=false;  //是否显示X刻度 成交量
    this.ShowXCount=3;
    this.Width=150*this.PixelRatio;       //筹码图宽度

    this.ButtonID=Guid();  //工具条Div id

    this.DAY_COLOR=
    [
        ['rgb(255,0,0)','rgb(255,128,128)','rgb(255,0,128)','rgb(255,100,0)','rgb(192,128,0)','rgb(255,192,0)'],
        ['rgb(120,80,225)','rgb(160,160,225)','rgb(80,80,255)','rgb(120,120,255)','rgb(32,64,192)','rgb(0,64,128)'],
    ];

    this.SetOption=function(option)
    {
        if (!option) return;
        if (option.ShowType>0) this.ShowType=option.ShowType;
        if (option.IsShowX) this.IsShowX=option.IsShowX;
        if (option.ShowXCount>0) this.ShowXCount=option.ShowXCount;
        if (option.Width>100) this.Width=option.Width*GetDevicePixelRatio();
    }
    
    this.Draw=function()
    {
        this.PixelRatio=GetDevicePixelRatio();
        var left=ToFixedPoint(this.ChartBorder.GetRight()+this.Left);
        var top=ToFixedPoint(this.ChartBorder.GetTop());
        var right=ToFixedPoint(left+this.Width-1*this.PixelRatio);
        var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
        var width=right-left;
        var height=bottom-top;
        this.ClientRect={Left:left,Top:top,Width:width,Height:height};

        if (this.CalculateChip())
        {
            this.DrawFrame();
            this.DrawAllChip();
            if (this.ShowType==1|| this.ShowType==2) this.DrawDayChip();

            this.CalculateCast();   //计算成本  
            this.DrawChipInfo();      
        }
        else
        {
            JSConsole.Chart.Log('[StockChip::Draw] no data');
        }

        this.DrawBorder();
        if (this.SizeChange==true) this.DrawButton();

        this.SizeChange=false;
    }

    this.DrawChipInfo=function()    
    {
        var bottom=this.ClientRect.Top+this.ClientRect.Height-1;
        var left=this.ClientRect.Left+2;
        var right=this.ClientRect.Left+this.ClientRect.Width;

        this.Canvas.font=this.Font;
        this.Canvas.fillStyle=this.InfoColor;
        this.Canvas.textBaseline='bottom';
        this.Canvas.textAlign='left';

        var lineHeight=this.LineHeight*GetDevicePixelRatio();
        var text='70%成本价'+ this.Data.Cast[1].MinPrice.toFixed(2)+'-'+this.Data.Cast[1].MaxPrice.toFixed(2)+'集中'+this.Data.Cast[1].Rate.toFixed(2)+'%';
        this.Canvas.fillText(text,left,bottom);
        bottom-=lineHeight;

        text='90%成本价'+ this.Data.Cast[0].MinPrice.toFixed(2)+'-'+this.Data.Cast[0].MaxPrice.toFixed(2)+'集中'+this.Data.Cast[0].Rate.toFixed(2)+'%';;
        this.Canvas.fillText(text,left,bottom);
        bottom-=lineHeight;

        text='平均成本:'+this.Data.ChipInfo.AveragePrice.toFixed(2)+'元';
        this.Canvas.fillText(text,left,bottom);
        bottom-=lineHeight;

        text=+this.Data.YPrice.toFixed(2)+'处获利盘:'+this.Data.ChipInfo.YProfitRate.toFixed(2)+'%';
        this.Canvas.fillText(text,left,bottom);
        bottom-=lineHeight;

        text='获利比例:';
        this.Canvas.fillText(text,left,bottom);
        var textWidth=this.Canvas.measureText(text).width+2;
        var barLeft=left+textWidth;
        var barWidth=(right-5-barLeft);
        this.Canvas.strokeStyle=this.ColorNoProfit;
        this.Canvas.strokeRect(barLeft,bottom-lineHeight,barWidth,lineHeight);
        this.Canvas.strokeStyle=this.ColorProfit;
        this.Canvas.strokeRect(barLeft,bottom-lineHeight,barWidth*(this.Data.ChipInfo.ProfitRate/100),lineHeight);
        text=this.Data.ChipInfo.ProfitRate.toFixed(2)+'%';
        this.Canvas.textAlign='center';
        this.Canvas.fillText(text,barLeft+barWidth/2,bottom);
        bottom-=lineHeight;

        this.Canvas.textAlign='left';
        text='成本分布,日期:'+IFrameSplitOperator.FormatDateString(this.Data.SelectData.Date);
        if (this.Data.SelectData.Time) text+=' '+IFrameSplitOperator.FormatTimeString(this.Data.SelectData.Time);
        this.Canvas.fillText(text,left,bottom);
        bottom-=lineHeight;

        if (this.ShowType!=1 && this.ShowType!=2) return;

        var right=this.ClientRect.Left+this.ClientRect.Width-1;
        this.Canvas.textAlign='right';
        var textWidth=50;
        this.Data.DayChip.sort(function(a,b){return b.Day-a.Day;})
        for(var i in this.Data.DayChip)
        {
            var item=this.Data.DayChip[i];
            var rate=0;
            if (this.Data.ChipInfo && this.Data.ChipInfo.Vol>0) rate=item.Vol/this.Data.ChipInfo.Vol*100;
            text=item.Day+'周期'+(this.ShowType==1?'前':'内')+'成本'+rate.toFixed(2)+'%';
            if (i==0) textWidth=this.Canvas.measureText(text).width+8;
            this.Canvas.fillStyle=item.Color;
            this.Canvas.fillRect(right-textWidth,bottom-lineHeight,textWidth,lineHeight);

            this.Canvas.fillStyle=this.DayInfoColor;
            this.Canvas.fillText(text,right,bottom);
            bottom-=lineHeight;
        }
    }

    this.DrawDayChip=function()
    {
        var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame;
        for(var i in this.Data.DayChip)
        {
            var aryPoint=[];
            var chipData=this.Data.DayChip[i].Chip;
            if (!chipData) continue;
            var totalVol=0;
            for(var j=0;j<chipData.length;++j)
            {
                var vol=chipData[j];
                if(!vol) continue;
                totalVol+=vol;
                var price=(j+this.Data.MinPrice)/100;
                var y=KLineFrame.GetYFromData(price);
                var x=(vol/this.Data.MaxVol)*this.ClientRect.Width+this.ClientRect.Left;
                aryPoint.push({X:x,Y:y});
            }
            this.Data.DayChip[i].Vol=totalVol;
            this.DrawArea(aryPoint,this.Data.DayChip[i].Color);
        }
    }

    this.Clear=function()
    {
        var divButton=document.getElementById(this.ButtonID);
        if (divButton) this.ChartBorder.UIElement.parentNode.removeChild(divButton);
    }

    this.DrawButton=function()  //顶部按钮
    {
        var divButton=document.getElementById(this.ButtonID);
        if (!divButton)
        {
            divButton=document.createElement("div");
            divButton.className='klineframe-button';
            divButton.id=this.ButtonID;
            //为divToolbar添加属性identify
            // divButton.setAttribute("identify",this.Identify.toString());
            this.ChartBorder.UIElement.parentNode.appendChild(divButton);
        }

        var pixelTatio = this.PixelRatio;
        var left=this.ClientRect.Left;
        var right=this.ClientRect.Left+this.ClientRect.Width;
        var chartRight=this.ChartBorder.GetChartWidth();
        var toolbarWidth=right-left;
        var toolbarHeight=this.ChartBorder.GetTitleHeight();
        // var left=this.ChartBorder.GetRight();
        var top=this.ChartBorder.GetTop()/pixelTatio;

        const spanStyle="<span class='{iconclass}' id='{iconid}' title='{icontitle}' style='cursor:pointer;display:inline-block;color:{iconcolor};font-size:{iconsize}px'></span>";
        const ICON_LIST=
        [
            {Class:'chip_default icon iconfont icon-chip_default', ID:'chip_default',Color:['#808080','#FF0000'],Title:'默认筹码分布图'},
            {Class:'chip_long icon iconfont icon-chip_date',ID:'chip_long',Color:['#808080','#FF8000'],Title:'远期筹码分布图'},
            {Class:'chip_recent icon iconfont icon-chip_date',ID:'chip_recent', Color:['#808080','#0000CC'],Title:'近期筹码分布图'}
        ];

        var spanHtml='';
        for(var i in ICON_LIST)
        {
            var item=ICON_LIST[i];
            var spanItem=spanStyle;
            spanItem=spanItem.replace('{iconclass}',item.Class);
            spanItem=spanItem.replace('{iconid}',item.ID);
            spanItem=spanItem.replace('{iconcolor}',item.Color[i==this.ShowType?1:0]);
            spanItem=spanItem.replace('{iconsize}',toolbarHeight);
            spanItem=spanItem.replace('{icontitle}',item.Title);

            if (spanHtml.length>0) spanHtml+='&nbsp;&nbsp;';
            spanHtml+=spanItem;
        }

        divButton.style.right = Math.floor((chartRight-right+2)/pixelTatio) + "px";
        divButton.style.top = top + "px";
        //divButton.style.width=toolbarWidth+"px";
        divButton.style.height=toolbarHeight+'px';
        divButton.innerHTML=spanHtml;

        var self = this;
        for(var i in ICON_LIST)
        {
            var item=ICON_LIST[i];
            $("#"+this.ButtonID+" ."+item.ID).click(
                {
                    IconID:item.ID,
                    ShowType:i
                },
                function (event) 
                {
                    var id=event.data.IconID;
                    var showType=event.data.ShowType;
                    self.ShowType=showType;
                    if (self.HQChart) self.HQChart.Draw();

                    for(var i in ICON_LIST)
                    {
                        var item=ICON_LIST[i];
                        var style=$("#"+self.ButtonID+" ."+item.ID)[0].style;
                        style['color']=item.Color[i==showType?1:0];
                    }
                }
            )
        }
    }

    this.DrawAllChip=function()
    {
        var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame;
        var selectPrice=this.Data.SelectData.Close;
        var aryProfitPoint=[];
        var aryNoProfitPoint=[];
        var totalVol=0,totalAmount=0,totalProfitVol=0, totalYProfitVol=0;   //总的成交量, 总的成交金额, 总的盈利的成交量
        var yPrice=this.Data.YPrice;

        var maxPrice=KLineFrame.HorizontalMax;
        var minPrice=KLineFrame.HorizontalMin;

        var MaxVol=1;
        for(var i=0;i<this.Data.AllChip.length;++i)
        {
            var vol=this.Data.AllChip[i];
            if(!vol) continue;
            var price=(i+this.Data.MinPrice)/100;
            totalVol+=vol;
            totalAmount+=price*vol;

            if (price<yPrice) totalYProfitVol+=vol;     //获利的成交量
            if (price<selectPrice) totalProfitVol+=vol; //鼠标当前位置 获利的成交量

            if (price<=maxPrice && price>=minPrice)
            {
                if (MaxVol<vol) MaxVol=vol;
            }
        }
        this.Data.MaxVol=MaxVol;    //把成交量最大值替换成 当前屏成交量最大值
        
        for(var i=0;i<this.Data.AllChip.length;++i)
        {
            var vol=this.Data.AllChip[i];
            if(!vol) continue;
            var price=(i+this.Data.MinPrice)/100;
            if (price>maxPrice || price<minPrice) continue;

            var y=KLineFrame.GetYFromData(price);
            var x=(vol/this.Data.MaxVol)*this.ClientRect.Width+this.ClientRect.Left;
           
            if (price<selectPrice) aryProfitPoint.push({X:x,Y:y});
            else aryNoProfitPoint.push({X:x,Y:y});
        }

        this.Data.ChipInfo=
        {
            Vol:totalVol, AveragePrice:totalAmount/totalVol, ProfitVol:totalProfitVol, 
            ProfitRate:totalVol>0?totalProfitVol/totalVol*100:0,
            YProfitRate:totalVol>0?totalYProfitVol/totalVol*100:0
        };

        if (this.ShowType==0)
        {
            this.DrawLines(aryProfitPoint,this.ColorProfit);
            this.DrawLines(aryNoProfitPoint,this.ColorNoProfit);
            var averagePrice=this.Data.ChipInfo.AveragePrice;
            if (averagePrice>0 && averagePrice<=maxPrice && averagePrice>=minPrice) 
            {
                averagePrice=averagePrice.toFixed(2);
                this.DrawAveragePriceLine(aryProfitPoint,aryNoProfitPoint,KLineFrame.GetYFromData(averagePrice),this.ColorAveragePrice);
            }
        }
        else    //在火焰山模式下, 筹码用一个颜色
        {
            this.DrawLines(aryProfitPoint,this.ColorBG);
            this.DrawLines(aryNoProfitPoint,this.ColorBG);
        }
    }

    this.CalculateCast=function()   //计算 90% 70%的成本价
    {
        if (!this.Data.ChipInfo || !this.Data.ChipInfo.Vol) return;

        var aryCast=
        [
            {Start:0.05,End:0.95, MaxPrice:0, MinPrice:0, Rate:0},
            {Start:0.15,End:0.85, MaxPrice:0, MinPrice:0, Rate:0}
        ];

        var averagePrice=this.Data.ChipInfo.AveragePrice;
        var totalProfitVol=this.Data.ChipInfo.ProfitVol;
        var tempVol=0;
        for(var i=0, castCount=0;i<this.Data.AllChip.length;++i)
        {
            if (castCount==4) break;
            var vol=this.Data.AllChip[i];
            if (vol<=0) continue;

            var price=(i+this.Data.MinPrice)/100;
            tempVol+=vol;
            var rate=tempVol/totalProfitVol;
            
            for(var j in aryCast)
            {
                var itemCast=aryCast[j];
                if (itemCast.MinPrice<=0 && rate>itemCast.Start)
                {
                    itemCast.MinPrice=price;
                    ++castCount;
                }

                if (itemCast.MaxPrice<=0 && rate>itemCast.End)
                {
                    itemCast.MaxPrice=price;
                    ++castCount;
                }
            }
        }

        for(var i in aryCast)
        {
            var item=aryCast[i];
            var addPrice=item.MaxPrice+item.MinPrice;
            if (addPrice) item.Rate=Math.abs(item.MaxPrice-item.MinPrice)/addPrice*100;
        }

        this.Data.Cast=aryCast;
    }

    this.DrawArea=function(aryPoint,color)
    {
        if (aryPoint.length<=0) return;

        this.Canvas.fillStyle=color;
        this.Canvas.beginPath();
        this.Canvas.moveTo(this.ClientRect.Left,aryPoint[0].Y);
        for(var i in aryPoint)
        {
            var item=aryPoint[i];
            this.Canvas.lineTo(item.X,item.Y);
        }
        this.Canvas.lineTo(this.ClientRect.Left,aryPoint[aryPoint.length-1].Y);
        this.Canvas.fill();
    }

    this.DrawLines=function(aryPoint,color)
    {
        if (aryPoint.length<=0) return;
        this.Canvas.strokeStyle=color;
        this.Canvas.beginPath();
        for(var i in aryPoint)
        {
            var item=aryPoint[i];
            this.Canvas.moveTo(this.ClientRect.Left,item.Y);
            this.Canvas.lineTo(item.X,item.Y);
        }
        this.Canvas.stroke();
    }

    this.DrawAveragePriceLine=function(aryProfitPoint,aryNoProfitPoint,y,color)
    {
        for(var i in aryProfitPoint)
        {
            var item=aryProfitPoint[i];
            if (item.Y==y)
            {
                this.Canvas.strokeStyle=color;
                this.Canvas.beginPath();
                this.Canvas.moveTo(this.ClientRect.Left,item.Y);
                this.Canvas.lineTo(item.X,item.Y);
                this.Canvas.stroke();
                return;
            }
        }

        for(var i in aryNoProfitPoint)
        {
            var item=aryNoProfitPoint[i];
            if (item.Y==y)
            {
                this.Canvas.strokeStyle=color;
                this.Canvas.beginPath();
                this.Canvas.moveTo(this.ClientRect.Left,item.Y);
                this.Canvas.lineTo(item.X,item.Y);
                this.Canvas.stroke();
                return;
            }
        }
    }

    this.DrawBorder=function()
    {
        this.Canvas.strokeStyle=this.PenBorder;
        this.Canvas.strokeRect(this.ClientRect.Left,this.ClientRect.Top,this.ClientRect.Width,this.ClientRect.Height);
    }

    this.CalculateChip=function()   //计算筹码
    {
        if (!this.HQChart) return false;
        if (!this.HQChart.FlowCapitalReady) return false;
        var symbol=this.HQChart.Symbol;
        if (!symbol) return false;
        if (MARKET_SUFFIX_NAME.IsSHSZIndex(symbol)) return false;   //指数暂时不支持移动筹码

        var bindData=this.HQChart.ChartPaint[0].Data;
        //if (bindData.Period>=4) return false;   //分钟K线不支持, 没时间做,以后再做吧
        var count=bindData.DataOffset+parseInt(this.HQChart.CursorIndex);
        if (count>=bindData.Data.length) count=bindData.Data.length-1;
        var selData=bindData.Data[count];
        var yPrice=selData.Close;

        var mouseY=this.HQChart.LastPoint.Y;
        if (mouseY) yPrice=this.HQChart.Frame.SubFrame[0].Frame.GetYData(mouseY);
        
        //JSConsole.Chart.Log("[StockChip::CalculateChip]",count,this.HQChart.CursorIndex,selData);
        const rate=1;
        var aryVol=[];
        var seed=1,vol,maxPrice,minPrice;
        for(let i=count;i>=0;--i)
        {
            var item=bindData.Data[i];
            var changeRate=1;   //换手率
            if (item.FlowCapital>0) changeRate=item.Vol/item.FlowCapital;
            if (i==count) vol=item.Vol*changeRate;
            else vol=item.Vol*seed;
            var dataItem={Vol:vol,High:item.High,Low:item.Low};
            aryVol.push(dataItem);
            seed*=(1-changeRate*rate);

            if (!maxPrice || maxPrice<item.High) maxPrice=item.High;
            if (!minPrice || minPrice>item.Low) minPrice=item.Low;
        }

        //JSConsole.Chart.Log("[StockChip::CalculateChip]",maxPrice,minPrice);
        if (!maxPrice || !minPrice) return true;

        maxPrice=parseInt(maxPrice*100);
        minPrice=parseInt(minPrice*100);

        var dataCount=maxPrice-minPrice;
        var aryChip=new Array()
        for(let i=0;i<=dataCount;++i)
        {
            aryChip.push(0);
        }

        var maxVol=1;
        var dayChip=[];
        if (this.ShowType==2)
        {
            var dayChip=
            [
                {Day:100, Color:this.DAY_COLOR[1][5]}, {Day:60, Color:this.DAY_COLOR[1][4]}, {Day:30, Color:this.DAY_COLOR[1][3]},
                {Day:20, Color:this.DAY_COLOR[1][2]}, {Day:10, Color:this.DAY_COLOR[1][1]}, {Day:5, Color:this.DAY_COLOR[1][0]}
            ];
            for(let i in aryVol)
            {
                var item=aryVol[i];
                var high=parseInt(item.High*100);
                var low=parseInt(item.Low*100);
                var averageVol=item.Vol;
                if (high-low>0) averageVol=item.Vol/(high-low);
                if (averageVol<=0.000000001) continue;

                for(var k=0;k<dayChip.length;++k)
                {
                    if (i==dayChip[k].Day) 
                    {
                        dayChip[k].Chip=aryChip.slice(0);
                        break;
                    }
                }
                
                for(var j=low;j<=high && j<=maxPrice;++j)
                {
                    var index=j-minPrice;
                    aryChip[index]+=averageVol;
                    if (maxVol<aryChip[index]) maxVol=aryChip[index];
                }
            }
        }
        else if (this.ShowType==1)
        {
            var dayChip=
            [
                {Day:5, Color:this.DAY_COLOR[0][0]},{Day:10, Color:this.DAY_COLOR[0][1]},{Day:20, Color:this.DAY_COLOR[0][2]},
                {Day:30, Color:this.DAY_COLOR[0][3]},{Day:60, Color:this.DAY_COLOR[0][4]},{Day:100, Color:this.DAY_COLOR[0][5]}
            ];

            for(let i=aryVol.length-1;i>=0;--i)
            {
                var item=aryVol[i];
                var high=parseInt(item.High*100);
                var low=parseInt(item.Low*100);
                var averageVol=item.Vol;
                if (high-low>0) averageVol=item.Vol/(high-low);
                if (averageVol<=0.000000001) continue;

                for(var k=0;k<dayChip.length;++k)
                {
                    if (i==dayChip[k].Day) 
                    {
                        dayChip[k].Chip=aryChip.slice(0);
                        break;
                    }
                }
                
                for(var j=low;j<=high && j<=maxPrice;++j)
                {
                    var index=j-minPrice;
                    aryChip[index]+=averageVol;
                    if (maxVol<aryChip[index]) maxVol=aryChip[index];
                }
            }
        }
        else
        {
            for(let i in aryVol)
            {
                var item=aryVol[i];
                var high=parseInt(item.High*100);
                var low=parseInt(item.Low*100);
                var averageVol=item.Vol;
                if (high-low>0) averageVol=item.Vol/(high-low);
                if (averageVol<=0.000000001) continue;

                for(var j=low;j<=high && j<=maxPrice;++j)
                {
                    var index=j-minPrice;
                    aryChip[index]+=averageVol;
                    if (maxVol<aryChip[index]) maxVol=aryChip[index];
                }
            }
        }

        this.Data={AllChip:aryChip, MaxVol:maxVol, MaxPrice:maxPrice, MinPrice:minPrice,SelectData:selData, DayChip:dayChip, YPrice:yPrice};
        return true;
    }

    this.DrawFrame=function()  //X轴成交量坐标
    {
        if (this.IsShowX==false) return;
        if (this.Data.MaxVol<=0) return;

        var isDrawXFrame=this.HQChart.Frame.SubFrame.length===1 ? false:true;         //是否画X轴,如果只有1个窗口就不画
        var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame;
        var chartBorder=KLineFrame.ChartBorder;
        var bottom=ToFixedPoint(chartBorder.GetBottomEx()+1);
        if (!isDrawXFrame) bottom=this.ClientRect.Top+this.ClientRect.Height;
        var left=this.ClientRect.Left;
        var right=left+this.ClientRect.Width;

        this.Canvas.strokeStyle=this.PenBorder;
        this.Canvas.beginPath();
        if (isDrawXFrame)
        {
            this.Canvas.moveTo(left,bottom);
            this.Canvas.lineTo(right,bottom);
        }
        
        var showCount=this.ShowXCount;
        var maxValue=this.Data.MaxVol;
        var perValue=Math.floor(maxValue/showCount);
        this.Canvas.font=this.Font;
        this.Canvas.textBaseline='top';
        this.Canvas.fillStyle=this.InfoColor;
        var xOffset=10*GetDevicePixelRatio();
        for(i=1;i<=showCount;++i)
        {
            var vol=perValue*i;
            var x=(vol/this.Data.MaxVol)*this.ClientRect.Width+this.ClientRect.Left;
            x=ToFixedPoint(x);
            if (i==showCount)   //最后一个刻度不要画线了
            {
                this.Canvas.textAlign='right';
                var text=IFrameSplitOperator.FormatValueString(maxValue, 1);
                this.Canvas.fillText(text,x,bottom+2);
            }
            else
            {
                this.Canvas.moveTo(x,this.ClientRect.Top);
                this.Canvas.lineTo(x,bottom);
               
                this.Canvas.textAlign='center';
               
                var text=IFrameSplitOperator.FormatValueString(vol, 1);
                var textWidth=this.Canvas.measureText(text).width;
                this.Canvas.fillText(text,Math.floor(x-textWidth*0.25),bottom+2);
            }
        }

        this.Canvas.stroke();
    }
}

//画图工具条
function DrawToolsButton()
{
    this.newMethod=IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='DrawToolsButton';
    this.HQChart;
    this.ID=Guid();
    this.ToolsDiv;
    // this.Color='rgb(105,105,105)';    //颜色
    this.Color = "#696969";        //input type="color"不支持rgb的颜色格式

    //this.Left=5;
    this.Top=5*GetDevicePixelRatio();
    this.Width=45*GetDevicePixelRatio();         //宽度

    this.SetOption=function(option)
    {
        var pixelRatio=GetDevicePixelRatio();
        if (!option) return;
        if (option.Width>10) this.Width=option.Width*pixelRatio;
    }

    this.Clear=function()
    {
        if (this.ToolsDiv) this.ChartBorder.UIElement.parentNode.removeChild(this.ToolsDiv);
    }

    this.Draw = function () {
        if (this.SizeChange == false) return;

        //工具列表
        const TOOL_LIST =
            [
                [
                    { HTML: { Title: '线段', IClass: 'iconfont icon-draw_line', ID: 'icon-segment' }, Name: '线段' },
                    { HTML: { Title: '射线', IClass: 'iconfont icon-draw_rays', ID: 'icon-beam' }, Name: '射线' },
                    { HTML: { Title: '趋势线', IClass: 'iconfont icon-draw_trendline', ID: 'icon-trendline' }, Name: '趋势线' },
                    { HTML: { Title: '水平线', IClass: 'iconfont icon-draw_hline', ID: 'icon-hline' }, Name: '水平线' },
                    { HTML: { Title: '平行线', IClass: 'iconfont icon-draw_parallel_lines', ID: 'icon-parallellines' }, Name: '平行线' },
                    { HTML: { Title: '平行通道', IClass: 'iconfont icon-draw_parallelchannel', ID: 'icon-parallelchannel' }, Name: '平行通道' },
                    { HTML: { Title: '价格通道线', IClass: 'iconfont icon-draw_pricechannel', ID: 'icon-pricechannel' }, Name: '价格通道线' },
                    { HTML: { Title: 'M头W底', IClass: 'iconfont icon-draw_wavemw', ID: 'icon-wavemw' }, Name: 'M头W底' },
                ],
                [
                    { HTML: { Title: '圆弧', IClass: 'iconfont icon-draw_arc', ID: 'icon-arc' }, Name: '圆弧线' },
                    { HTML: { Title: '矩形', IClass: 'iconfont icon-rectangle', ID: 'icon-rect' }, Name: '矩形' },
                    { HTML: { Title: '平行四边形', IClass: 'iconfont icon-draw_quadrangle', ID: 'icon-quad' }, Name: '平行四边形' },
                    { HTML: { Title: '三角形', IClass: 'iconfont icon-draw_triangle', ID: 'icon-triangle' }, Name: '三角形' },
                    { HTML: { Title: '圆', IClass: 'iconfont icon-draw_circle', ID: 'icon-circle' }, Name: '圆' },
                    { HTML: { Title: '对称角度', IClass: 'iconfont icon-draw_symangle', ID: 'icon-symangle' }, Name: '对称角度' },
                ],
                [
                    { HTML: { Title: '文本', IClass: 'iconfont icon-draw_text', ID: 'icon-text' }, Name: '文本' },
                    { HTML: { Title: '向上箭头', IClass: 'iconfont icon-arrow_up', ID: 'icon-arrowup' }, Name: 'icon-arrow_up' },
                    { HTML: { Title: '向下箭头', IClass: 'iconfont icon-arrow_down', ID: 'icon-arrowdown' }, Name: 'icon-arrow_down' },
                    { HTML: { Title: '向左箭头', IClass: 'iconfont icon-arrow_left', ID: 'icon-arrowleft' }, Name: 'icon-arrow_left' },
                    { HTML: { Title: '向右箭头', IClass: 'iconfont icon-arrow_right', ID: 'icon-arrowright' }, Name: 'icon-arrow_right' },
                ],
                [
                    { HTML: { Title: '江恩角度线', IClass: 'iconfont icon-draw_gannfan', ID: 'icon-gannfan' }, Name: '江恩角度线' },
                    { HTML: { Title: '斐波那契周期线', IClass: 'iconfont icon-draw_fibonacci', ID: 'icon-fibonacci' }, Name: '斐波那契周期线' },
                    { HTML: { Title: '阻速线', IClass: 'iconfont icon-draw_resline', ID: 'icon-resline' }, Name: '阻速线' },
                    { HTML: { Title: '黄金分割', IClass: 'iconfont icon-draw_goldensection', ID: 'icon-goldensection' }, Name: '黄金分割' },
                    { HTML: { Title: '百分比线', IClass: 'iconfont icon-draw_percentage', ID: 'icon-percentage' }, Name: '百分比线' },
                    { HTML: { Title: '波段线', IClass: 'iconfont icon-draw_waveband', ID: 'icon-waveband' }, Name: '波段线' },
                ],
                [{ HTML: { Title: '全部删除', IClass: 'iconfont icon-recycle_bin', ID: 'icon-delete' }, Name: '全部删除' }]
            ];

        var hqChart = this.HQChart;

        if (!this.ToolsDiv) {
            var div = document.createElement("div");
            div.className = 'drawtools';
            div.id = this.ID;

            var spanList = "";  //一层菜单
            var menuTwoList = ""; //二层菜单
            var menuOne = new Array();
            TOOL_LIST.forEach(function(item,index){
                menuOne.push(item[0]);
            });
            for (var i = 0; i < TOOL_LIST.length; i++) {
                var itemOut = menuOne[i];
                var itemIn = TOOL_LIST[i];
                var menuTwoStr = "";
                var contentArrow = "";
                for (var j = 0; j < itemIn.length; j++) {
                    var currentItem = itemIn[j];
                    var menuTwoName = currentItem.Name;
                    if(menuTwoName.indexOf('up') > -1){
                        menuTwoName = "向上箭头";
                    }else if(menuTwoName.indexOf('down') > -1){
                        menuTwoName = "向下箭头";
                    }else if(menuTwoName.indexOf('left') > -1){
                        menuTwoName = "向左箭头";
                    }else if(menuTwoName.indexOf('right') > -1){
                        menuTwoName = "向右箭头";
                    }
                    menuTwoStr += '<p class="menuTwoItem ' + currentItem.HTML.ID + '">' + menuTwoName + '<i class="' + currentItem.HTML.IClass + '" title="' + currentItem.HTML.Title + '"></i></p>';
                }
                if (i !== TOOL_LIST.length - 1) { //不是“全部删除”项
                    menuTwoList = '<div class="menuTwo">' + menuTwoStr + '</div>';
                    contentArrow = '<i class="contentArrow iconfont icon-menu_arraw_left"></i>';
                } else {
                    menuTwoList = "";
                    contentArrow = "";
                }

                var spanNode = '<div class="icon-image ' + 'first-' + itemOut.HTML.ID + '"><i class="' + itemOut.HTML.IClass + '" title="' + itemOut.HTML.Title + '"></i>' + menuTwoList + contentArrow +'</div>';
                spanList += spanNode;
            }
            this.ChartBorder.UIElement.parentNode.appendChild(div);

            div.innerHTML = spanList;
            this.ToolsDiv = div;
            
            for (var i in TOOL_LIST) {
                var item = TOOL_LIST[i][0];
                $('#' + this.ID + " .first-" + item.HTML.ID).hover(function(){  //箭头的旋转过渡
                    $(".drawtools").find(".contentArrow").hide();
                    $(this).find(".contentArrow").removeClass("trans").show();
                });
                $('#' + this.ID + " .first-" + item.HTML.ID+" .contentArrow").click(function(event){ //点击三角展示二级菜单
                    event.stopPropagation();
                    $(".drawtools").find(".menuTwo").hide();
                    $(this).siblings('.menuTwo').show();
                });
                $('#' + this.ID + " .first-" + item.HTML.ID+" .trans").click(function(){ //点击三角隐藏二级菜单
                    event.stopPropagation();
                    $(this).siblings('.menuTwo').hide();
                });
                
                
                if (item.Name == '全部删除') {
                    $('#' + this.ID + " .first-icon-delete").click(function () {
                        $(".drawtools").find(".menuTwo").hide();
                        $(this).siblings().removeClass('active');
                        $(this).addClass('active');
                        hqChart.ClearChartDrawPicture();
                        $(".subTolls").css("display", "none");
                    });
                }
                else {
                    $('#' + this.ID + " .first-" + menuOne[i].HTML.ID).click( //一层菜单类名是:“first-”+item.HTML.ID
                        {
                            // DrawName: menuOne[i].Name,  //把画法名字传进去
                            CurrentIndex:i
                        },
                        function (event) {
                            $(".drawtools").find(".menuTwo").hide();
                            $(this).siblings().removeClass('active');
                            $(this).addClass('active');
                            hqChart.CreateChartDrawPicture(menuOne[event.data.CurrentIndex].Name);
                        }
                    );
                    for (var j in TOOL_LIST[i]) {
                        var itemTwo = TOOL_LIST[i][j];
                        let classname = itemTwo.HTML.IClass;  //闭包问题
                        $('#' + this.ID + ' .' + itemTwo.HTML.ID).hover(function(event){
                            event.stopPropagation();
                            $(this).closest('.icon-image').find(".contentArrow").addClass("trans");
                        });
                        $('#' + this.ID + ' .' + itemTwo.HTML.ID).click(//二层菜单
                            {
                                DrawName: itemTwo.Name,  //把画法名字传进去
                                CurrentIndex:i,
                                CurrentData:itemTwo
                            },
                            function (event) {
                                event.stopPropagation();
                                $(this).closest('.icon-image').find(".contentArrow").hide();
                                $(this).siblings().removeClass("current");
                                $(this).addClass("current");
                                $(this).closest('.icon-image').children('i').eq(0).removeClass().addClass(classname,"active").attr('title',event.data.CurrentData.HTML.Title);
                                menuOne.splice(event.data.CurrentIndex,1,event.data.CurrentData);
                                $(this).parent().hide();
                                hqChart.CreateChartDrawPicture(event.data.DrawName);
                            }
                        );
                    }
                }
            }

        }
        var curID = this.ID;
        $(document).click(function(event){
            if(!($("#"+curID).is(event.target)) && ($("#"+curID).has(event.target).length === 0)){
                $("#"+curID+" .menuTwo").hide();
                $("#"+curID+" .contentArrow").hide();
            }
        });
        var scrollPos = GetScrollPosition();
        var pixelRatio=GetDevicePixelRatio();
        // var left=this.ChartBorder.GetChartWidth()-this.Right-this.ToolsWidth;
        var left=ToFixedPoint(this.ChartBorder.GetRight()+this.Left);
        // var top = this.Top+this.ChartBorder.UIElement.getBoundingClientRect().top+scrollPos.Top;
        var top = this.ChartBorder.GetTop();
        this.ToolsDiv.style.left = left/pixelRatio + "px";
        this.ToolsDiv.style.top = top/pixelRatio + "px";
        this.ToolsDiv.style.width = (this.Width-5)/pixelRatio + "px";
        this.ToolsDiv.style.height = 'auto';
        this.ToolsDiv.style.position = "absolute";
        this.ToolsDiv.style.display = "block";
        // this.ToolsDiv.style.paddingLeft = "10px";

        this.SizeChange == true;
    }
}


//弹幕数据 { X:X偏移, Y:Y偏移, Text:内容, Color:颜色 }
function BarrageList()
{
    this.PlayList=[];   //正在播放队列
    this.Cache=[];      //没有播放的弹幕数据
    this.MinLineHeight=40*GetDevicePixelRatio();
    this.Height;        //高度
    this.Step=1;

    //{Canves:画布, Right:右边坐标, Left:左边坐标, Font:默认字体 }
    this.GetPlayList=function(obj)
    {
        var canves=obj.Canves;
        var right=obj.Right;
        var left=obj.Left;
        var width=right-left;
        var isMoveStep=obj.IsMoveStep;

        var list=[];
        var yOffset=0;
        for(var i=0;i<this.PlayList.length;++i)
        {
            var ary=this.PlayList[i];
            var lineHeight=this.MinLineHeight;
            if (ary.Height>this.MinLineHeight) lineHeight=ary.Height;

            var bAddNewItem=true;  //是否需要加入新弹幕
            var bRemoveFirst=false; //是否删除第1个数据
            for(var j=0;j<ary.Data.length;++j)
            {
                var item=ary.Data[j];
                var playItem={ X:item.X, Y:yOffset, Text:item.Text, Color:item.Color, Height:lineHeight, Font:item.Font, Info:item.Info };
                list.push(playItem);

                if (!isMoveStep) continue;
                
                if(j==ary.Data.length-1 && this.Cache.length>0)    //最后一个数据了 判断是否需要增加弹幕
                {
                    bAddNewItem=false;
                    if (!item.TextWidth)
                    {
                        if (item.Font && item.Font.Name) canves.font=item.Font.Name;
                        else canves.font=obj.Font;
                        item.TextWidth=canves.measureText(playItem.Text+'擎擎').width;
                    }
                    
                    if (item.X>=item.TextWidth) 
                        bAddNewItem=true;
                }
                else if (j==0)
                {
                    bRemoveFirst=false;
                    if (!item.TextWidth)
                    {
                        if (item.Font && item.Font.Name) canves.font=item.Font.Name;
                        else canves.font=obj.Font;
                        item.TextWidth=canves.measureText(playItem.Text+'擎擎').width;
                    }

                    if (item.X>width+item.TextWidth) bRemoveFirst=true;
                }

                item.X+=this.Step;
            }

            if(isMoveStep && bAddNewItem && this.Cache.length>0)    //最后一个数据了 判断是否需要增加弹幕
            {
                var cacheItem=this.Cache.shift();
                var newItem={ X:0, Text:cacheItem.Text, Color:cacheItem.Color , Font:cacheItem.Font, Info:cacheItem.Info };
                ary.Data.push(newItem);
            }

            if (isMoveStep && bRemoveFirst && ary.Data.length>0)
            {
                var removeItem=ary.Data.shift();
                this.OnItemPlayEnd(obj.HQChart,removeItem);
            }

            yOffset+=lineHeight;
        }

        return list;
    }

    //根据高度计算播放队列个数
    this.CacluatePlayLine=function(height)
    {
        this.Height=height;
        var lineCount=parseInt(height/this.MinLineHeight);
        if (this.PlayList.length<lineCount)
        {
            var addCount=lineCount-this.PlayList.length;
            for(var i=0;i<addCount;++i)
            {
                this.PlayList.push({Data:[]});
            }
        }
        else if (this.PlayList.length>lineCount)
        {
            var removeCount=this.PlayList.length-lineCount;
            for(var i=0;i<removeCount;++i)
            {
                var ary=this.PlayList.pop();
                for(var j=0;j<ary.Data.length;++j)
                {
                    var item=ary.Data[j];
                    var cacheItem={ Text:item.Text, Color:item.Color, Font:item.Font, Info:item.Info };
                    this.Cache.unshift(cacheItem);
                }
            }
        }

        JSConsole.Chart.Log(`[BarrageList::CacluatePlayLine] LineCount=${this.PlayList.length} Height=${this.Height}` )
    }

    //添加弹幕
    this.AddBarrage=function(barrageData)
    {
        for(var i in barrageData)
        {
            var item=barrageData[i];
            this.Cache.push(item);
        }
    }

    this.OnItemPlayEnd=function(hqChart, item)  //单挑弹幕播放完毕
    {
        //监听事件
        if (!hqChart.mapEvent.has(JSCHART_EVENT_ID.BARRAGE_PLAY_END)) return;
        var event=hqChart.mapEvent.get(JSCHART_EVENT_ID.BARRAGE_PLAY_END);
        if (!event.Callback) return;

        event.Callback(event,item,this);
    }

    this.Count=function() { return this.Cache.length; } //未播放的弹幕个数
}

//弹幕
function BarragePaint()
{
    this.newMethod=IExtendChartPainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='BarragePaint';
    this.IsAnimation=true;
    this.IsEraseBG=true;
    this.HQChart;

    this.Font=g_JSChartResource.Barrage.Font;
    this.TextColor=g_JSChartResource.Barrage.Color;
    this.FontHeight=g_JSChartResource.Barrage.Height;

    this.BarrageList=new BarrageList();  //字幕列表
    this.IsMoveStep=false;


    //设置参数接口
    this.SetOption=function(option)
    {
        if (option)
        {
            if (option.Step>0) this.BarrageList.Step=option.Step;
            if (option.MinLineHeight) this.Barrage.MinLineHeight=option.MinLineHeight;
        }
    }

    this.DrawHScreen=function()
    {
        var height=this.ChartBorder.GetWidth();
        var left=this.ChartBorder.GetTop();
        var right=this.ChartBorder.GetBottom();
        var top=this.ChartBorder.GetRightEx();
        var wdith=this.ChartBorder.GetChartWidth();

        if (height!=this.BarrageList.Height)
            this.BarrageList.CacluatePlayLine(height);

        this.Canvas.textBaseline="middle";
        this.Canvas.textAlign="left";

        var play=this.BarrageList.GetPlayList({Canves:this.Canvas, Right:right, Left:left, Font:this.Font, IsMoveStep:this.IsMoveStep, HQChart:this.HQChart});
        this.IsMoveStep=false;
        if (!play) return;

        this.Canvas.save(); 
        this.Canvas.translate(this.ChartBorder.GetChartHeight(), 0);
        this.Canvas.rotate(90 * Math.PI / 180);

        for(var i=0;i<play.length;++i)
        {
            var item=play[i];
            if (item.Color) this.Canvas.fillStyle=item.Color;
            else this.Canvas.fillStyle=this.TextColor;
            if (item.Font) this.Canvas.font=item.Font.Name;
            else this.Canvas.font=this.Font;

            var fontHeight=this.FontHeight;
            if (item.Font && item.Font.Height>0) fontHeight=item.Font.Height;
            var yOffset=item.Y+parseInt((item.Height-fontHeight)/2);

            this.Canvas.fillText(item.Text, right-item.X,top+yOffset);
        }

        this.Canvas.restore();
    }

    this.Draw=function()
    {
        if (this.ChartFrame.IsHScreen)
        {
            this.DrawHScreen();
            return;
        }

        var left=this.ChartBorder.GetLeft();
        var right=this.ChartBorder.GetRight();
        var top=this.ChartBorder.GetTopEx();
        var height=this.ChartBorder.GetHeight();

        if (height!=this.BarrageList.Height)
            this.BarrageList.CacluatePlayLine(height);

        this.Canvas.textBaseline="middle";
        this.Canvas.textAlign="left";

        var play=this.BarrageList.GetPlayList({Canves:this.Canvas, Right:right, Left:left, Font:this.Font, IsMoveStep:this.IsMoveStep, HQChart:this.HQChart});
        this.IsMoveStep=false;
        if (!play) return;

        for(var i=0;i<play.length;++i)
        {
            var item=play[i];
            if (item.Color) this.Canvas.fillStyle=item.Color;
            else this.Canvas.fillStyle=this.TextColor;
            if (item.Font) this.Canvas.font=item.Font.Name;
            else this.Canvas.font=this.Font;

            var fontHeight=this.FontHeight;
            if (item.Font && item.Font.Height>0) fontHeight=item.Font.Height;
            var yOffset=item.Y+parseInt((item.Height-fontHeight)/2);

            this.Canvas.fillText(item.Text, right-item.X,top+yOffset);
        }
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//坐标分割
//
//
////////////////////////////////////////////////////////////////////////////////////////////////////
function IFrameSplitOperator()
{
    this.ChartBorder;                   //边框信息
    this.Frame;                         //框架信息
    this.FrameSplitData;                //坐标轴分割方法
    this.SplitCount=5;                  //刻度个数
    this.StringFormat=0;                //刻度字符串格式
    this.IsShowLeftText=true;           //显示左边刻度 
    this.IsShowRightText=true;          //显示右边刻度
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;

    //////////////////////
    // data.Min data.Max data.Interval data.Count
    //
    this.IntegerCoordinateSplit=function(data)
    {
        var splitItem=this.FrameSplitData.Find(data.Interval);
        if (!splitItem) return false;

        if (data.Interval==splitItem.FixInterval) return true;

        //调整到整数倍数,不能整除的 +1
        var fixMax=parseInt((data.Max/(splitItem.FixInterval)+0.5).toFixed(0))*splitItem.FixInterval;
        var fixMin=parseInt((data.Min/(splitItem.FixInterval)-0.5).toFixed(0))*splitItem.FixInterval;
        if (data.Min==0) fixMin=0;  //最小值是0 不用调整了.
        if (fixMin<0 && data.Min>0) fixMin=0;   //都是正数的, 最小值最小调整为0

        var count=0;
        for(var i=fixMin;(i-fixMax)<0.00000001;i+=splitItem.FixInterval)
        {
            ++count;
        }

        data.Interval=splitItem.FixInterval;
        data.Max=fixMax;
        data.Min=fixMin;
        data.Count=count;

        return true;
    }

    this.Filter = function (aryInfo,keepZero) 
    {
        if (this.SplitCount <= 0 || aryInfo.length <= 0 || aryInfo.length < this.SplitCount) return aryInfo;

        //分割线比预设的多, 过掉一些
        var filter = parseInt(aryInfo.length / this.SplitCount);
        if (filter <= 1) filter = 2;
        var data = [];
        for (var i = 0; i < aryInfo.length; i += filter) 
        {
            if (i + filter >= aryInfo.length && i != aryInfo.length - 1) //最后一个数据放进去
            {
                data.push(aryInfo[aryInfo.length - 1]);
            }
            else 
            {
                data.push(aryInfo[i]);
            }
        }

        if (this.SplitCount == 2 && data.length>2) //之显示第1个和最后一个刻度
        {
            for(var i=1;i<data.length-1;++i)
            {
                var item=data[i];
                item.Message[0]=null;
                item.Message[1]=null;
            }
        }

        if (keepZero)   //如果不存在0轴,增加一个0轴,刻度信息部显示
        {
            var bExsitZero=false;
            for(var i=0;i<data;++i)
            {
                var item=data[i];
                if (Math.abs(item.Value) < 0.00000001) 
                {
                    bExsitZero=true;
                    break;
                }
            }

            if (bExsitZero==false)
            {
                var zeroCoordinate = new CoordinateInfo();
                zeroCoordinate.Value = 0;
                zeroCoordinate.Message[0] = null
                zeroCoordinate.Message[1] = null;
                data.push(zeroCoordinate);
            }
        }

        return data;
    }

    this.RemoveZero = function (aryInfo)   //移除小数后面多余的0
    {
        //所有的数字小数点后面都0,才会去掉
        var isAllZero = [true, true];
        for (var i in aryInfo) 
        {
            var item = aryInfo[i];
            var message = item.Message[0];
           if (!this.IsDecimalZeroEnd(message)) isAllZero[0] = false;

            var message = item.Message[1];
            if (!this.IsDecimalZeroEnd(message)) isAllZero[1] = false;
        }

        if (isAllZero[0] == false && isAllZero[1] == false) return;
        for (var i in aryInfo) 
        {
            var item = aryInfo[i];
            if (isAllZero[0]) 
            {
                var message = item.Message[0];
                if (message!=null)
                {
                    if (typeof (message) == 'number') message = message.toString();
                    item.Message[0] = message.replace(/[.][0]+/g, '');
                }
            }

            if (isAllZero[1])
            {
                var message = item.Message[1];
                if (message!=null)
                {
                    if (typeof (message) == 'number') message = message.toString();
                    item.Message[1] = message.replace(/[.][0]+/g, '');
                }
            }
        }
    }

    this.IsDecimalZeroEnd = function (text)   //是否是0结尾的小数
    {
        if (text==null) return true;
        if (typeof(text)=='number') text=text.toString();
        if (text=='0') return true;
        
        var pos = text.search(/[.]/);
        if (pos < 0) return false;

        for (var i = pos + 1; i < text.length; ++i) 
        {
            var char = text.charAt(i);
            if (char >= '1' && char <= '9') return false;
        }

        return true;
    }
    
}

//字符串格式化 千分位分割
IFrameSplitOperator.FormatValueThousandsString=function(value,floatPrecision)
{
    if (value==null || isNaN(value))
    {
        if (floatPrecision>0)
        {
            var nullText='-.';
            for(var i=0;i<floatPrecision;++i)
                nullText+='-';
            return nullText;
        }

        return '--';
    }

    var result='';
    var num=value.toFixed(floatPrecision);
    if(floatPrecision>0){
        var numFloat = num.split('.')[1];
        var numM = num.split('.')[0];
        while (numM.length > 3)
        {
            result = ',' + numM.slice(-3) + result;
            numM = numM.slice(0, numM.length - 3);
        }
        if (numM) { result = numM + result + '.' + numFloat; }
    }else{
        while (num.length > 3)
        {
            result = ',' + num.slice(-3) + result;
            num = num.slice(0, num.length - 3);
        }
        if (num) { result = num + result; }
    }
    
    return result;
}

//数据输出格式化 floatPrecision=小数位数
IFrameSplitOperator.FormatValueString=function(value, floatPrecision,languageID)
{
    if (value==null || isNaN(value))
    {
        if (floatPrecision>0)
        {
            var nullText='-.';
            for(var i=0;i<floatPrecision;++i)
                nullText+='-';
            return nullText;
        }

        return '--';
    }

    if (value<0.00000000001 && value>-0.00000000001)
    {
        return "0";
    }

    var absValue = Math.abs(value);
    if (languageID===JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID)
    {
        if (absValue < 10000)
            return value.toFixed(floatPrecision);
        else if (absValue < 1000000)
            return (value/1000).toFixed(floatPrecision)+"K";
        else if (absValue < 1000000000)
            return (value/1000000).toFixed(floatPrecision)+"M";
        else if (absValue < 1000000000000)
            return (value/1000000000).toFixed(floatPrecision)+"B";
        else 
            return (value/1000000000000).toFixed(floatPrecision)+"T";
    }
    else
    {
        if (absValue < 10000)
            return value.toFixed(floatPrecision);
        else if (absValue < 100000000)
            return (value/10000).toFixed(floatPrecision)+"万";
        else if (absValue < 1000000000000)
            return (value/100000000).toFixed(floatPrecision)+"亿";
        else
            return (value/1000000000000).toFixed(floatPrecision)+"万亿";
    }

    return '';
}

//整形输出格式化 floatPrecision=小数位数
IFrameSplitOperator.FromatIntegerString=function(value, floatPrecision,languageID)
{
    if (value<10000 && IFrameSplitOperator.IsInteger(value)) floatPrecision=0;  //<10000的整形 去掉小数位数
    return IFrameSplitOperator.FormatValueString(value, floatPrecision,languageID);
}

IFrameSplitOperator.NumberToString=function(value)
{
    if (value<10) return '0'+value.toString();
    return value.toString();
}

IFrameSplitOperator.FormatDateString=function(value,format)
{
    var year=parseInt(value/10000);
    var month=parseInt(value/100)%100;
    var day=value%100;

    switch(format)
    {
        case 'MM-DD':
            return IFrameSplitOperator.NumberToString(month) + '-' + IFrameSplitOperator.NumberToString(day);
        default:
            return year.toString() + '-' + IFrameSplitOperator.NumberToString(month) + '-' + IFrameSplitOperator.NumberToString(day);
    }
}

IFrameSplitOperator.FormatTimeString=function(value, format)    //format hh:mm:ss
{
    if (format=='HH:MM:SS')
    {
        var hour=parseInt(value/10000);
        var minute=parseInt((value%10000)/100);
        var second=value%100;
        return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute) + ':' + IFrameSplitOperator.NumberToString(second);
    }
    else if (format=='HH:MM')
    {
        var hour=parseInt(value/100);
        var minute=value%100;
        return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute);
    }
    else
    {
        if (value<10000)
        {
            var hour=parseInt(value/100);
            var minute=value%100;
            return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute);
        }
        else
        {
            var hour=parseInt(value/10000);
            var minute=parseInt((value%10000)/100);
            var second=value%100;
            return IFrameSplitOperator.NumberToString(hour)+':'+ IFrameSplitOperator.NumberToString(minute) + ':' + IFrameSplitOperator.NumberToString(second);
        }
    }
}

//报告格式化
IFrameSplitOperator.FormatReportDateString=function(value)
{
    var year=parseInt(value/10000);
    var month=parseInt(value/100)%100;
    var monthText;
    switch(month)
    {
        case 3:
            monthText="一季度报";
            break;
        case 6:
            monthText="半年报";
            break;
        case 9:
            monthText="三季度报";
            break;
        case 12:
            monthText="年报";
            break;
    }

    return year.toString()+ monthText;
}

IFrameSplitOperator.FormatDateTimeString=function(value,isShowDate)
{
    var aryValue=value.split(' ');
    if (aryValue.length<2) return "";
    var time=parseInt(aryValue[1]);
    var minute=time%100;
    var hour=parseInt(time/100);
    var text=(hour<10? ('0'+hour.toString()):hour.toString()) + ':' + (minute<10?('0'+minute.toString()):minute.toString());

    if (isShowDate==true)
    {
        var date=parseInt(aryValue[0]);
        var year=parseInt(date/10000);
        var month=parseInt(date%10000/100);
        var day=date%100;
        text=year.toString() +'-'+ (month<10? ('0'+month.toString()) :month.toString()) +'-'+ (day<10? ('0'+day.toString()):day.toString()) +" " +text;
    }

    return text;
}

//字段颜色格式化
IFrameSplitOperator.FormatValueColor = function (value, value2) 
{
    if (value != null && value2 == null)  //只传一个值的 就判断value正负
    {
        if (value == 0) return 'PriceNull';
        else if (value > 0) return 'PriceUp';
        else return 'PriceDown';
    }

    //2个数值对比 返回颜色
    if (value == null || value2 == null) return 'PriceNull';
    if (value == value2) return 'PriceNull';
    else if (value > value2) return 'PriceUp';
    else return 'PriceDown';
}

IFrameSplitOperator.IsNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;

    return true;
}

//判断是否是正数
IFrameSplitOperator.IsPlusNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;

    return value>0;
}

//是否是整形
IFrameSplitOperator.IsInteger=function(x) 
{
    return (typeof x === 'number') && (x % 1 === 0);
}

//判断字段是否存在
IFrameSplitOperator.IsObjectExist=function(obj)
{
    if (obj===undefined) return false;
    if (obj==null) return false;
    
    return true;
}

//是否时bool
IFrameSplitOperator.IsBool=function(value)
{
    if (value===true || value===false) return true;
    return false;
}

IFrameSplitOperator.IsString=function(value)
{
    if (value && typeof(value)=='string') return true;
    return false;
}

function FrameSplitKLinePriceY()
{
    this.newMethod=IFrameSplitOperator;   //派生
    this.newMethod();
    delete this.newMethod;
    this.CoordinateType=0;  //坐标类型 0=普通坐标  1=百分比坐标 (右边坐标刻度)
    this.Symbol;
    this.Data;              //K线数据 (计算百分比坐标)
    this.FrameSplitData2;                //坐标轴分割方法(计算百分比刻度)
    this.FloatPrecision=null;   //小数位数 (设置了就使用这个位数,否则使用品种对应的小数位数)

    this.Period;            //周期
    this.KLineChart;
    this.Custom=[]; //[{Type:0}];   定制刻度 0=显示最后的价格刻度
    this.SplitType=0;       //0=自动分割  1=固定分割

    this.Operator=function()
    {
        var splitData={};
        splitData.Max=this.Frame.HorizontalMax;
        splitData.Min=this.Frame.HorizontalMin;
        splitData.Count=this.SplitCount;
        splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1);
        var pixelTatio = GetDevicePixelRatio();             //获取设备的分辨率
        var width=this.Frame.ChartBorder.GetChartWidth();   //画布的宽度
        var isPhoneModel=width<450*pixelTatio;
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        if (isPhoneModel && MARKET_SUFFIX_NAME.IsSHSZIndex(this.Symbol)) defaultfloatPrecision = 0;    //手机端指数不显示小数位数,太长了
        if (this.FloatPrecision!=null) defaultfloatPrecision=this.FloatPrecision;
        JSConsole.Chart.Log(`[FrameSplitKLinePriceY::Operator] Max=${splitData.Max} Min=${splitData.Min} Count=${splitData.Count} isPhoneModel=${isPhoneModel} defaultfloatPrecision=${defaultfloatPrecision} `);

        var bFilter=true;   //是否需要通过高度过滤刻度
        if (ChartData.IsTickPeriod(this.Period))
        {
            this.SplitTickData(splitData,defaultfloatPrecision);
        }
        else
        {
            switch(this.CoordinateType)
            {
                case 1:
                    this.SplitPercentage(splitData,defaultfloatPrecision);
                    break;
                default:
                    if (this.SplitType==1) 
                    {
                        this.SplitFixed(splitData,defaultfloatPrecision);
                        bFilter=false;
                    }
                    else 
                    {
                        this.SplitDefault(splitData,defaultfloatPrecision);
                    }
                    break;
            }
        }

        this.CustomCoordinate(defaultfloatPrecision);
        if (bFilter) this.Frame.HorizontalInfo = this.Filter(this.Frame.HorizontalInfo,false);
        this.Frame.HorizontalMax=splitData.Max;
        this.Frame.HorizontalMin=splitData.Min;

        JSConsole.Chart.Log(`[FrameSplitKLinePriceY::Operator] fixed . Max=${splitData.Max} Min=${splitData.Min} Count=${splitData.Count}`);
    }

    this.SplitTickData=function(splitData,floatPrecision)
    {
        var aryPrice=this.KLineChart.GetAllPrice();

        this.Frame.HorizontalInfo=[];
        for(var i in aryPrice)
        {
            var value=aryPrice[i];
            this.Frame.HorizontalInfo[i]= new CoordinateInfo();
            this.Frame.HorizontalInfo[i].Value=value;
            if (this.IsShowLeftText) this.Frame.HorizontalInfo[i].Message[0]=value.toFixed(floatPrecision);
            if (this.IsShowRightText) this.Frame.HorizontalInfo[i].Message[1]=value.toFixed(floatPrecision);
        }
    }

    this.SplitPercentage=function(splitData,floatPrecision)    //百分比坐标
    {
        var firstOpenPrice=this.GetFirstOpenPrice();
        splitData.Max=(splitData.Max-firstOpenPrice)/firstOpenPrice;
        splitData.Min=(splitData.Min-firstOpenPrice)/firstOpenPrice;
        splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1);
        this.IntegerCoordinateSplit2(splitData);

        this.Frame.HorizontalInfo=[];
        for(var i=0,value=splitData.Min;i<splitData.Count;++i,value+=splitData.Interval)
        {
            var price=(value+1)*firstOpenPrice;
            this.Frame.HorizontalInfo[i]= new CoordinateInfo();
            this.Frame.HorizontalInfo[i].Value=price;
            if (this.IsShowLeftText) this.Frame.HorizontalInfo[i].Message[0]=price.toFixed(floatPrecision);   //左边价格坐标      
            if (this.IsShowRightText) this.Frame.HorizontalInfo[i].Message[1]=(value*100).toFixed(2)+'%';      //右边百分比
        }

        splitData.Min=(1+splitData.Min)*firstOpenPrice; //最大最小值调整
        splitData.Max=(1+splitData.Max)*firstOpenPrice;
    }

    this.SplitDefault=function(splitData,floatPrecision)       //默认坐标
    {
        this.IntegerCoordinateSplit(splitData);
       
        this.Frame.HorizontalInfo=[];
        for(var i=0,value=splitData.Min;i<splitData.Count;++i,value+=splitData.Interval)
        {
            this.Frame.HorizontalInfo[i]= new CoordinateInfo();
            this.Frame.HorizontalInfo[i].Value=value;
            if (this.IsShowLeftText)  this.Frame.HorizontalInfo[i].Message[0]=value.toFixed(floatPrecision);
            if (this.IsShowRightText)   this.Frame.HorizontalInfo[i].Message[1]=value.toFixed(floatPrecision);
        }
    }

    this.SplitFixed=function(splitData,floatPrecision)      //固定分割坐标
    {
        this.Frame.HorizontalInfo=[];
        for(var i=0,value=splitData.Min;i<splitData.Count;++i,value+=splitData.Interval)
        {
            this.Frame.HorizontalInfo[i]= new CoordinateInfo();
            this.Frame.HorizontalInfo[i].Value=value;
            if (this.IsShowLeftText)  this.Frame.HorizontalInfo[i].Message[0]=value.toFixed(floatPrecision);
            if (this.IsShowRightText)   this.Frame.HorizontalInfo[i].Message[1]=value.toFixed(floatPrecision);
        }
    }

    this.CustomCoordinate=function(floatPrecision)
    {
        this.Frame.CustomHorizontalInfo=[];
        for(var i in this.Custom)
        {
            var item=this.Custom[i];
            if (item.Type==0)
            {
                var latestItem=this.GetLatestPrice(floatPrecision,item);
                if (latestItem) this.Frame.CustomHorizontalInfo.push(latestItem);
            }
            else if (item.Type==1)
            {
                this.CustomFixedCoordinate(item);
            }
        }
    }

    this.GetLatestPrice=function(floatPrecision,option)
    {
        if (!this.Data || !this.Data.Data) return null;
        if (this.Data.Data.length<=0) return null;
        var latestItem=this.Data.Data[this.Data.Data.length-1];
        var info=new CoordinateInfo();
        info.Type=0;
        info.Value=latestItem.Close;
        info.TextColor=g_JSChartResource.FrameLatestPrice.TextColor;
        if (option.Position=='left') info.Message[0]=latestItem.Close.toFixed(floatPrecision);
        else info.Message[1]=latestItem.Close.toFixed(floatPrecision);
        if (latestItem.Close>latestItem.Open) info.LineColor=g_JSChartResource.FrameLatestPrice.UpBarColor;
        else if (latestItem.Close<latestItem.Open) info.LineColor=g_JSChartResource.FrameLatestPrice.DownBarColor;
        else info.LineColor=g_JSChartResource.FrameLatestPrice.UnchagneBarColor;

        if (option.IsShowLine==false) info.LineType=-1;

        return info;
    }

    this.GetFirstOpenPrice=function()   //获取显示第1个数据的开盘价
    {
        if (!this.Data) return null;

        var xPointCount=this.Frame.XPointCount;
        for(var i=this.Data.DataOffset,j=0;i<this.Data.Data.length && j<xPointCount;++i,++j)
        {
            var data=this.Data.Data[i];
            if (data.Open==null || data.High==null || data.Low==null || data.Close==null) continue;

            return data.Open;
        }

        return null;
    }

    this.CustomFixedCoordinate=function(option)    //固定坐标刻度
    {
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        for(var i in option.Data)
        {
            var item=option.Data[i];
            var info=new CoordinateInfo();
            info.Type=1;
            info.TextColor=item.TextColor;
            info.LineColor=item.Color;
            info.LineType=2;    //虚线
            if (option.IsShowLine==false) info.LineType=-1;

            info.Value=item.Value;
            var text;
            if (item.Text) text=item.Text;
            else text=info.Value.toFixed(defaultfloatPrecision);
            if (option.Position=='left') info.Message[0]=text;
            else info.Message[1]=text;

            this.Frame.CustomHorizontalInfo.push(info);
        }
    }

    //////////////////////
    // data.Min data.Max data.Interval data.Count
    //
    this.IntegerCoordinateSplit2=function(data)
    {
        var splitItem=this.FrameSplitData2.Find(data.Interval);
        if (!splitItem) return false;

        if (data.Interval==splitItem.FixInterval) return true;

        //调整到整数倍数,不能整除的 +1
        var fixMax=parseInt((data.Max/(splitItem.FixInterval)+0.5).toFixed(0))*splitItem.FixInterval;
        var fixMin=parseInt((data.Min/(splitItem.FixInterval)-0.5).toFixed(0))*splitItem.FixInterval;
        if (data.Min==0) fixMin=0;  //最小值是0 不用调整了.
        if (fixMin<0 && data.Min>0) fixMin=0;   //都是正数的, 最小值最小调整为0

        var count=0;
        for(var i=fixMin;(i-fixMax)<0.00000001;i+=splitItem.FixInterval)
        {
            ++count;
        }

        data.Interval=splitItem.FixInterval;
        data.Max=fixMax;
        data.Min=fixMin;
        data.Count=count;

        return true;
    }
}

function FrameSplitY()
{
    this.newMethod=IFrameSplitOperator;   //派生
    this.newMethod();
    delete this.newMethod;

    this.SplitCount=3;                        //刻度个数
    this.FloatPrecision = 2;                  //坐标小数位数(默认2)
    this.FLOATPRECISION_RANGE=[1,0.1,0.01,0.001,0.0001];

    this.GetFloatPrecision=function(value,floatPrecision)
    {
        if (value>this.FLOATPRECISION_RANGE[0]) return floatPrecision;
        if (floatPrecision<0) return 2;
        for(;floatPrecision<this.FLOATPRECISION_RANGE.length;++floatPrecision)
        {
            if (value>this.FLOATPRECISION_RANGE[floatPrecision]) break;
        }

        return floatPrecision;
    }

    this.Operator=function()
    {
        var splitData={};
        splitData.Max=this.Frame.HorizontalMax;
        splitData.Min=this.Frame.HorizontalMin;
        if(this.Frame.YSpecificMaxMin)
        {
            splitData.Count=this.Frame.YSpecificMaxMin.Count;
            splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1);
        }
        else
        {
            splitData.Count=this.SplitCount;
            splitData.Interval=(splitData.Max-splitData.Min)/(splitData.Count-1);
            this.IntegerCoordinateSplit(splitData);
        }

        this.Frame.HorizontalInfo=[];

        if (this.Frame.YSplitScale) //固定坐标
        {
            for(var i in this.Frame.YSplitScale)
            {
                var value=this.Frame.YSplitScale[i];
                var coordinate=new CoordinateInfo();
                coordinate.Value=value;

                var absValue=Math.abs(value);
                if (absValue<0.0000000001) 
                {
                    coordinate.Message[1]=0;
                }
                else if (absValue<this.FLOATPRECISION_RANGE[this.FLOATPRECISION_RANGE.length-1]) 
                {
                    coordinate.Message[1] = value.toExponential(2).toString();
                }
                else
                {
                    var floatPrecision=this.GetFloatPrecision(absValue,this.FloatPrecision); //数据比小数位数还小, 调整小数位数
                    coordinate.Message[1] = IFrameSplitOperator.FormatValueString(value, floatPrecision,this.LanguageID);
                }

                coordinate.Message[0]=coordinate.Message[1];

                if (this.IsShowLeftText==false) this.Frame.HorizontalInfo[i].Message[0]=null;
                if (this.IsShowRightText==false) this.Frame.HorizontalInfo[i].Message[1]=null;

                this.Frame.HorizontalInfo.push(coordinate);
            }
        }
        else
        {
            for(var i=0,value=splitData.Min;i<splitData.Count;++i,value+=splitData.Interval)
            {
                this.Frame.HorizontalInfo[i]= new CoordinateInfo();
                this.Frame.HorizontalInfo[i].Value=value;

                if (this.StringFormat==1)   //手机端格式 如果有万,亿单位了 去掉小数
                {
                    var floatPrecision=this.FloatPrecision;
                    if (!isNaN(value) && Math.abs(value) > 1000) floatPrecision=0;
                    this.Frame.HorizontalInfo[i].Message[1]=IFrameSplitOperator.FormatValueString(value,floatPrecision,this.LanguageID);
                }
                else
                {
                    var absValue=Math.abs(value);
                    if (absValue<0.0000000001) 
                    {
                        this.Frame.HorizontalInfo[i].Message[1]=0;
                    }
                    else if (absValue<this.FLOATPRECISION_RANGE[this.FLOATPRECISION_RANGE.length-1]) 
                    {
                        this.Frame.HorizontalInfo[i].Message[1] = value.toExponential(2).toString();
                    }
                    else
                    {
                        var floatPrecision=this.GetFloatPrecision(absValue,this.FloatPrecision); //数据比小数位数还小, 调整小数位数
                        this.Frame.HorizontalInfo[i].Message[1] = IFrameSplitOperator.FormatValueString(value, floatPrecision,this.LanguageID);
                    }
                }
                
                this.Frame.HorizontalInfo[i].Message[0]=this.Frame.HorizontalInfo[i].Message[1];
                if (this.IsShowLeftText==false) this.Frame.HorizontalInfo[i].Message[0]=null;
                if (this.IsShowRightText==false) this.Frame.HorizontalInfo[i].Message[1]=null;
                //this.Frame.HorizontalInfo[i].Font="14px 微软雅黑";
                //this.Frame.HorizontalInfo[i].TextColor="rgb(100,0,200)";
                //this.Frame.HorizontalInfo[i].LineColor="rgb(220,220,220)";
            }
        }

        this.Frame.HorizontalInfo = this.Filter(this.Frame.HorizontalInfo,(splitData.Max>0 && splitData.Min<0));
        this.RemoveZero(this.Frame.HorizontalInfo);
        this.Frame.HorizontalMax=splitData.Max;
        this.Frame.HorizontalMin=splitData.Min;

        this.RightFrameSplitY();
    }

    this.RightFrameSplitY=function()
    {
        if (!this.Frame.RightFrame) return;

        var rightFrame=this.Frame.RightFrame;
        for(var i in this.Frame.HorizontalInfo)
        {
            var item=this.Frame.HorizontalInfo[i];
            var y=this.Frame.GetYFromData(item.Value);
            var yValue=rightFrame.GetYData(y);
            item.Message[1] = IFrameSplitOperator.FormatValueString(yValue, this.FloatPrecision,this.LanguageID);
        }
    }
}

function FrameSplitKLineX()
{
    this.newMethod=IFrameSplitOperator;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ShowText=true;                 //是否显示坐标信息
    this.Period;                        //周期
    this.Symbol;                        //股票代码
    this.MinTextDistance=50*GetDevicePixelRatio();
    this.MinBarDistance=5; //刻度间最小的K线间距


    this.SplitDateTime=function()   //根据时间分割
    {
        this.Frame.VerticalInfo=[];
        var itemWidth=this.Frame.DistanceWidth+this.Frame.DataWidth;
        var xOffset=this.Frame.Data.DataOffset;
        var xPointCount=this.Frame.XPointCount;
        var lastYear=null, lastMonth=null;
        var textDistance=0;
        var barDistance=0;
        for(var i=0, index=xOffset; i<xPointCount && index<this.Frame.Data.Data.length; ++i,++index)
        {
            textDistance+=itemWidth;
            ++barDistance;
            var infoData=null;
            if (i==0)
            {
                var date=IFrameSplitOperator.FormatDateString(this.Frame.Data.Data[index].Date,'MM-DD');
                infoData={Value:index-xOffset, Text:date};
            }
            else if (textDistance>this.MinTextDistance && barDistance>=this.MinBarDistance)
            {
                var time=IFrameSplitOperator.FormatTimeString(this.Frame.Data.Data[index].Time);
                infoData={Value:index-xOffset, Text:time};
            }

            if (infoData)
            {
                var info= new CoordinateInfo();
                info.Value=infoData.Value;
                if (this.ShowText) info.Message[0]=infoData.Text;
                this.Frame.VerticalInfo.push(info);
                textDistance=0;
                barDistance=0;
                if (i==0) textDistance=-(this.MinTextDistance/2);
            }
        }
    }

    this.SplitSecond=function()   //根据时间分割
    {
        this.Frame.VerticalInfo=[];
        var itemWidth=this.Frame.DistanceWidth+this.Frame.DataWidth;
        var xOffset=this.Frame.Data.DataOffset;
        var xPointCount=this.Frame.XPointCount;
        var textDistance=0;
        var barDistance=0;

        for(var i=0, index=xOffset; i<xPointCount && index<this.Frame.Data.Data.length; ++i,++index)
        {
            textDistance+=itemWidth;
            ++barDistance;
            var infoData=null;
            if (i==0)
            {
                var date=IFrameSplitOperator.FormatDateString(this.Frame.Data.Data[index].Date,'MM-DD');
                infoData={Value:index-xOffset, Text:date};
            }
            else if (textDistance>this.MinTextDistance && barDistance>=this.MinBarDistance)
            {
                var time=IFrameSplitOperator.FormatTimeString(this.Frame.Data.Data[index].Time,"HH:MM:SS");
                infoData={Value:index-xOffset, Text:time};
            }

            if (infoData)
            {
                var info= new CoordinateInfo();
                info.Value=infoData.Value;
                if (this.ShowText) info.Message[0]=infoData.Text;
                this.Frame.VerticalInfo.push(info);
                textDistance=0;
                barDistance=0;
                if (i==0) textDistance=-(this.MinTextDistance/2);
            }
        }
    }

    this.SplitDate=function()   //根据日期分割
    {
        this.Frame.VerticalInfo=[];
        var xOffset=this.Frame.Data.DataOffset;
        var xPointCount=this.Frame.XPointCount;
        var lastYear=null, lastMonth=null;
        var minDistance=12;
        for(var i=0, index=xOffset, distance=minDistance;i<xPointCount && index<this.Frame.Data.Data.length ;++i,++index)
        {
            var year=parseInt(this.Frame.Data.Data[index].Date/10000);
            var month=parseInt(this.Frame.Data.Data[index].Date/100)%100;

            if (distance<minDistance ||
                (lastYear!=null && lastYear==year && lastMonth!=null && lastMonth==month))
            {
                lastMonth=month;
                ++distance;
                continue;
            }

            var info= new CoordinateInfo();
            info.Value=index-xOffset;
            var text;
            if (lastYear==null || lastYear!=year)
            {
                text=year.toString();
            }
            else if (lastMonth==null || lastMonth!=month)
            {
                text=month.toString()+'月';
                text=g_JSChartLocalization.GetText(text, this.LanguageID);
            }

            lastYear=year;
            lastMonth=month;
            if (this.ShowText)
            {
                info.Message[0]=text;
            }

            this.Frame.VerticalInfo.push(info);
            distance=0;
        }
    }

    this.Operator=function()
    {
        if (this.Frame.Data==null) return;
        if (FrameSplitKLineX.SplitCustom) FrameSplitKLineX.SplitCustom(this);   //自定义分割
        else if (ChartData.IsMinutePeriod(this.Period, true)) this.SplitDateTime();
        else if (ChartData.IsSecondPeriod(this.Period)) this.SplitSecond();
        else this.SplitDate();
    }

    this.CreateCoordinateInfo=function()
    {
        return new CoordinateInfo();
    }
}

//FrameSplitKLineX.SplitCustom=function(split) { }

function FrameSplitMinutePriceY()
{
    this.newMethod=IFrameSplitOperator;   //派生
    this.newMethod();
    delete this.newMethod;

    this.High=null;                 //最高最低价
    this.Low=null;

    this.YClose;                        //昨收
    this.Data;                          //分钟数据
    this.AverageData;                   //分钟均线数据
    this.SourceData;                    //原始数据
    this.OverlayChartPaint;
    this.SplitCount=7;
    this.Symbol;
    this.SplitType=0;                   //0=默认根据最大最小值分割 1=涨跌停分割
    this.LimitPrice;
    this.Custom;

    this.Operator=function()
    {
        this.Frame.HorizontalInfo=[];
        this.Frame.CustomHorizontalInfo=[];
        if (!this.Data) return;

        var range=this.GetMaxMin();

        if (this.Symbol && MARKET_SUFFIX_NAME.IsUSA(this.Symbol.toUpperCase()))
        {
            this.USASplit(range);
        }
        else
        {
            this.DefaultSplit(range);
        }

        this.CustomCoordinate();
    }

    this.CustomCoordinate=function()    //自定义刻度
    {
        if (!this.Custom) return;

        for(var i in this.Custom)
        {
            var item=this.Custom[i];
            if (item.Type==1) this.CustomFixedCoordinate(item);
        }
    }

    this.CustomFixedCoordinate=function(option)    //固定坐标刻度
    {
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        for(var i in option.Data)
        {
            var item=option.Data[i];
            var info=new CoordinateInfo();
            info.Type=1;
            info.TextColor=item.TextColor;
            info.LineColor=item.Color;
            info.LineType=2;    //虚线
            if (option.IsShowLine==false) info.LineType=-1;

            if (IFrameSplitOperator.IsNumber(item.Increase)) //涨幅计算价格
            {
                if (!IFrameSplitOperator.IsNumber(this.YClose)) conintue;
                info.Value=this.YClose*(1+item.Increase);
            }
            else 
            {
                info.Value=item.Value;
            }
            
            var text;
            if (item.Text) text=item.Text;
            else text=info.Value.toFixed(defaultfloatPrecision);
            if (option.Position=='left') info.Message[0]=text;
            else info.Message[1]=text;

            this.Frame.CustomHorizontalInfo.push(info);
        }
    }

    this.GetMaxMin=function()   //计算图中所有的数据的最大最小值
    {
        var max=this.YClose;
        var min=this.YClose;
        var data=this.Data;
        var isBerforeData=false;
        if (this.SourceData)
        {
            data=this.SourceData;
            isBerforeData=true;
        }

        for(var i in data.Data)
        {
            var value=null;
            if (isBerforeData)
            {
                var item=data.Data[i];
                if (item.Before) value=item.Before.Close;
                else value=item.Close;
            }
            else
            {
                value=data.Data[i];
            }
            if (value==null) continue;
            if (max<value) max=value;
            if (min>value) min=value;
        }
        
        if (this.AverageData)
        {
            for(var i in this.AverageData.Data)
            {
                if (this.AverageData.Data[i]==null) continue;
                if (max<this.AverageData.Data[i]) max=this.AverageData.Data[i];
                if (min>this.AverageData.Data[i]) min=this.AverageData.Data[i];
            }
        }
        
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            //if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue;
            var range=item.GetMaxMin();
            if (range.Max && range.Max>max) max=range.Max;
            if (range.Min && range.Min<min) min=range.Min;
        }

        if (this.SplitType==1 && this.LimitPrice)
        {
            if (max<this.LimitPrice.Max) max=this.LimitPrice.Max;
            if (min>this.LimitPrice.Min) min=this.LimitPrice.Min;
        }

        if (IFrameSplitOperator.IsNumber(this.High) && IFrameSplitOperator.IsNumber(this.Low))
        {
            if (max<this.High) max=this.High;
            if (min>this.Low) min=this.Low;
        }

        return { Max:max, Min:min };
    }

    this.USASplit=function(range)
    {
        var max=range.Max;
        var min=range.Min;

        if (max==min)
        {
            max=max+max*0.1;
            min=min-min*0.1;
        }
        else
        {
            var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
            var height=this.Frame.ChartBorder.GetHeight();   //画布的高度
            var spacePrice=5*pixelTatio*(max-min)/height;
            max+=spacePrice;
            min-=spacePrice;
        }

        var showCount=this.SplitCount;
        var distance=(max-min)/(showCount-1);
        const minDistance=[1, 0.1, 0.01, 0.001, 0.0001];
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        if (distance<minDistance[defaultfloatPrecision]) 
        {
            distance=minDistance[defaultfloatPrecision];
            max=min+distance*showCount;
            //min=this.YClose-(distance*(showCount-1)/2);
        }

        for(var i=0;i<showCount;++i)
        {
            var price=min+(distance*i);
            var coordinate=new CoordinateInfo();
            coordinate.Value=price;
            if (this.IsShowLeftText) coordinate.Message[0]=price.toFixed(defaultfloatPrecision);

            if (this.YClose)
            {
                var per=(price/this.YClose-1)*100;
                if (per>0) coordinate.TextColor=g_JSChartResource.UpTextColor;
                else if (per<0) coordinate.TextColor=g_JSChartResource.DownTextColor;
                if (this.IsShowRightText) coordinate.Message[1]=IFrameSplitOperator.FormatValueString(per,2)+'%'; //百分比
            }

            this.Frame.HorizontalInfo.push(coordinate);
        }

        if (this.YClose>min && this.YClose<max)
        {
            var coordinate=new CoordinateInfo();
            coordinate.Value=this.YClose;
            this.Frame.HorizontalInfo.push(coordinate);
        }

        this.Frame.HorizontalInfo.sort(function(a,b) { return a.Value-b.Value; });

        this.Frame.HorizontalMax=max;
        this.Frame.HorizontalMin=min;
    }

    this.DefaultSplit=function(range)
    {
        var max=range.Max;
        var min=range.Min;
        
        if (this.YClose==max && this.YClose==min)
        {
            max=this.YClose+this.YClose*0.1;
            min=this.YClose-this.YClose*0.1
        }
        else
        {
            var distanceValue=Math.max(Math.abs(this.YClose-max),Math.abs(this.YClose-min));
            max=this.YClose+distanceValue;
            min=this.YClose-distanceValue;
        }

        var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
        var width=this.Frame.ChartBorder.GetChartWidth();   //画布的宽度
        var isPhoneModel=width<450*pixelTatio;
        JSConsole.Chart.Log('[FrameSplitMinutePriceY]'+ 'max='+ max + ' min='+ min +' isPhoneModel='+isPhoneModel);

        var showCount=this.SplitCount;
        var distance=(max-min)/(showCount-1);
        const minDistance=[1, 0.1, 0.01, 0.001, 0.0001];
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
        if (isPhoneModel && MARKET_SUFFIX_NAME.IsSHSZIndex(this.Symbol)) defaultfloatPrecision = 0;    //手机端指数不显示小数位数,太长了
        if (distance<minDistance[defaultfloatPrecision]) 
        {
            distance=minDistance[defaultfloatPrecision];
            max=this.YClose+(distance*(showCount-1)/2);
            min=this.YClose-(distance*(showCount-1)/2);
        }

        for(var i=0;i<showCount;++i)
        {
            var price=min+(distance*i);
            this.Frame.HorizontalInfo[i]= new CoordinateInfo();
            this.Frame.HorizontalInfo[i].Value=price;
            if (this.IsShowLeftText) this.Frame.HorizontalInfo[i].Message[0]=price.toFixed(defaultfloatPrecision);
            if (price==this.YClose) 
            {
                this.Frame.HorizontalInfo[i].LineType=2;//中间的线画虚线
                if (g_JSChartResource.FrameDotSplitPen) this.Frame.HorizontalInfo[i].LineColor=g_JSChartResource.FrameDotSplitPen;
            }

            if (this.YClose)
            {
                var per=(price/this.YClose-1)*100;
                if (per>0) this.Frame.HorizontalInfo[i].TextColor=g_JSChartResource.UpTextColor;
                else if (per<0) this.Frame.HorizontalInfo[i].TextColor=g_JSChartResource.DownTextColor;
                if (this.IsShowRightText) this.Frame.HorizontalInfo[i].Message[1]=IFrameSplitOperator.FormatValueString(per,2)+'%'; //百分比
            }
        }

        this.Frame.HorizontalMax=max;
        this.Frame.HorizontalMin=min;
    }

}

function FrameSplitMinuteX()
{
    this.newMethod=IFrameSplitOperator;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ShowText=true;                 //是否显示坐标信息
    this.Symbol=null;                   //股票代码 x轴刻度根据股票类型来调整
    this.DayCount=1;
    this.ShowDateFormate=1;             //0=YYYY-MM-DD  1=MM-DD
    this.IsBeforeData=false;            //是否显示盘前数据

    this.Operator=function()
    {
        this.Frame.VerticalInfo=[];
        var xPointCount=this.Frame.XPointCount;
        var width = this.Frame.ChartBorder.GetWidth();
        var isHScreen=(this.Frame.IsHScreen===true);
        if (isHScreen) width = this.Frame.ChartBorder.GetHeight();
        width=width/GetDevicePixelRatio();

        const minuteCoordinate = g_MinuteCoordinateData;
        var xcoordinateData = minuteCoordinate.GetCoordinateData(this.Symbol,width);
        var minuteCount=xcoordinateData.Count;
        var minuteMiddleCount=xcoordinateData.MiddleCount>0? xcoordinateData.MiddleCount: parseInt(minuteCount/2);
        var xcoordinate = xcoordinateData.Data;

        this.Frame.XPointCount=minuteCount*this.DayCount;
        this.Frame.MinuteCount=minuteCount;
        this.Frame.VerticalInfo=[];
        
        if (this.DayCount<=1)
        {
            var beforeDataCount=14;
            if (this.IsBeforeData) //增加盘前的数据个数
            {
                this.Frame.XPointCount=minuteCount+beforeDataCount;
                this.Frame.MinuteCount=minuteCount+beforeDataCount;
            }
            for(var i in xcoordinate)
            {
                var info=new CoordinateInfo();
                info.Value=xcoordinate[i][0];
                if (this.IsBeforeData) info.Value+=beforeDataCount;
                if (this.ShowText)
                    info.Message[0]=xcoordinate[i][3];
                this.Frame.VerticalInfo[i]=info;
            }
        }
        else
        {
            for(var i=this.DayData.length-1,j=0;i>=0;--i,++j)
            {
                var info=new CoordinateInfo();
                info.Value=j*minuteCount+minuteMiddleCount;
                info.LineType=-1;    //线段不画
                if (this.ShowText) info.Message[0]=IFrameSplitOperator.FormatDateString(this.DayData[i].Date, this.ShowFormate==0?'YYYY-MM-DD':'MM-DD');
                this.Frame.VerticalInfo.push(info);

                var info=new CoordinateInfo();
                info.Value=(j+1)*minuteCount;
                this.Frame.VerticalInfo.push(info);
            }
        }
    }
}

function FrameSplitXData()
{
    this.newMethod=IFrameSplitOperator;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ShowText=true;                 //是否显示坐标信息

    this.Operator=function()
    {
        if (this.Frame.Data==null || this.Frame.XData==null) return;
        this.Frame.VerticalInfo=[];
        var xOffset=this.Frame.Data.DataOffset;
        var xPointCount=this.Frame.XPointCount;

        for(var i=0, index=xOffset; i<xPointCount && index<this.Frame.Data.Data.length ;++i,++index)
        {
            var info= new CoordinateInfo();
            info.Value=index-xOffset;
            
            if (this.ShowText)
                info.Message[0]=this.Frame.XData[i];

            this.Frame.VerticalInfo.push(info);
            distance=0;
        }
    }
}


///////////////////////////////////////////////////////////////////////////////////////////////////
//十字光标
function ChartCorssCursor()
{
    this.Frame;
    this.Canvas;                            //画布

    this.HPenColor=g_JSChartResource.CorssCursorHPenColor; //水平线颜色
    this.HPenType=0;  //水平线样式 0=虚线 1=实线

    this.VPenColor=g_JSChartResource.CorssCursorVPenColor; //垂直线颜色
    this.VPenType=0;  //垂直线颜色 0=虚线 1=实线 2=K线宽度

    this.Font=g_JSChartResource.CorssCursorTextFont;            //字体
    this.TextColor=g_JSChartResource.CorssCursorTextColor;      //文本颜色
    this.TextBGColor=g_JSChartResource.CorssCursorBGColor;      //文本背景色
    this.TextHeight=20;                                         //文本字体高度
    this.LastPoint;
    this.CursorIndex;
    this.IsOnlyDrawKLine=false;                                 //是否只能画在K线上 (手机端)

    this.PointX;
    this.PointY;

    this.StringFormatX;
    this.StringFormatY;

    this.ShowTextMode={ Left:1, Right:1, Bottom:1 }; //0=不显示  1=显示在框架外 2=显示在框架内
    this.IsShowCorss=true;  //是否显示十字光标
    this.IsShow=true;
    this.IsShowClose=false;     //Y轴始终显示收盘价

    //内部使用
    this.Close=null;     //收盘价格

    this.GetCloseYPoint=function(index)
    {
        if (!this.StringFormatX.Data) return null;
        var data = this.StringFormatX.Data;
        if (!data.Data || data.Data.length <= 0) return null;
        var dataIndex = data.DataOffset + index;
        if (dataIndex >= data.Data.length) dataIndex = data.Data.length - 1;
        if (dataIndex < 0) return null;

        var klineData = data.Data[dataIndex];
        if (!klineData) return null;
        this.Close=klineData.Close;
        var yPoint = this.Frame.GetYFromData(this.Close);
        return yPoint;
    }

    this.Draw=function()
    {
        if (!this.LastPoint) return;

        this.Close=null;
        var x=this.LastPoint.X;
        var y=this.LastPoint.Y;

        var isInClient=false;
        this.Canvas.beginPath();
        this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight());
        isInClient=this.Canvas.isPointInPath(x,y);

        this.PointY=null;
        this.PointY==null;

        if (!isInClient) return;

        if (this.Frame.IsHScreen===true)
        {
            this.HScreenDraw();
            return;
        }

        var left=this.Frame.ChartBorder.GetLeft();
        var right=this.Frame.ChartBorder.GetRight();
        var top=this.Frame.ChartBorder.GetTopTitle();
        var bottom=this.Frame.ChartBorder.GetBottom();
        var rightWidth=this.Frame.ChartBorder.Right;
        var chartRight=this.Frame.ChartBorder.GetChartWidth();

        if (this.IsOnlyDrawKLine)   //手机端 十字只能画在K线上
        {
            x=this.Frame.GetXFromIndex(this.CursorIndex);
            if (this.IsShowClose)
            {
                var yPoint = this.GetCloseYPoint(this.CursorIndex);
                if (yPoint != null) y=yPoint;
            }
        }

        this.PointY=[[left,y],[right,y]];
        this.PointX=[[x,top],[x,bottom]];

        //十字线
        if (this.IsShowCorss)
        {
            var pixel=GetDevicePixelRatio();
            this.Canvas.save();
            this.Canvas.strokeStyle=this.HPenColor;
            if (this.HPenType==0) this.Canvas.setLineDash([3*pixel,2*pixel]);   //虚线
            //this.Canvas.lineWidth=0.5
            this.Canvas.beginPath();
            this.Canvas.moveTo(left,ToFixedPoint(y));
            this.Canvas.lineTo(right,ToFixedPoint(y));
            this.Canvas.stroke();
            this.Canvas.restore();

            this.Canvas.save();
            this.Canvas.strokeStyle=this.VPenColor;
            if (this.VPenType==0) 
            {
                this.Canvas.setLineDash([3*pixel,2*pixel]);   //虚线
            }
            else if (this.VPenType==2) 
            {
                let barWidth=this.Frame.SubFrame[0].Frame.DataWidth;    //和K线一样宽度
                if (barWidth>2*pixel) this.Canvas.lineWidth=barWidth;
            }

            this.Canvas.beginPath();
            if (this.Frame.SubFrame.length>0)
            {
                for(var i in this.Frame.SubFrame)
                {
                    var frame=this.Frame.SubFrame[i].Frame;
                    top=frame.ChartBorder.GetTopTitle();
                    bottom=frame.ChartBorder.GetBottom();
                    this.Canvas.moveTo(ToFixedPoint(x),top);
                    this.Canvas.lineTo(ToFixedPoint(x),bottom);
                }
            }
            else
            {
                this.Canvas.moveTo(ToFixedPoint(x),top);
                this.Canvas.lineTo(ToFixedPoint(x),bottom);
            }
            this.Canvas.stroke();
            this.Canvas.restore();
            
        }

        var xValue=this.Frame.GetXData(x);
        var yValueExtend={};
        var yValue=this.Frame.GetYData(y,yValueExtend);
        if (this.IsShowClose && this.Close != null) yValue=this.Close;

        this.StringFormatX.Value=xValue;
        this.StringFormatY.Value=yValue;
        this.StringFormatY.RValue=yValueExtend.RightYValue; //右侧子坐标
        this.StringFormatY.FrameID=yValueExtend.FrameID;

        //Y轴
        if ( ((this.ShowTextMode.Left==1 && this.Frame.ChartBorder.Left>=30) || this.ShowTextMode.Left==2 ||
             (this.ShowTextMode.Right==1 && this.Frame.ChartBorder.Right>=30) || this.ShowTextMode.Right==2 ) && this.StringFormatY.Operator() )
        {
            var text=this.StringFormatY.Text;
            this.Canvas.font=this.Font;
            var textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素

            if (this.Frame.ChartBorder.Left>=30 && this.ShowTextMode.Left==1)
            {
                this.Canvas.fillStyle=this.TextBGColor;
                if (left<textWidth ) //左边空白的地方太少了画布下
                {
                    this.Canvas.fillRect(2,y-this.TextHeight/2,textWidth,this.TextHeight);
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,2+2,y,textWidth);
                }
                else
                {
                    this.Canvas.fillRect(left-2,y-this.TextHeight/2,-textWidth,this.TextHeight);
                    this.Canvas.textAlign="right";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,left-4,y,textWidth);
                }
            }
            else if (this.ShowTextMode.Left==2) //在框架内显示
            {
                this.Canvas.fillStyle=this.TextBGColor;
                this.Canvas.fillRect(left,y-this.TextHeight/2,textWidth,this.TextHeight);
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,left+2,y,textWidth);
            }

            if (this.StringFormatY.PercentageText)
            {
                text=this.StringFormatY.PercentageText+'%';
                textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素
            }

            if (this.StringFormatY.RText) 
            {
                text=this.StringFormatY.RText;
                var textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素
            }

            if (this.Frame.ChartBorder.Right>=30 && this.ShowTextMode.Right==1)
            {
                var isOverlayIndex=false;       //是否有叠加子坐标
                var overlayIndexInterval=null;  //子坐标间距
                if (yValueExtend.FrameID>=0)
                {
                    var frame=this.Frame.SubFrame[yValueExtend.FrameID];
                    isOverlayIndex=frame.OverlayIndex.length>0;
                    overlayIndexInterval=frame.Interval;
                }

                this.Canvas.fillStyle=this.TextBGColor;
                if (isOverlayIndex && textWidth>overlayIndexInterval)   //大于子坐标宽度
                {
                    var drawRight=right+overlayIndexInterval;
                    if (drawRight>chartRight) drawRight=chartRight;
                    this.Canvas.fillRect(drawRight-2-textWidth,y-this.TextHeight/2,textWidth,this.TextHeight);
                    this.Canvas.textAlign="right";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,drawRight-4,y,textWidth);
                }
                else if (rightWidth<textWidth)   //右边空白显示不下, 
                {
                    this.Canvas.fillRect(chartRight-2-textWidth,y-this.TextHeight/2,textWidth,this.TextHeight);
                    this.Canvas.textAlign="right";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,chartRight-4,y,textWidth);
                }
                else
                {
                    this.Canvas.fillRect(right+2,y-this.TextHeight/2,textWidth,this.TextHeight);
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,right+4,y,textWidth);
                }
            }
            else if (this.ShowTextMode.Right==2)
            {
                this.Canvas.fillStyle=this.TextBGColor;
                var showLeft=right-textWidth;
                this.Canvas.fillRect(showLeft,y-this.TextHeight/2,textWidth,this.TextHeight);
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,showLeft+2,y,textWidth);
            }
        }

        //X轴
        if (this.ShowTextMode.Bottom==1 && this.StringFormatX.Operator())
        {
            var text=this.StringFormatX.Text;
            this.Canvas.font=this.Font;

            this.Canvas.fillStyle=this.TextBGColor;
            var textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素
            var yCenter=bottom+2+this.TextHeight/2;
            //JSConsole.Chart.Log('[ChartCorssCursor::Draw] ',yCenter);
            if (x-textWidth/2<3)    //左边位置不够了, 顶着左边画
            {
                this.Canvas.fillRect(x-1,bottom+2,textWidth,this.TextHeight);
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,x+1,yCenter,textWidth);
            }
            else if (x+textWidth/2>=right)
            {
                this.Canvas.fillRect(right-textWidth,bottom+2,textWidth,this.TextHeight);
                this.Canvas.textAlign="right";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,right-2,yCenter,textWidth);
            }
            else
            {
                this.Canvas.fillRect(x-textWidth/2,bottom+2,textWidth,this.TextHeight);
                this.Canvas.textAlign="center";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,x,yCenter,textWidth);
            }
        }

        //子坐标Y轴
        if (yValueExtend.FrameID>=0)
        {
            var frame=this.Frame.SubFrame[yValueExtend.FrameID];
            var overlayLeft=right;
            this.Canvas.font=this.Font;
            for(var i in frame.OverlayIndex)
            {
                var item=frame.OverlayIndex[i];
                if (item.Frame.IsShow===false) continue;

                overlayLeft+=frame.Interval;
                if (overlayLeft+30>chartRight) break;
                var yValue=item.Frame.GetYData(y);

                for(var i=2;i>=0;--i)
                {
                    var text=IFrameSplitOperator.FormatValueString(yValue,i);
                    var textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素
                    if (textWidth<frame.Interval) break;
                }

                this.Canvas.fillStyle=this.TextBGColor;
                this.Canvas.fillRect(overlayLeft+2,y-this.TextHeight/2,textWidth,this.TextHeight);
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,overlayLeft+4,y,textWidth);
            }
        }
    }

    this.HScreenDraw=function()
    {
        var x=this.LastPoint.X;
        var y=this.LastPoint.Y;

        if (this.IsOnlyDrawKLine)   //手机端 十字只能画在K线上
            y=this.Frame.GetXFromIndex(this.CursorIndex);

        var left=this.Frame.ChartBorder.GetLeft();
        var right=this.Frame.ChartBorder.GetRightEx();
        var top=this.Frame.ChartBorder.GetTop();
        var bottom=this.Frame.ChartBorder.GetBottom();
        var bottomWidth=this.Frame.ChartBorder.Bottom;

        this.PointY=[[left,y],[right,y]];
        this.PointX=[[x,top],[x,bottom]];

        //十字线
        if (this.IsShowCorss)
        {
            var pixel=GetDevicePixelRatio();
            this.Canvas.save();
            this.Canvas.strokeStyle=this.HPenColor;
            if (this.HPenType==0) this.Canvas.setLineDash([3*pixel,2*pixel]);   //虚线

            //画竖线
            this.Canvas.beginPath();
            this.Canvas.moveTo(ToFixedPoint(x),top);
            this.Canvas.lineTo(ToFixedPoint(x),bottom);
            this.Canvas.stroke();
            this.Canvas.restore();

            //画横线
            this.Canvas.save();
            this.Canvas.strokeStyle=this.VPenColor;
            if (this.VPenType==0) 
            {
                this.Canvas.setLineDash([3*pixel,2*pixel]);   //虚线
            }
            else if (this.VPenType==2) 
            {
                let barWidth=this.Frame.SubFrame[0].Frame.DataWidth;    //和K线一样宽度
                if (barWidth>2*pixel) this.Canvas.lineWidth=barWidth;
            }

            this.Canvas.beginPath();
            if (this.Frame.SubFrame.length>0)
            {
                for(var i in this.Frame.SubFrame)
                {
                    var frame=this.Frame.SubFrame[i].Frame;
                    this.Canvas.moveTo(frame.ChartBorder.GetLeft(),ToFixedPoint(y));
                    this.Canvas.lineTo(frame.ChartBorder.GetRightTitle(),ToFixedPoint(y));
                }
            }
            else
            {
                this.Canvas.moveTo(left,ToFixedPoint(y));
                this.Canvas.lineTo(right,ToFixedPoint(y));
            }

            this.Canvas.stroke();
            this.Canvas.restore();
        }

        var xValue=this.Frame.GetXData(y);
        var yValueExtend={};
        var yValue=this.Frame.GetYData(x,yValueExtend);

        this.StringFormatX.Value=xValue;
        this.StringFormatY.Value=yValue;
        this.StringFormatY.FrameID=yValueExtend.FrameID;

        if ( ((this.ShowTextMode.Left==1 && this.Frame.ChartBorder.Top>=30) || this.ShowTextMode.Left==2 ||
            (this.ShowTextMode.Right==1 && this.Frame.ChartBorder.Bottom>=30) || this.ShowTextMode.Right==2) && this.StringFormatY.Operator() )
        {
            var text=this.StringFormatY.Text;
            this.Canvas.font=this.Font;
            var textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素

            if (this.Frame.ChartBorder.Top>=30 && this.ShowTextMode.Left==1)
            {
                var xText=x;
                var yText=top;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度
                
                this.Canvas.fillStyle=this.TextBGColor;
                if (top>=textWidth)
                {
                    this.Canvas.fillRect(0,-(this.TextHeight/2),-textWidth,this.TextHeight);
                    this.Canvas.textAlign="right";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,-2,0,textWidth);
                }
                else
                {
                    this.Canvas.fillRect((textWidth-top),-(this.TextHeight/2),-textWidth,this.TextHeight);
                    this.Canvas.textAlign="right";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,(textWidth-top)-2,0,textWidth);
                }

                this.Canvas.restore();
            }
            else if (this.ShowTextMode.Left==2)
            {
                var xText=x;
                var yText=top;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度

                this.Canvas.fillStyle=this.TextBGColor;
                this.Canvas.fillRect(0,-(this.TextHeight/2),textWidth,this.TextHeight);
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,2,0,textWidth);

                this.Canvas.restore();
            }

            if (this.Frame.ChartBorder.Bottom>=30 && this.ShowTextMode.Right==1)
            {
                var xText=x;
                var yText=bottom;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度
                
                this.Canvas.fillStyle=this.TextBGColor;
                if (bottomWidth>textWidth)
                {
                    this.Canvas.fillRect(0,-(this.TextHeight/2),textWidth,this.TextHeight);
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,2,0,textWidth);
                }
                else
                {
                    this.Canvas.fillRect((bottomWidth-textWidth),-(this.TextHeight/2),textWidth,this.TextHeight);
                    this.Canvas.textAlign="left";
                    this.Canvas.textBaseline="middle";
                    this.Canvas.fillStyle=this.TextColor;
                    this.Canvas.fillText(text,(bottomWidth-textWidth)+2,0,textWidth);
                }

                this.Canvas.restore();
            }
            else if (this.ShowTextMode.Right==2)
            {
                var xText=x;
                var yText=bottom;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度

                this.Canvas.fillStyle=this.TextBGColor;
                this.Canvas.fillRect(0,-(this.TextHeight/2),-textWidth,this.TextHeight);
                this.Canvas.textAlign="right";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,-2,0,textWidth);

                this.Canvas.restore();
            }
        }

        if (this.ShowTextMode.Bottom===1 && this.StringFormatX.Operator())
        {
            var text=this.StringFormatX.Text;
            this.Canvas.font=this.Font;

            this.Canvas.fillStyle=this.TextBGColor;
            var textWidth=this.Canvas.measureText(text).width+4;    //前后各空2个像素
            if (y-textWidth/2<3)    //左边位置不够了, 顶着左边画
            {
                var xText=left;
                var yText=y;
                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度

                this.Canvas.fillRect(0,0,textWidth,this.TextHeight);
                this.Canvas.textAlign="left";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,0,this.TextHeight/2,textWidth);

                this.Canvas.restore();
            }
            else
            {
                var xText=left;
                var yText=y;

                this.Canvas.save();
                this.Canvas.translate(xText, yText);
                this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度

                this.Canvas.fillRect(-textWidth/2,0,textWidth,this.TextHeight);
                this.Canvas.textAlign="center";
                this.Canvas.textBaseline="middle";
                this.Canvas.fillStyle=this.TextColor;
                this.Canvas.fillText(text,0,this.TextHeight/2,textWidth);

                this.Canvas.restore();
            }
        }
    }
}
////////////////////////////////////////////////////////////////////////////////
// 等待提示
function ChartSplashPaint()
{
    this.Frame;
    this.Canvas;                            //画布
    this.Font=g_JSChartResource.DefaultTextFont;            //字体
    this.TextColor=g_JSChartResource.DefaultTextColor;      //文本颜色
    this.IsEnableSplash=false;
    this.SplashTitle='数据加载中';

    this.Draw=function()
    {
        if (!this.IsEnableSplash) return;

        if (this.Frame.IsHScreen===true)
        {
            this.HScreenDraw();
            return;
        }

        var xCenter = (this.Frame.ChartBorder.GetLeft() + this.Frame.ChartBorder.GetRight()) / 2;
        var yCenter = (this.Frame.ChartBorder.GetTop() + this.Frame.ChartBorder.GetBottom()) / 2;
        this.Canvas.textAlign='center';
        this.Canvas.textBaseline='middle';
        this.Canvas.fillStyle=this.TextColor;
        this.Canvas.font=this.Font;
        this.Canvas.fillText(this.SplashTitle,xCenter,yCenter);
    }

    this.HScreenDraw=function() //横屏
    {
        var xCenter = (this.Frame.ChartBorder.GetLeft() + this.Frame.ChartBorder.GetRight()) / 2;
        var yCenter = (this.Frame.ChartBorder.GetTop() + this.Frame.ChartBorder.GetBottom()) / 2;

        this.Canvas.save();
        this.Canvas.translate(xCenter, yCenter);
        this.Canvas.rotate(90 * Math.PI / 180); //数据和框子旋转180度

        this.Canvas.textAlign='center';
        this.Canvas.textBaseline='middle';
        this.Canvas.fillStyle=this.TextColor;
        this.Canvas.font=this.Font;
        this.Canvas.fillText(this.SplashTitle,0,0);

        this.Canvas.restore();
    }
}

/////////////////////////////////////////////////////////////////////////////////
//
function IChangeStringFormat()
{
    this.Data;
    this.Value;     //数据
    this.Text;      //输出字符串

    this.Operator=function()
    {
        return false;
    }
}


function HQPriceStringFormat()
{
    this.newMethod=IChangeStringFormat;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Symbol;
    this.FrameID;
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
    this.YClose;            //收盘价
    this.PercentageText;    //百分比
    this.RValue;            //右边值
    this.RText;

    this.Operator=function()
    {
        this.RText=null;
        if (IFrameSplitOperator.IsString(this.RValue)) this.RText=this.RValue;
        if (!this.Value) return false;

        this.PercentageText=null;
        var defaultfloatPrecision=2;     //价格小数位数 
        if (this.FrameID==0)    //第1个窗口显示原始价格
        {
            var defaultfloatPrecision=GetfloatPrecision(this.Symbol);
            this.Text=this.Value.toFixed(defaultfloatPrecision);
            if (this.YClose>0) this.PercentageText=((this.Value-this.YClose)*100/this.YClose).toFixed(2);   //走势图右边坐标显示百分比
        }
        else if (this.FrameID==1)
        {
            
            this.Text=IFrameSplitOperator.FormatValueString(this.Value,defaultfloatPrecision,this.LanguageID);
            if (IFrameSplitOperator.IsNumber(this.RValue)) this.RText=IFrameSplitOperator.FormatValueString(this.RValue,defaultfloatPrecision,this.LanguageID);
        }
        else
        {
            this.Text=IFrameSplitOperator.FormatValueString(this.Value,defaultfloatPrecision,this.LanguageID);
        }

        return true;
    }
}

function HQDateStringFormat()
{
    this.newMethod=IChangeStringFormat;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Operator=function()
    {
        if (!IFrameSplitOperator.IsNumber(this.Value)) return false;
        if (!this.Data) return false;

        var index=this.Value;
        index=parseInt(index.toFixed(0));
        if (this.Data.DataOffset+index>=this.Data.Data.length) return false;
        var currentData = this.Data.Data[this.Data.DataOffset+index];
        this.Text=IFrameSplitOperator.FormatDateString(currentData.Date);
        if (ChartData.IsMinutePeriod(this.Data.Period,true) ) // 分钟周期
        {
            var time = IFrameSplitOperator.FormatTimeString(currentData.Time);
            this.Text = this.Text + "  " + time;
        }
        else if (ChartData.IsSecondPeriod(this.Data.Period))
        {
            var time = IFrameSplitOperator.FormatTimeString(currentData.Time,'HH:MM:SS');
            this.Text = this.Text + "  " + time;
        }
        else if (ChartData.IsTickPeriod(this.Data.Period))  //分笔
        {
            var time = IFrameSplitOperator.FormatTimeString(currentData.Time);
            this.Text = this.Text + "  " + time;
        }

        return true;
    }
}

function HQMinuteTimeStringFormat()
{
    this.newMethod=IChangeStringFormat;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Frame;
    this.Symbol;
    this.IsBeforeData=false;    //是否显示盘前数据

    this.Operator=function()
    {
        if (this.Value==null || isNaN(this.Value)) return false;
        
        var index=Math.abs(this.Value);
        index=parseInt(index.toFixed(0));
        var showIndex=index;
        if (this.Frame && this.Frame.MinuteCount) showIndex=index%this.Frame.MinuteCount;

        var timeStringData=g_MinuteTimeStringData;
        var timeData=timeStringData.GetTimeData(this.Symbol);
        if (!timeData) return false;

        if (this.IsBeforeData)  //增加盘前数据时间刻度
        {
            var allTimeData=[915,916,917,918,919,920,921,922,923,924,925,926,927,928,929]
            for(var i in timeData)
            {
                if (timeData[i]==925) continue;
                allTimeData.push(timeData[i])
            }
            timeData=allTimeData;
        }

        if (showIndex<0) showIndex=0;
        else if (showIndex>timeData.length) showIndex=timeData.length-1;
        if (this.Frame && index>=this.Frame.XPointCount) 
            showIndex=timeData.length-1;

        var time=timeData[showIndex];
        this.Text=IFrameSplitOperator.FormatTimeString(time);
        return true;
    }
}


//行情tooltip提示信息格式
var WEEK_NAME=["日","一","二","三","四","五","六"];
function HistoryDataStringFormat()
{
    this.newMethod=IChangeStringFormat;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Symbol;
    this.UpColor=g_JSChartResource.UpTextColor;
    this.DownColor=g_JSChartResource.DownTextColor;
    this.UnchagneColor=g_JSChartResource.UnchagneTextColor;

    this.VolColor=g_JSChartResource.DefaultTextColor;
    this.AmountColor=g_JSChartResource.DefaultTextColor;
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
    this.LineCount=0;    //一共几行

    this.Operator=function()
    {
        var data=this.Value.Data;
        if (!data) return false;

        var date=new Date(parseInt(data.Date/10000),(data.Date/100%100-1),data.Date%100);
        var strDate=IFrameSplitOperator.FormatDateString(data.Date);
        var title2=g_JSChartLocalization.GetText(WEEK_NAME[date.getDay()],this.LanguageID);
        if (ChartData.IsMinutePeriod(this.Value.ChartPaint.Data.Period,true)) // 分钟周期
        {
            title2=IFrameSplitOperator.FormatTimeString(data.Time);
        }
        else if (ChartData.IsSecondPeriod(this.Value.ChartPaint.Data.Period))
        {
            title2=IFrameSplitOperator.FormatTimeString(data.Time,'HH:MM:SS');
        }
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数
        var increase=null;
        if (data.YClose>0) increase=(data.Close-data.YClose)/data.YClose*100;
        var strText=
            "<span class='tooltip-title'>"+strDate+"&nbsp&nbsp"+title2+"</span>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Open',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.GetColor(data.Open,data.YClose)+";'>"+data.Open.toFixed(defaultfloatPrecision)+"</span><br/>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-High',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.GetColor(data.High,data.YClose)+";'>"+data.High.toFixed(defaultfloatPrecision)+"</span><br/>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Low',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.GetColor(data.Low,data.YClose)+";'>"+data.Low.toFixed(defaultfloatPrecision)+"</span><br/>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Close',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.GetColor(data.Close,data.YClose)+";'>"+data.Close.toFixed(defaultfloatPrecision)+"</span><br/>"+
            //"<span style='color:"+this.YClose+";font:微软雅黑;font-size:12px'>&nbsp;前收: "+IFrameSplitOperator.FormatValueString(data.YClose,2)+"</span><br/>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Vol',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.VolColor+";'>"+IFrameSplitOperator.FormatValueString(data.Vol,2,this.LanguageID)+"</span><br/>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Amount',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.AmountColor+";'>"+IFrameSplitOperator.FormatValueString(data.Amount,2,this.LanguageID)+"</span><br/>"+
            "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Increase',this.LanguageID)+"</span>"+
            (increase==null? "<span class='tooltip-num' style='color:"+this.GetColor(0,0)+";'>"+'--'+"</span><br/>" :
            "<span class='tooltip-num' style='color:"+this.GetColor(increase,0)+";'>"+increase.toFixed(2)+'%'+"</span><br/>");

        this.LineCount=8;
        if(MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol) && data.FlowCapital>0)  //换手率
        {
            var value=data.Vol/data.FlowCapital*100;
            strText+= "<span class='tooltip-con'>"+g_JSChartLocalization.GetText('DivTooltip-Exchange',this.LanguageID)+"</span>"+
            "<span class='tooltip-num' style='color:"+this.AmountColor+";'>"+value.toFixed(2)+'%'+"</span><br/>";
            ++this.LineCount;
        }

        //叠加股票
        if (this.Value.ChartPaint.Name=="Overlay-KLine")
        {
            var title="<span style='color:rgb(0,0,0);font:微软雅黑;font-size:12px;text-align:center;display: block;'>"+this.Value.ChartPaint.Title+"</span>";
            strText=title+strText;
            ++this.LineCount;
        }

        this.Text=strText;
        return true;
    }

    this.GetColor=function(price,yclse)
    {
        if(price>yclse) return this.UpColor;
        else if (price<yclse) return this.DownColor;
        else return this.UnchagneColor;
    }
}

//K线信息地雷提示信息格式
function KLineInfoDataStringFormat()
{
    this.newMethod=IChangeStringFormat;   //派生
    this.newMethod();
    delete this.newMethod;

    this.UpColor=g_JSChartResource.UpTextColor;
    this.DownColor=g_JSChartResource.DownTextColor;
    this.UnchagneColor=g_JSChartResource.UnchagneTextColor;

    this.Operator=function()
    {
        if (!this.Value) return false;

        var infoList=this.Value.Data.Data;  //数据
        var infoType=this.Value.Data.Type;  //类型
        var strText='';

        for(var i in infoList)
        {
            var item=infoList[i];
            var tempText='';
            switch(infoType)
            {
                case KLINE_INFO_TYPE.BLOCKTRADING:
                    tempText=this.BlockTradingFormat(item);
                    break;
                case KLINE_INFO_TYPE.TRADEDETAIL:
                    tempText=this.TradeDetailFormat(item);
                    break;
                case KLINE_INFO_TYPE.RESEARCH:
                    tempText=this.ResearchFormat(item);
                    break;
                case KLINE_INFO_TYPE.PFORECAST:
                    tempText=this.PerformanceForecastFormat(item);
                    break;
                default:
                    tempText=this.DefaultFormat(item);
                    break;
            }

            strText+=tempText;
        }

        var html="<div class='title-length'>"+strText+"</div>";

        if(infoList.length > 8)
        {
            var strBox="<div class='total-list'>共"+infoList.length+"条</div>";
            html+=strBox;
        }

        this.Text=html;
        return true;
    }

    this.DefaultFormat=function(item)
    {
        var strDate=IFrameSplitOperator.FormatDateString(item.Date);
        var strText="<span>"+strDate+"&nbsp;&nbsp;&nbsp;"+item.Title+"</span>";
        return strText;
    }

    //大宗交易
    this.BlockTradingFormat=function(item)
    {
        var showPriceInfo = item.ExtendData;
        var strDate=IFrameSplitOperator.FormatDateString(item.Date);
        var strText="<span><i class='date-tipbox'>"+strDate+"</i>&nbsp;&nbsp;<i class='tipBoxTitle'>成交价:&nbsp;"+showPriceInfo.Price.toFixed(2)+"</i><i class='tipBoxTitle'>收盘价:&nbsp;"+showPriceInfo.ClosePrice.toFixed(2)+
            "</i><br/><i class='rate-discount tipBoxTitle'>溢折价率:&nbsp;<strong style='color:"+ this.GetColor(showPriceInfo.Premium.toFixed(2))+"'>"+
            showPriceInfo.Premium.toFixed(2)+"%</strong></i><i class='tipBoxTitle'>成交量(万股):&nbsp;"+showPriceInfo.Vol.toFixed(2)+"</i></span>";

        return strText;
    }

    //龙虎榜
    this.TradeDetailFormat=function(item)
    {
        /*var detail=
            [
                "日价格涨幅偏离值达到9.89%",
                "日价格涨幅偏离值达格涨幅偏离值达格涨幅偏离值达到9.89%"
            ]
        */

        var detail=item.ExtendData.Detail;
        //格式:日期 上榜原因:  detail[0].TypeExplain
        //                    detail[1].TypeExplain
        //      一周后涨幅: xx 四周后涨幅: xx
        var strDate=IFrameSplitOperator.FormatDateString(item.Date);
        var reasons = [];
        for(var i in detail)
        {
            reasons += "<i>"+detail[i].TypeExplain+"</i><br/>"
            // reasons += detail[i] + "<br/>"
        }

        var strText= "<span><i class='trade-time'>"+strDate+"&nbsp;&nbsp;&nbsp;上榜原因:&nbsp;&nbsp;</i><i class='reason-list'>"+reasons+"</i><br/><i class='trade-detall'>一周后涨幅:&nbsp;<strong style='color:"+
            this.GetColor(item.ExtendData.FWeek.Week1.toFixed(2))+"'>"+ item.ExtendData.FWeek.Week1.toFixed(2)+
            "%</strong>&nbsp;&nbsp;&nbsp;四周后涨幅:&nbsp;<strong style='color:"+this.GetColor(item.ExtendData.FWeek.Week4.toFixed(2))+";'>"+
            item.ExtendData.FWeek.Week4.toFixed(2)+"%</strong></i></span>";

        return strText;
    }

    //调研
    this.ResearchFormat=function(item)
    {
        var levels=item.ExtendData.Level;
        var recPerson='';
        if(levels.length==0)
        {
            recPerson = "<i>一般调研</i>"
        }
        else
        {
            for(var j in levels)
            {
                if(levels[j]==0) recPerson+="<i style='color:#00a0e9'>证券代表&nbsp;&nbsp;&nbsp;</i>";
                else if(levels[j]==1) recPerson+="<i>董秘&nbsp;&nbsp;&nbsp;</i>";
                else if(levels[j]==2) recPerson+="<i style='color:#00a0e9'>总经理&nbsp;&nbsp;&nbsp;</i>";
                else if(levels[j]==3) recPerson+="<i style='color:#00a0e9'>董事长&nbsp;&nbsp;&nbsp;</i>";
            }

            recPerson='接待:&nbsp;&nbsp;&nbsp;'+recPerson;
        }

        var researchType='';
        if (item.ExtendData.Type && item.ExtendData.Type!='其他')
        {
            researchType='&nbsp;&nbsp;&nbsp;'+'<i>'+item.ExtendData.Type+'</i>';
            if (levels.length==0) recPerson='';
        }

        var strDate=IFrameSplitOperator.FormatDateString(item.Date);
        var strText="<span>"+strDate+"&nbsp;&nbsp;&nbsp;"+researchType+recPerson+"</span>";
        return strText;
    }

    //业绩预测
    this.PerformanceForecastFormat=function(item)
    {
        var reportDate=item.ExtendData.ReportDate;
        var year=parseInt(reportDate/10000);  //年份
        var day=reportDate%10000;   //比较 这个去掉年份的日期
        var reportType;
        if(day == 1231){
            reportType = "年报"
        }else if(day == 331){
            reportType = "一季度报"
        }else if(day == 630){
            reportType = "半年度报"
        }else if(day == 930){
            reportType = "三季度报"
        }

        var weekData="";
        if (item.ExtendData.FWeek)
        {
            if (item.ExtendData.FWeek.Week1!=null) weekData+="一周后涨幅:<i class='increase' style='color:"+this.GetColor(item.ExtendData.FWeek.Week1.toFixed(2))+"'>"+ item.ExtendData.FWeek.Week1.toFixed(2)+"%</i>";
            if (item.ExtendData.FWeek.Week4!=null) weekData+="&nbsp;四周后涨幅:<i class='increase' style='color:"+this.GetColor(item.ExtendData.FWeek.Week4.toFixed(2))+"'>"+ item.ExtendData.FWeek.Week4.toFixed(2)+"%</i>";
            if (weekData.length>0) weekData="<br/>&nbsp;&nbsp;<i class='prorecast-week'>"+weekData+"</i>";
        }
        var strDate=IFrameSplitOperator.FormatDateString(item.Date);
        var strText="<span>"+strDate+"&nbsp;&nbsp;"+year+reportType+item.Title+"&nbsp;"+weekData+"</span>";
        return strText;
    }

    this.GetColor=function(price)
    {
        if(price>0) return this.UpColor;
        else if (price<0) return this.DownColor;
        else return this.UnchagneColor;
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                      标题
//
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function IChartTitlePainting()
{
    this.Frame;
    this.Data=new Array();
    this.Canvas;                        //画布
    this.IsDynamic=false;               //是否是动态标题
    this.Position=0;                    //标题显示位置 0 框架里的标题  1 框架上面
    this.CursorIndex;                   //数据索引
    this.Font=g_JSChartResource.TitleFont;
    this.Title;                         //固定标题(可以为空)
    this.TitleColor=g_JSChartResource.DefaultTextColor;
    this.ClassName='IChartTitlePainting';
}

//var PERIOD_NAME=["日线","周线","月线","年线","1分","5分","15分","30分","60分","季线","分笔", "2小时","4小时","双周","",""];
var RIGHT_NAME=['不复权','前复权','后复权'];

function DynamicKLineTitlePainting()
{
    this.newMethod=IChartTitlePainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='DynamicKLineTitlePainting';
    this.IsDynamic=true;
    this.IsShow=true;       //是否显示
    this.Period;            //周期

    this.UpColor=g_JSChartResource.UpTextColor;
    this.DownColor=g_JSChartResource.DownTextColor;
    this.UnchagneColor=g_JSChartResource.UnchagneTextColor;

    this.VolColor=g_JSChartResource.DefaultTextColor;
    this.AmountColor=g_JSChartResource.DefaultTextColor;
    this.DateTimeColor=g_JSChartResource.DefaultTextColor;
    this.NameColor = g_JSChartResource.DefaultTextColor;

    this.Symbol;
    this.Name;

    this.SpaceWidth=2*GetDevicePixelRatio(); //获取设备的分辨率;
    this.OverlayChartPaint;         //叠加画法

    this.IsShowName=true;           //是否显示股票名称
    this.IsShowSettingInfo=true;    //是否显示设置信息(周期 复权)
    this.IsShowDateTime=true;       //是否显示日期
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
    this.OnDrawEvent;

    this.GetCurrentKLineData=function() //获取当天鼠标位置所在的K线数据
    {
        if (this.CursorIndex==null || !this.Data) return null;
        if (this.Data.length<=0) return null;

        var index=this.CursorIndex;
        index=parseInt(index.toFixed(0));
        var dataIndex=this.Data.DataOffset+index;
        if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1;
        if (dataIndex<0) return null;

        var item=this.Data.Data[dataIndex];
        return item;
    }

    this.DrawItem=function(item)
    {
        var isHScreen=this.Frame.IsHScreen===true;
        var left=this.Frame.ChartBorder.GetLeft();
        var bottom=this.Frame.ChartBorder.GetTop()-this.Frame.ChartBorder.Top/2;
        var right=this.Frame.ChartBorder.GetRight();
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数

        if (isHScreen)
        {
            if (this.Frame.ChartBorder.Right<5) return;
            var left=2;
            var bottom=this.Frame.ChartBorder.Right/2;    //上下居中显示
            var right=this.Frame.ChartBorder.GetHeight();
            var xText=this.Frame.ChartBorder.GetChartWidth();
            var yText=this.Frame.ChartBorder.GetTop();
            this.Canvas.translate(xText, yText);
            this.Canvas.rotate(90 * Math.PI / 180);
        }
        else
        {
            if (this.Frame.ChartBorder.Top<5*GetDevicePixelRatio()) return;
        }

        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="middle";
        this.Canvas.font=this.Font;
        var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen };

        if (this.IsShowName)
        {
            if (!this.DrawText(this.Name,this.UnchagneColor,position)) return;
        }

        if (this.IsShowSettingInfo)
        {
            this.Canvas.fillStyle=this.NameColor;
            var periodName='';
            if (this.Data.Period>CUSTOM_MINUTE_PERIOD_START && this.Data.Period<=CUSTOM_MINUTE_PERIOD_END) 
                periodName=(this.Data.Period-CUSTOM_MINUTE_PERIOD_START)+g_JSChartLocalization.GetText('自定义分钟',this.LanguageID);
            else if (this.Data.Period>CUSTOM_DAY_PERIOD_START && this.Data.Period<=CUSTOM_DAY_PERIOD_END)
                periodName=(this.Data.Period-CUSTOM_DAY_PERIOD_START)+g_JSChartLocalization.GetText('自定义日线',this.LanguageID);
            else if (this.Data.Period>CUSTOM_SECOND_PERIOD_START && this.Data.Period<=CUSTOM_SECOND_PERIOD_END)
                periodName=(this.Data.Period-CUSTOM_SECOND_PERIOD_START)+g_JSChartLocalization.GetText('自定义秒',this.LanguageID);
            else 
                periodName=g_JSChartLocalization.GetText(ChartData.GetPeriodName(this.Data.Period),this.LanguageID);
            var rightName=g_JSChartLocalization.GetText(RIGHT_NAME[this.Data.Right],this.LanguageID);
            var text="("+periodName+" "+rightName+")";
            var isStock=MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol); //是否是指数
            if(item.Time!=null || !isStock)  text="("+periodName+")";           //分钟K线 指数 没有复权
            if (!this.DrawText(text,this.NameColor,position)) return;
        }

        if (this.IsShowDateTime)    //是否显示日期
        {
            this.Canvas.fillStyle=this.DateTimeColor;
            var text=IFrameSplitOperator.FormatDateString(item.Date);
            if (!this.DrawText(text,this.DateTimeColor,position)) return;
        }

        if (ChartData.IsMinutePeriod(this.Period,true) && item.Time)
        {
            var text=IFrameSplitOperator.FormatTimeString(item.Time);
            if (!this.DrawText(text,this.DateTimeColor,position)) return;
        }
        else if (ChartData.IsSecondPeriod(this.Period) && item.Time)
        {
            var text=IFrameSplitOperator.FormatTimeString(item.Time, "HH:MM:SS");
            if (!this.DrawText(text,this.DateTimeColor,position)) return;
        }

        var color=this.GetColor(item.Open,item.YClose);
        var text=g_JSChartLocalization.GetText('KTitle-Open',this.LanguageID)+item.Open.toFixed(defaultfloatPrecision);
        if (!this.DrawText(text,color,position)) return;

        var color=this.GetColor(item.High,item.YClose);
        var text=g_JSChartLocalization.GetText('KTitle-High',this.LanguageID)+item.High.toFixed(defaultfloatPrecision);
        if (!this.DrawText(text,color,position)) return;

        var color=this.GetColor(item.Low,item.YClose);
        var text=g_JSChartLocalization.GetText('KTitle-Low',this.LanguageID)+item.Low.toFixed(defaultfloatPrecision);
        if (!this.DrawText(text,color,position)) return;

        var color=this.GetColor(item.Close,item.YClose);
        var text=g_JSChartLocalization.GetText('KTitle-Close',this.LanguageID)+item.Close.toFixed(defaultfloatPrecision);
        if (!this.DrawText(text,color,position)) return;

        if (item.YClose>0)
        {
            var value=(item.Close-item.YClose)/item.YClose*100;
            var color = this.GetColor(value, 0);
            var text = g_JSChartLocalization.GetText('KTitle-Increase',this.LanguageID) + value.toFixed(2)+'%';
            if (!this.DrawText(text,color,position)) return;
        }

        var text=g_JSChartLocalization.GetText('KTitle-Vol',this.LanguageID)+IFrameSplitOperator.FromatIntegerString(item.Vol,2,this.LanguageID);
        if (!this.DrawText(text,this.VolColor,position)) return;

        if (IFrameSplitOperator.IsNumber(item.Amount))
        {
            var text=g_JSChartLocalization.GetText('KTitle-Amount',this.LanguageID)+IFrameSplitOperator.FormatValueString(item.Amount,2,this.LanguageID);
            if (!this.DrawText(text,this.AmountColor,position)) return;
        }

        if (MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol) && item.FlowCapital>0)   //A股有换手率
        {
            var value=item.Vol/item.FlowCapital*100;    //成交量/流通A股*100
            var text=g_JSChartLocalization.GetText('KTitle-Exchange',this.LanguageID)+IFrameSplitOperator.FormatValueString(value,2,this.LanguageID)+'%';
            if (!this.DrawText(text,this.AmountColor,position)) return;
        }

        //叠加股票的名字
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (!item.Symbol || !item.Title) continue;
        
            var name=item.Title;
            var clrText=item.Color;
            var text='['+name+']';
            if (!this.DrawText(text,clrText,position)) return;
        }
    }

    this.Draw=function()
    {
        if (!this.IsShow) return;
        if (this.CursorIndex==null || !this.Data || this.Data.length<=0) 
        {
            this.OnDrawEventCallback(null);
            return;
        }

        this.Canvas.font=this.Font;
        this.SpaceWidth = this.Canvas.measureText('0').width;

        var index=this.CursorIndex;
        index=parseInt(index.toFixed(0));
        var dataIndex=this.Data.DataOffset+index;
        if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1;
        if (dataIndex<0) 
        {
            this.OnDrawEventCallback(null);
            return;
        }

        var item=this.Data.Data[dataIndex];
        this.OnDrawEventCallback(item);
        this.Canvas.save();
        this.DrawItem(item);
        this.Canvas.restore();
    }

    this.OnDrawEventCallback=function(drawData)
    {
        if (!this.OnDrawEvent || !this.OnDrawEvent.Callback) return;
        var data={ Draw: drawData, Name:this.ClassName};
        this.OnDrawEvent.Callback(this.OnDrawEvent,data,this);
    }

    this.GetColor=function(price,yclse)
    {
        if(price>yclse) return this.UpColor;
        else if (price<yclse) return this.DownColor;
        else return this.UnchagneColor;
    }

    this.DrawText=function(title,color,position)
    {
        if (!title) return true;

        var isHScreen=this.Frame.IsHScreen===true;
        var right = this.Frame.ChartBorder.GetRight();
        if (isHScreen) right=this.Frame.ChartBorder.GetHeight();

        this.Canvas.fillStyle = color;
        var textWidth = this.Canvas.measureText(title).width;
        if (position.Left + textWidth > right) return false;
        this.Canvas.fillText(title, position.Left, position.Bottom, textWidth);

        position.Left += textWidth + this.SpaceWidth;
        return true;
    }

}

function DynamicMinuteTitlePainting()
{
    this.newMethod=DynamicKLineTitlePainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='DynamicMinuteTitlePainting';

    this.SpaceWidth=1*GetDevicePixelRatio();
    this.YClose;
    this.IsShowDate=false;  //标题是否显示日期
    this.IsShowName=true;   //标题是否显示股票名字
    this.OverlayChartPaint; //叠加画法
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
    this.LastShowData;  //保存最后显示的数据 给tooltip用
    this.OnDrawEvent;

    this.GetCurrentKLineData=function() //获取当天鼠标位置所在的K线数据
    {
        if (this.LastShowData) return this.LastShowData;

        if (this.CursorIndex==null || !this.Data) return null;
        if (this.Data.length<=0) return null;

        var index=Math.abs(this.CursorIndex-0.5);
        index=parseInt(index.toFixed(0));
        var dataIndex=this.Data.DataOffset+index;
        if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1;
        if (dataIndex<0) return null;

        var item=this.Data.Data[dataIndex];
        return item;
    }

    this.DrawItem=function(item)
    {
        var isHScreen=this.Frame.IsHScreen===true;
        var left=this.Frame.ChartBorder.GetLeft();
        var bottom=this.Frame.ChartBorder.GetTop()-this.Frame.ChartBorder.Top/2;
        var right=this.Frame.ChartBorder.GetRight();
        var defaultfloatPrecision=GetfloatPrecision(this.Symbol);//价格小数位数

        if (isHScreen)
        {
            if (this.Frame.ChartBorder.Right<5) return;
            var left=2;
            var bottom=this.Frame.ChartBorder.Right/2;    //上下居中显示
            var right=this.Frame.ChartBorder.GetHeight();
            var xText=this.Frame.ChartBorder.GetChartWidth();
            var yText=this.Frame.ChartBorder.GetTop();
            this.Canvas.translate(xText, yText);
            this.Canvas.rotate(90 * Math.PI / 180);
        }
        else
        {
            if (this.Frame.ChartBorder.Top<5) return;
        }

        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="middle";
        this.Canvas.font=this.Font;
        var position = { Left: left, Bottom: bottom, IsHScreen: isHScreen };
        if(this.IsShowName)
        {
            if (!this.DrawText(this.Name,this.NameColor,position)) return;
        }

        var text=IFrameSplitOperator.FormatDateTimeString(item.DateTime,this.IsShowDate);
        if (!this.DrawText(text,this.DateTimeColor,position)) return;

        var close=item.Close;
        var increase=item.Increase;
        var vol=item.Vol;
        var amount=item.Amount;
        if (item.Before)    //读取盘前数据
        {
            close=item.Before.Close;
            increase=item.Before.Increase;
            vol=item.Before.Vol;
            amount=item.Before.Amount;
        }

        if (close)
        {
            var color=this.GetColor(close,this.YClose);
            var text=g_JSChartLocalization.GetText('MTitle-Close',this.LanguageID)+close.toFixed(defaultfloatPrecision);
            if (!this.DrawText(text,color,position)) return;
        }

        if (increase!=null)
        {
            var color=this.GetColor(increase,0);
            var text=g_JSChartLocalization.GetText('MTitle-Increase',this.LanguageID)+increase.toFixed(2)+'%';
            if (!this.DrawText(text,color,position)) return;
        }

        var isShowAvPrice=true;
        var upperSymbol=this.Symbol.toUpperCase();
        if (MARKET_SUFFIX_NAME.IsET(upperSymbol) && !MARKET_SUFFIX_NAME.IsETShowAvPrice(upperSymbol)) isShowAvPrice=false;

        if (item.AvPrice && isShowAvPrice)
        {
            var color=this.GetColor(item.AvPrice,this.YClose);
            var text=g_JSChartLocalization.GetText('MTitle-AvPrice',this.LanguageID)+item.AvPrice.toFixed(defaultfloatPrecision);
            if (!this.DrawText(text,color,position)) return;
        }

        var text=g_JSChartLocalization.GetText('MTitle-Vol',this.LanguageID)+IFrameSplitOperator.FromatIntegerString(vol,2);
        if (!this.DrawText(text,this.VolColor,position)) return;

        if (IFrameSplitOperator.IsNumber(amount))
        {
            var text=g_JSChartLocalization.GetText('MTitle-Amount',this.LanguageID)+IFrameSplitOperator.FormatValueString(amount,2);
            if (!this.DrawText(text,this.AmountColor,position)) return;
        }

        //叠加股票的名字
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (!item.Symbol || !item.Title) continue;

            var name=item.Title;
            var clrText=item.Color;
            var text='['+name+']';
            if (!this.DrawText(text,clrText,position)) return;
        }
    }

    this.Draw=function()
    {
        this.LastShowData=null;
        if (!this.IsShow) return;
        if (this.CursorIndex==null || !this.Data || !this.Data.Data) return;
        if (this.Data.Data.length<=0) return;

        this.Canvas.font=this.Font;
        this.SpaceWidth = this.Canvas.measureText('0').width;

        //var index=Math.abs(this.CursorIndex-0.5);
        var index=this.CursorIndex;
        index=parseInt(index.toFixed(0));
        var dataIndex=index+this.Data.DataOffset;
        if (dataIndex>=this.Data.Data.length) dataIndex=this.Data.Data.length-1;

        var item=this.Data.Data[dataIndex];
        this.LastShowData=item;

        this.Canvas.save();
        this.DrawItem(item);
        this.Canvas.restore();
    }
}

//字符串输出格式
var STRING_FORMAT_TYPE =
{
    DEFAULT: 1,     //默认 2位小数 单位自动转化 (万 亿)
    ORIGINAL:2,     //原始数据
    INTEGER:3,      //整形数据输出 如果不是整形使用 DEFAULT
    THOUSANDS:21,   //千分位分割
};

function DynamicTitleData(data,name,color)
{
    this.Data=data;
    this.Name=name;
    this.Color=color;   //字体颜色
    this.DataType;      //数据类型
    this.StringFormat=STRING_FORMAT_TYPE.DEFAULT;   //字符串格式
    this.FloatPrecision=2;                          //小数位数
}

function DynamicChartTitlePainting()
{
    this.newMethod=IChartTitlePainting;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='DynamicChartTitlePainting';

    this.IsDynamic=true;
    this.Data=new Array();
    this.Explain;
    this.ColorIndex;    //五彩K线名字 {Name:'名字'}
    this.TradeIndex;    //专家系统名字{Name:'名字'}
    this.OverlayIndex=new Map();   //叠加指标 key=Identify value={ Data:数据, Title:标题, Identify:标识}
    this.LanguageID=JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
    this.TitleRect;             //指标名字显示区域
    this.IsDrawTitleBG=false;    //是否绘制指标名字背景色
    this.BGColor=g_JSChartResource.IndexTitleBGColor;
    this.OnDrawEvent;

    this.IsClickTitle=function(x,y) //是否点击了指标标题
    {
        if (!this.TitleRect) return false;

        if (x>this.TitleRect.Left && x<this.TitleRect.Left+this.TitleRect.Width && y>this.TitleRect.Top && y<this.TitleRect.Top+this.TitleRect.Height)
        {
            return true;
        }

        return false;
    }

    this.FormatValue=function(value,item)
    {
        if (item.StringFormat==STRING_FORMAT_TYPE.DEFAULT)
            return IFrameSplitOperator.FormatValueString(value,item.FloatPrecision,this.LanguageID);
        else if (item.StringFormat==STRING_FORMAT_TYPE.THOUSANDS)
            return IFrameSplitOperator.FormatValueThousandsString(value,item.FloatPrecision);
        else if (item.StringFormat==STRING_FORMAT_TYPE.ORIGINAL) 
            return value.toFixed(item.FloatPrecision).toString();
        else if (item.StringFormat==STRING_FORMAT_TYPE.INTEGER)
            return IFrameSplitOperator.FromatIntegerString(value,item.FloatPrecision,this.LanguageID);
    }

    this.FormatMultiReport=function(data,format)
    {
        var text="";
        for(var i in data)
        {
            var item = data[i];
            let quarter=item.Quarter;
            let year=item.Year;
            let value=item.Value;

            if (text.length>0) text+=',';

            text+=year.toString();
            switch(quarter)
            {
                case 1:
                    text+='一季报 ';
                    break;
                case 2:
                    text+='半年报 ';
                    break;
                case 3:
                    text+='三季报 ';
                    break;
                case 4:
                    text+='年报 ';
                    break;
            }

            text+=this.FormatValue(value,format);
        }

        return text;
    }

    this.Draw=function()
    {
        this.IsDrawTitleBG=this.Frame.IsDrawTitleBG;
        this.TitleRect=null;
        if (this.CursorIndex==null ) return;
        if (!this.Data) return;
        if (this.Frame.ChartBorder.TitleHeight<5) return;

        if (this.Frame.IsHScreen===true)
        {
            this.Canvas.save();
            this.HScreenDraw();
            this.Canvas.restore();
            return;
        }

        var left=this.Frame.ChartBorder.GetLeft()+1;
        var bottom=this.Frame.ChartBorder.GetTop()+this.Frame.ChartBorder.TitleHeight/2;    //上下居中显示
        var right=this.Frame.ChartBorder.GetRight();

        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="middle";
        this.Canvas.font=this.Font;

        if (this.Title)
        {
            var textWidth=this.Canvas.measureText(this.Title).width+2;
            if (this.IsDrawTitleBG)
            {
                var spaceSize=GetDevicePixelRatio()*2;
                this.Canvas.fillStyle=this.BGColor;
                this.TitleRect={Left:left, Top:this.Frame.ChartBorder.GetTop()+spaceSize, Width:textWidth, Height:this.Frame.ChartBorder.TitleHeight-(spaceSize*2)};    //保存下标题的坐标
                this.Canvas.fillRect(this.TitleRect.Left,this.TitleRect.Top,this.TitleRect.Width,this.TitleRect.Height);
            }

            this.Canvas.fillStyle=this.TitleColor;
            this.Canvas.fillText(this.Title,left,bottom,textWidth);
            left+=textWidth;
        }

        var lockRect=this.Frame.GetLockRect();
        if (lockRect)   //指标上锁区域不显示动态标题
        {
            var index=Math.abs(this.CursorIndex-0.5);
            var x=this.Frame.GetXFromIndex(index.toFixed(0));
            if (x>=lockRect.Left) return;
        }

        for(var i in this.Data)
        {
            var item=this.Data[i];
            if (!item || !item.Data || !item.Data.Data || !item.Name) continue;

            if (item.Data.Data.length<=0) continue;

            var value=null;
            var valueText=null;
            if (item.DataType=="StraightLine")  //直线只有1个数据
            {
                value=item.Data.Data[0];
                valueText=this.FormatValue(value,item);
            }
            else
            {
                var index=Math.abs(this.CursorIndex-0.5);
                index=parseInt(index.toFixed(0));
                var dataIndex=item.Data.DataOffset+index;
                if (dataIndex>=item.Data.Data.length) dataIndex=item.Data.Data.length-1;
                if (dataIndex<0) continue;
                //if (item.Data.DataOffset+index>=item.Data.Data.length) continue;

                value=item.Data.Data[dataIndex];
                if (value==null) continue;

                if (item.DataType=="HistoryData-Vol")
                {
                    value=value.Vol;
                    valueText=this.FormatValue(value,item);
                }
                else if (item.DataType=="MultiReport")
                {
                    valueText=this.FormatMultiReport(value,item);
                }
                else
                {
                    valueText=this.FormatValue(value,item);
                }
            }

            this.Canvas.fillStyle=item.Color;

            var text=item.Name+":"+valueText;
            var textWidth=this.Canvas.measureText(text).width+2;    //后空2个像素
            this.Canvas.fillText(text,left,bottom,textWidth);
            left+=textWidth;
        }

        if (this.Explain)   //说明信息
        {
            this.Canvas.fillStyle=this.TitleColor;
            var text="说明:"+this.Explain;
            var textWidth=this.Canvas.measureText(text).width+2;
            if (left+textWidth<right)
            {
                this.Canvas.fillText(text,left,bottom,textWidth);
                left+=textWidth;
            }
        }

        if (this.ColorIndex)
        {
            this.Canvas.fillStyle='rgb(105,105,105)'
            var textWidth=this.Canvas.measureText(this.ColorIndex.Name).width+2;    //后空2个像素
            this.Canvas.fillText(this.ColorIndex.Name,left,bottom,textWidth);
            left+=textWidth;
        }

        if (this.TradeIndex)
        {
            this.Canvas.fillStyle='rgb(105,105,105)';
            var textWidth=this.Canvas.measureText(this.TradeIndex.Name).width+2;    //后空2个像素
            this.Canvas.fillText(this.TradeIndex.Name,left,bottom,textWidth);
            left+=textWidth;
        }

        this.DrawOverlayIndex(left+10*GetDevicePixelRatio());   //间距都空点 和主指标区分开
    }

    this.DrawOverlayIndex=function(left)    //叠加指标标题
    {
        var bottom=this.Frame.ChartBorder.GetTop()+this.Frame.ChartBorder.TitleHeight/2;    //上下居中显示
        var right=this.Frame.ChartBorder.GetRight();
        if (this.Frame.IsHScreen===true) 
        {
            bottom=-this.Frame.ChartBorder.TitleHeight/2;
            right=this.Frame.ChartBorder.GetHeight();
        }
        if (left>right) return;

        var spaceWidth=5*GetDevicePixelRatio();
        var drawLeft=left;
        for(item of this.OverlayIndex)
        {
            left+=spaceWidth;
            var overlayItem=item[1];
            if (overlayItem.Title)
            {
                this.Canvas.fillStyle=this.TitleColor;
                var textWidth=this.Canvas.measureText(overlayItem.Title).width+2;
                drawLeft=left;
                left+=textWidth;
                if (left>right) break;
                this.Canvas.fillText(overlayItem.Title,drawLeft,bottom,textWidth);
            }

            for(var i in overlayItem.Data)
            {
                var item=overlayItem.Data[i];
                if (!item || !item.Data || !item.Data.Data || !item.Name) continue;
                if (item.Data.Data.length<=0) continue;

                var value=null;
                var valueText=null;
                if (item.DataType=="StraightLine")  //直线只有1个数据
                {
                    value=item.Data.Data[0];
                    valueText=this.FormatValue(value,item);
                }
                else
                {
                    var index=Math.abs(this.CursorIndex-0.5);
                    index=parseInt(index.toFixed(0));
                    var dataIndex=item.Data.DataOffset+index;
                    if (dataIndex>=item.Data.Data.length) dataIndex=item.Data.Data.length-1;
                    if (dataIndex<0) continue;

                    value=item.Data.Data[dataIndex];
                    if (value==null) continue;

                    if (item.DataType=="HistoryData-Vol")
                    {
                        value=value.Vol;
                        valueText=this.FormatValue(value,item);
                    }
                    else if (item.DataType=="MultiReport")
                    {
                        valueText=this.FormatMultiReport(value,item);
                    }
                    else
                    {
                        valueText=this.FormatValue(value,item);
                    }
                }

                this.Canvas.fillStyle=item.Color;

                var text=item.Name+":"+valueText;
                var textWidth=this.Canvas.measureText(text).width+2;    //后空2个像素
                drawLeft=left;
                left+=textWidth;
                if (left>right) break;
                this.Canvas.fillText(text,drawLeft,bottom,textWidth);
            }

            if (left>right) break;
        }
    }

    this.HScreenDraw=function()
    {
        var xText=this.Frame.ChartBorder.GetRightTitle();
        var yText=this.Frame.ChartBorder.GetTop();
        this.Canvas.translate(xText, yText);
        this.Canvas.rotate(90 * Math.PI / 180);

        var left=1;
        var bottom=-this.Frame.ChartBorder.TitleHeight/2;    //上下居中显示
        var right=this.Frame.ChartBorder.GetHeight();

        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="middle";
        this.Canvas.font=this.Font;

        if (this.Title)
        {
            var textWidth=this.Canvas.measureText(this.Title).width+2;

            if (this.IsDrawTitleBG)
            {
                var spaceSize=GetDevicePixelRatio()*2;
                this.Canvas.fillStyle=this.BGColor;
                this.TitleRect= {Left:this.Frame.ChartBorder.GetRightTitle(),Top:this.Frame.ChartBorder.GetTop(),Width:this.Frame.ChartBorder.TitleHeight ,Height:textWidth};   //保存下标题的坐标
                let drawRect={Left:left, Top:-this.Frame.ChartBorder.TitleHeight+spaceSize, Width:textWidth, Height:this.Frame.ChartBorder.TitleHeight-(spaceSize*2)};
                this.Canvas.fillRect(drawRect.Left,drawRect.Top,drawRect.Width,drawRect.Height);
            }

            this.Canvas.fillStyle=this.TitleColor;
            this.Canvas.fillText(this.Title,left,bottom,textWidth);
            left+=textWidth;
        }

        var lockRect=this.Frame.GetLockRect();
        if (lockRect)   //指标上锁区域不显示动态标题
        {
            var index=Math.abs(this.CursorIndex-0.5);
            var x=this.Frame.GetXFromIndex(index.toFixed(0));
            if (x>=lockRect.Top) return;
        }

        for(var i in this.Data)
        {
            var item=this.Data[i];
            if (!item || !item.Data || !item.Data.Data || !item.Name) continue;

            if (item.Data.Data.length<=0) continue;

            var value=null;
            var valueText=null;
            if (item.DataType=="StraightLine")  //直线只有1个数据
            {
                value=item.Data.Data[0];
                valueText=this.FormatValue(value,item);
            }
            else
            {
                var index=Math.abs(this.CursorIndex-0.5);
                index=parseInt(index.toFixed(0));
                var dataIndex=item.Data.DataOffset+index;
                if (dataIndex>=item.Data.Data.length) dataIndex=item.Data.Data.length-1;
                if (dataIndex<0) continue;

                value=item.Data.Data[dataIndex];
                if (value==null) continue;

                if (item.DataType=="HistoryData-Vol")
                {
                    value=value.Vol;
                    valueText=this.FormatValue(value,item);
                }
                else if (item.DataType=="MultiReport")
                {
                    valueText=this.FormatMultiReport(value,item);
                }
                else
                {
                    valueText=this.FormatValue(value,item);
                }
            }

            this.Canvas.fillStyle=item.Color;

            var text=item.Name+":"+valueText;
            var textWidth=this.Canvas.measureText(text).width+2;    //后空2个像素
            this.Canvas.fillText(text,left,bottom,textWidth);
            left+=textWidth;
        }

        if (this.Explain)   //说明信息
        {
            this.Canvas.fillStyle=this.TitleColor;
            var text="说明:"+this.Explain;
            var textWidth=this.Canvas.measureText(text).width+2;
            if (left+textWidth<right)
            {
                this.Canvas.fillText(text,left,bottom,textWidth);
                left+=textWidth;
            }
        }

        this.DrawOverlayIndex(left+5*GetDevicePixelRatio());   //间距都空点 和主指标区分开
    }

    this.OnDrawEventCallback=function(drawData)
    {
        if (!this.OnDrawEvent || !this.OnDrawEvent.Callback) return;
        var data={ Draw: drawData, Name:'DynamicChartTitlePainting'};
        this.OnDrawEvent.Callback(this.OnDrawEvent,data,this);
    }
}

//画图工具
function IChartDrawPicture()
{
    this.Frame;
    this.Canvas;
    this.Point=new Array()                      //画图的点
    this.Value=new Array();                     //XValue,YValue
    this.LinePoint=[];
    this.PointCount=2;                          //画点的个数
    this.Status=0;                              //0=开始画 1=完成第1个点  2=完成第2个点 3=完成第3个点  10=完成 20=移动
    this.PointStatus=0;                         //2=第2个点完成
    this.MovePointIndex=null;                   //移动哪个点 0-10 对应Point索引  100 整体移动
    this.ClassName='IChartDrawPicture';
    this.FinishedCallback;                      //画图完成回调通知
    this.Guid=Guid();                            //ID标识
    this.Symbol;        //对应的股票
    this.Period;        //对应的周期
    this.IsSelected=false;  //是否选中
    this.Option;

    // this.LineColor=g_JSChartResource.DrawPicture.LineColor[0];                            //线段颜色
    this.LineColor="#1e90ff";      //线段颜色,input type="color" 不支持rgb和rgba 的格式
    this.AreaColor='rgba(25,25,25,0.4)';    //面积颜色
    this.PointColor=g_JSChartResource.DrawPicture.PointColor[0];
    

    //接口函数
    this.SetLastPoint=null; //this.SetLastPoint=function(obj)  obj={X:,Y:}
    this.Update=null;       //更新数据回调

    this.Draw=function()
    {

    }

    //Point => Value
    this.PointToValue=function()
    {
        if (!this.Frame) return false;
        var data=this.Frame.Data;
        if (!data) return false;

        var isHScreen=this.Frame.IsHScreen;
        if (isHScreen)
        {
            for(var i in this.Point)
            {
                var item=this.Point[i];
                var xValue=parseInt(this.Frame.GetXData(item.Y))+data.DataOffset;
                var yValue=this.Frame.GetYData(item.X);

                this.Value[i]={};
                this.Value[i].XValue=xValue;
                this.Value[i].YValue=yValue;
            }
        }
        else
        {
            for(var i in this.Point)
            {
                var item=this.Point[i];
                var xValue=parseInt(this.Frame.GetXData(item.X))+data.DataOffset;
                var yValue=this.Frame.GetYData(item.Y);

                this.Value[i]={};
                this.Value[i].XValue=xValue;
                this.Value[i].YValue=yValue;
            }
        }

        return true;
    }

    this.IsPointIn=function(x,y)
    {
        return false;
    }

    //Value => Point
    this.ValueToPoint=function()
    {
        if (!this.Frame) return false;
        var data=this.Frame.Data;
        if (!data) return false;

        var isHScreen=this.Frame.IsHScreen;
        this.Point=[];
        for(var i in this.Value)
        {
            var item=this.Value[i];
            var pt=new Point();
            if (isHScreen)
            {
                pt.Y=this.Frame.GetXFromIndex(item.XValue-data.DataOffset);
                pt.X=this.Frame.GetYFromData(item.YValue);
            }
            else
            {
                pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset);
                pt.Y=this.Frame.GetYFromData(item.YValue);
            }
            this.Point[i]=pt;
        }
    }

    //xStep,yStep 移动的偏移量
    this.Move=function(xStep,yStep)
    {
        if (this.Status!=20) return fasle;
        if (!this.Frame) return false;
        var data=this.Frame.Data;
        if (!data) return false;
        if (this.MovePointIndex==null) return false;

        var index=parseInt(this.MovePointIndex);
        if (index===100)    //整体移动
        {
            for(var i in this.Point)
            {
                this.Point[i].X+=xStep;
                this.Point[i].Y+=yStep;
            }
        }
        else if (index===0 || index===1 || index===2 || index===3 || index===4 || index===5)
        {
            if (index<this.Point.length)
            {
                this.Point[index].X+=xStep;
                this.Point[index].Y+=yStep;
            }
        }
        else
        {
            return false;
        }
    }

    this.ClipFrame=function()
    {
        if (this.Frame.IsHScreen)
        {
            var left=this.Frame.ChartBorder.GetLeftEx();
            var top=this.Frame.ChartBorder.GetTop();
            var width=this.Frame.ChartBorder.GetWidthEx();
            var height=this.Frame.ChartBorder.GetHeight();
        }
        else
        {
            var left=this.Frame.ChartBorder.GetLeft();
            var top=this.Frame.ChartBorder.GetTopEx();
            var width=this.Frame.ChartBorder.GetWidth();
            var height=this.Frame.ChartBorder.GetHeightEx();
        }

        this.Canvas.save();
        this.Canvas.beginPath();
        this.Canvas.rect(left,top,width,height);
        this.Canvas.clip();
    }

    //计算需要画的点的坐标option:{IsCheckX:是否检测X值, IsCheckY:是否检测Y值}
    this.CalculateDrawPoint=function(option)
    {
        if (this.Status<2) return null;
        if(!this.Point.length || !this.Frame) return null;

        var drawPoint=new Array();
        if (this.Status==10)
        {
            var data=this.Frame.Data;
            if (!data) return null;

            var showCount=this.Frame.XPointCount;
            var invaildX=0; //超出范围的x点个数
            var isHScreen=this.Frame.IsHScreen;
            for(var i in this.Value)
            {
                var item=this.Value[i];
                var dataIndex=item.XValue-data.DataOffset;
                if (dataIndex<0 || dataIndex>=showCount) ++invaildX;
            
                var pt=new Point();
                if (isHScreen)  //横屏X,Y对调
                {
                    pt.Y=this.Frame.GetXFromIndex(item.XValue-data.DataOffset,false);
                    pt.X=this.Frame.GetYFromData(item.YValue,false);
                }
                else
                {
                    pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset,false);
                    pt.Y=this.Frame.GetYFromData(item.YValue,false);
                }
                drawPoint.push(pt);
            }

            if (option && option.IsCheckX===true)
            {
                if (invaildX==this.Value.length) return null;
            }
        }
        else
        {
            drawPoint=this.Point;
        }

        return drawPoint;
    }

    this.IsYValueInFrame=function(yValue)
    {
        if (!this.Frame) return false;

        if (yValue>this.Frame.HorizontalMax || yValue<this.Frame.HorizontalMin) return false;

        return true;
    }

    this.DrawPoint=function(aryPoint)
    {
        if (!aryPoint || aryPoint.length<=0) return;

        //画点
        this.ClipFrame();
        var pixel=GetDevicePixelRatio();
        for(var i in aryPoint)
        {
            var item=aryPoint[i];

            this.Canvas.beginPath();
            this.Canvas.arc(item.X,item.Y,5*pixel,0,360,false);
            this.Canvas.fillStyle=this.PointColor;      //填充颜色
            this.Canvas.fill();                         //画实心圆
            this.Canvas.closePath();
        }

        this.Canvas.restore();
    }

    //计算2个点线的,左右的延长线的点
    this.CalculateExtendLinePoint=function(ptStart,ptEnd)
    {
        var result={};

        var left=this.Frame.ChartBorder.GetLeft();
        var right=this.Frame.ChartBorder.GetRight();
        var top=this.Frame.ChartBorder.GetTopEx();
        var bottom=this.Frame.ChartBorder.GetBottom();

        var a=ptEnd.X-ptStart.X;
        var b=ptEnd.Y-ptStart.Y;

        if (a>0)
        {
            var b2=bottom-ptStart.Y;
            var a2=a*b2/b;

            var pt=new Point();
            pt.X=ptStart.X+a2;
            pt.Y=bottom;
            result.End=pt;


            var b2=ptEnd.Y-top;
            var a2=a*b2/b;
            var pt2=new Point();
            pt2.Y=top;
            pt2.X=ptEnd.X-a2;
            result.Start=pt2;
        }
        else
        {
            var b2=bottom-ptStart.Y;
            var a2=Math.abs(a)*b2/b;

            var pt=new Point();
            pt.X=ptStart.X-a2;;
            pt.Y=bottom;
            result.End=pt;

            var b2=ptEnd.Y-top;
            var a2=Math.abs(a)*b2/b;
            var pt2=new Point();
            pt2.Y=top;
            pt2.X=ptEnd.X+a2;
            result.Start=pt2;
        }

        return result;
    }

    //坐标是否在点上 返回在第几个点上
    this.IsPointInXYValue=function(x,y)
    {
        if (!this.Frame) return -1;

        var data=this.Frame.Data;
        if (!data) return -1;
        if (!this.Value) return -1;

        var radius=5;
        if (this.Option) radius+=this.Option.Zoom;
        var isHScreen=this.Frame.IsHScreen;
        radius*=GetDevicePixelRatio();
        for(var i=0;i<this.Value.length; ++i)   //是否在点上
        {
            var item=this.Value[i];
            var pt=new Point();
            if (isHScreen)
            {
                pt.Y=this.Frame.GetXFromIndex(item.XValue-data.DataOffset);
                pt.X=this.Frame.GetYFromData(item.YValue);
            }
            else
            {
                pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset);
                pt.Y=this.Frame.GetYFromData(item.YValue);
            }
            this.Canvas.beginPath();
            this.Canvas.arc(pt.X,pt.Y,radius,0,360);
            if (this.Canvas.isPointInPath(x,y))  return i;
        }

        return -1;
    }

    //坐标是否在线段上 返回在第几个线段上
    this.IsPointInLine=function(x,y)
    {
        if (!this.LinePoint) return -1;

        var lineWidth=5;
        if (this.Option) lineWidth+=this.Option.Zoom;
        var pixel=GetDevicePixelRatio();
        lineWidth*=pixel;
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            var ptStart=item.Start;
            var ptEnd=item.End;
            this.Canvas.beginPath();
            if (ptStart.X==ptEnd.X) //竖线
            {
                this.Canvas.moveTo(ptStart.X-lineWidth,ptStart.Y);
                this.Canvas.lineTo(ptStart.X+lineWidth,ptStart.Y);
                this.Canvas.lineTo(ptEnd.X+lineWidth,ptEnd.Y);
                this.Canvas.lineTo(ptEnd.X-lineWidth,ptEnd.Y);
            }
            else
            {
                this.Canvas.moveTo(ptStart.X,ptStart.Y+lineWidth);
                this.Canvas.lineTo(ptStart.X,ptStart.Y-lineWidth);
                this.Canvas.lineTo(ptEnd.X,ptEnd.Y-lineWidth);
                this.Canvas.lineTo(ptEnd.X,ptEnd.Y+lineWidth);
            }
            this.Canvas.closePath();
            if (this.Canvas.isPointInPath(x,y))
                return i;
        }

        return -1;
    }

    //0-10 鼠标对应的点索引   100=鼠标在正个图形上  -1 鼠标不在图形上
    this.IsPointIn_XYValue_Line=function(x,y)
    {
        if (this.Status!=10) return -1;

        var value=this.IsPointInXYValue(x,y);
        if (value>=0) return value;

        value=this.IsPointInLine(x,y);
        if (value>=0) return 100;

        return -1;
    }

    this.DrawLine=function(ptStart,ptEnd,isDottedline)
    {
        if (isDottedline) this.Canvas.setLineDash([5,10]);

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ptStart.X,ptStart.Y);
        this.Canvas.lineTo(ptEnd.X,ptEnd.Y);
        this.Canvas.stroke();

        if (isDottedline) this.Canvas.setLineDash([]);
    }

    this.CreateLineData=function(ptStart,ptEnd)
    {
        var line={Start:new Point(), End:new Point()};
        line.Start.Y=ptStart.Y;
        line.Start.X=ptStart.X;
        line.End.Y=ptEnd.Y;
        line.End.X=ptEnd.X;

        return line;
    }

    //导出成存储格式
    this.ExportStorageData=function()
    {
        var storageData=
        { 
            ClassName:this.ClassName, 
            Symbol:this.Symbol, Guid:this.Guid, Period:this.Period,Value:[] ,
            FrameID:this.Frame.Identify, LineColor:this.LineColor, AreaColor:this.AreaColor
        };
        for(var i in this.Value)
        {
            var item=this.Value[i];
            storageData.Value.push({XValue:item.XValue, YValue:item.YValue});
        }

        if (this.Text) storageData.Text=this.Text;  //如果有文本, 也导出
        if (this.FontOption) storageData.FontOption=this.FontOption;    //字体也导出

        return storageData;
    }

}

IChartDrawPicture.ColorToRGBA=function(color,opacity)
{
    return "rgba(" + parseInt("0x" + color.slice(1, 3)) + "," + parseInt("0x" + color.slice(3, 5)) + "," + parseInt("0x" + color.slice(5, 7)) + "," + opacity + ")";
}

IChartDrawPicture.CreateChartDrawPrictrue=function(obj)    //创建画图工具
{
    const MAP_CREATE=new Map(
    [
        ['ChartDrawPictureLine', { Create:function() { return new ChartDrawPictureLine(); }}],
        ['ChartDrawPictureHaflLine', { Create:function() { return new ChartDrawPictureHaflLine(); }}],
        ['ChartDrawPictureHorizontalLine', { Create:function() { return new ChartDrawPictureHorizontalLine(); }}],
        ['ChartDrawPictureTrendLine', { Create:function() { return new ChartDrawPictureTrendLine(); }}],
        ['ChartDrawPictureRect', { Create:function() { return new ChartDrawPictureRect(); }}],
        ['ChartDrawPictureArc', { Create:function() { return new ChartDrawPictureArc(); }}],
        ['ChartDrawPictureWaveMW', { Create:function() { return new ChartDrawPictureWaveMW(); }}],
        ['ChartDrawPictureParallelLines', { Create:function() { return new ChartDrawPictureParallelLines(); }}],
        ['ChartDrawPictureParallelChannel', { Create:function() { return new ChartDrawPictureParallelChannel(); }}],
        ['ChartDrawPicturePriceChannel', { Create:function() { return new ChartDrawPicturePriceChannel(); }}],
        ['ChartDrawPictureText', { Create:function() { return new ChartDrawPictureText(); }}],
        ['ChartDrawPictureGannFan', { Create:function() { return new ChartDrawPictureGannFan(); }}],
        ['ChartDrawPictureResistanceLine', { Create:function() { return new ChartDrawPictureResistanceLine(); }}],
        ['ChartDrawPictureGoldenSection', { Create:function() { return new ChartDrawPictureGoldenSection(); }}],
        ['ChartDrawPicturePercentage', { Create:function() { return new ChartDrawPicturePercentage(); }}],
        ['ChartDrawPictureWaveBand', { Create:function() { return new ChartDrawPictureWaveBand(); }}],
        ['ChartDrawPictureTriangle', { Create:function() { return new ChartDrawPictureTriangle(); }}],
        ['ChartDrawPictureSymmetryAngle', { Create:function() { return new ChartDrawPictureSymmetryAngle(); }}],
        ['ChartDrawPictureCircle', { Create:function() { return new ChartDrawPictureCircle(); }}],
        ['ChartDrawPictureQuadrangle', { Create:function() { return new ChartDrawPictureQuadrangle(); }}],
        ['ChartDrawPictureFibonacci', { Create:function() { return new ChartDrawPictureFibonacci(); }}],
        ['ChartDrawPictureIconFont', { Create:function() { return new ChartDrawPictureIconFont(); }}]
    ]);

    if (!MAP_CREATE.has(obj.ClassName)) return null;

    var item=MAP_CREATE.get(obj.ClassName);
    var chartDraw=item.Create();
    
    if (obj.Period>=0) chartDraw.Period=obj.Period;
    if (obj.Guid) chartDraw.Guid=obj.Guid;
    if (obj.Symbol) chartDraw.Symbol=obj.Symbol;
    if (obj.Value) chartDraw.Value=obj.Value;
    if (obj.Text) chartDraw.Text=obj.Text;
    if (obj.LineColor) chartDraw.LineColor=obj.LineColor;
    if (obj.AreaColor) chartDraw.AreaColor=obj.AreaColor;
    if (obj.FontOption) chartDraw.FontOption=obj.FontOption;

    return chartDraw;
}


//画图工具-线段
function ChartDrawPictureLine()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureLine';
    this.IsPointIn=this.IsPointIn_XYValue_Line;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint( {IsCheckX:true, IsCheckY:true} );
        if (!drawPoint) return;
        if (drawPoint.length!=2) return;

        this.ClipFrame();

        var ptStart=drawPoint[0];
        var ptEnd=drawPoint[1];

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ptStart.X,ptStart.Y);
        this.Canvas.lineTo(ptEnd.X,ptEnd.Y);
        this.Canvas.stroke();

        /*
        if (this.IsSelected)
        {
            this.Canvas.strokeStyle='rgba(255,0,0,0.5)';
            this.Canvas.lineWidth=20 * GetDevicePixelRatio();
            this.Canvas.stroke();
        }
        */

        var line={Start:ptStart, End:ptEnd};
        this.LinePoint.push(line);
        
        this.DrawPoint(drawPoint);  //画点
        this.Canvas.restore();
    }
}

//画图工具-射线
function ChartDrawPictureHaflLine()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureHaflLine';
    this.IsPointIn=this.IsPointIn_XYValue_Line;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint || drawPoint.length!=2) return;

        var ptStart=drawPoint[0];
        var ptEnd=drawPoint[1];
        this.ClipFrame();

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(drawPoint[0].X,drawPoint[0].Y);
        this.Canvas.lineTo(drawPoint[1].X,drawPoint[1].Y);
        var endPoint=this.CalculateEndPoint(drawPoint);
        this.Canvas.lineTo(endPoint.X,endPoint.Y);
        this.Canvas.stroke();

        var line={Start:ptStart, End:ptEnd};
        this.LinePoint.push(line);

        this.DrawPoint(drawPoint);  //画点
        this.Canvas.restore();
    }

    this.CalculateEndPoint=function(aryPoint)
    {
        var left=this.Frame.ChartBorder.GetLeft();
        var right=this.Frame.ChartBorder.GetRight();

        var a=aryPoint[1].X-aryPoint[0].X;
        var b=aryPoint[1].Y-aryPoint[0].Y;

        if (a>0)
        {
            var a1=right-aryPoint[0].X;
            var b1=a1*b/a;
            var y=b1+aryPoint[0].Y;

            var pt=new Point();
            pt.X=right;
            pt.Y=y;
            return pt;
        }
        else
        {
            var a1=aryPoint[0].X-left;
            var b1=a1*b/Math.abs(a);
            var y=b1+aryPoint[0].Y;

            var pt=new Point();
            pt.X=left;
            pt.Y=y;
            return pt;
        }
    }
}

// 画图工具-水平线
function ChartDrawPictureHorizontalLine()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.PointCount=1;
    this.ClassName='ChartDrawPictureHorizontalLine';
    this.IsPointIn=this.IsPointIn_XYValue_Line;
    this.Font=16*GetDevicePixelRatio() +"px 微软雅黑";

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint();
        if (!drawPoint || drawPoint.length!=1) return;
        if (!this.Frame) return;
        if (this.Value.length!=1) return;
        if (!this.IsYValueInFrame(this.Value[0].YValue)) return null;

        var left=this.Frame.ChartBorder.GetLeft();
        var right=this.Frame.ChartBorder.GetRight();
        this.ClipFrame();

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(left,drawPoint[0].Y);
        this.Canvas.lineTo(right,drawPoint[0].Y);
        this.Canvas.stroke();

        var line={Start:new Point(), End:new Point()};
        line.Start.X=left;
        line.Start.Y=drawPoint[0].Y;
        line.End.X=right;
        line.End.Y=drawPoint[0].Y;
        this.LinePoint.push(line);

        //显示价格
        var yValue=this.Frame.GetYData(drawPoint[0].Y);
        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="let";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=this.Font;
        this.Canvas.fillText(yValue.toFixed(2),left,drawPoint[0].Y);

        this.DrawPoint(drawPoint);
        this.Canvas.restore();
    }
}

//趋势线
function ChartDrawPictureTrendLine()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureTrendLine';
    this.IsPointIn=this.IsPointIn_XYValue_Line;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint || drawPoint.length!=2) return;

        var ptStart=drawPoint[0];
        var ptEnd=drawPoint[1];
        var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd);

        this.ClipFrame();

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(extendLine.Start.X,extendLine.Start.Y);
        this.Canvas.lineTo(extendLine.End.X,extendLine.End.Y);
        this.Canvas.stroke();

        var line={Start:ptStart, End:ptEnd};
        this.LinePoint.push(line);

        this.DrawPoint(drawPoint);  //画点
        this.Canvas.restore();
    }
}


//画图工具-矩形
function ChartDrawPictureRect()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureRect';

    this.Draw=function()
    {
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint || drawPoint.length!=2) return;

        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.rect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y);
        this.Canvas.stroke();
        
        //透明背景
        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.fillRect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y);
        this.Canvas.restore();

        //画点
        this.DrawPoint(drawPoint);
    }

    //0-10 鼠标对应的点索引   100=鼠标在正个图形上  -1 鼠标不在图形上
    this.IsPointIn=function(x,y)
    {
        if (!this.Frame || this.Status!=10) return -1;

        var data=this.Frame.Data;
        if (!data) return -1;

        var nIndex=this.IsPointInXYValue(x,y);
        if (nIndex>=0) return nIndex;

        var aryPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!aryPoint || aryPoint.length!=2) return -1;

        //是否在矩形边框上
        var linePoint=[ {X:aryPoint[0].X,Y:aryPoint[0].Y},{X:aryPoint[1].X,Y:aryPoint[0].Y}];
        if (this.IsPointInLine(linePoint,x,y))
            return 100;

        linePoint=[ {X:aryPoint[1].X,Y:aryPoint[0].Y},{X:aryPoint[1].X,Y:aryPoint[1].Y}];
        if (this.IsPointInLine2(linePoint,x,y))
            return 100;

        linePoint=[ {X:aryPoint[1].X,Y:aryPoint[1].Y},{X:aryPoint[0].X,Y:aryPoint[1].Y}];
        if (this.IsPointInLine(linePoint,x,y))
            return 100;

        linePoint=[ {X:aryPoint[0].X,Y:aryPoint[1].Y},{X:aryPoint[0].X,Y:aryPoint[0].Y}];
        if (this.IsPointInLine2(linePoint,x,y))
            return 100;

        return -1;
    }

    //点是否在线段上 水平线段
    this.IsPointInLine=function(aryPoint,x,y)
    {
        var radius=5;
        if (this.Option) radius+=this.Option.Zoom;
        var pixel=GetDevicePixelRatio();
        radius*=pixel;

        this.Canvas.beginPath();
        this.Canvas.moveTo(aryPoint[0].X,aryPoint[0].Y+radius);
        this.Canvas.lineTo(aryPoint[0].X,aryPoint[0].Y-radius);
        this.Canvas.lineTo(aryPoint[1].X,aryPoint[1].Y-radius);
        this.Canvas.lineTo(aryPoint[1].X,aryPoint[1].Y+radius);
        this.Canvas.closePath();
        if (this.Canvas.isPointInPath(x,y))
            return true;
    }

    //垂直线段
    this.IsPointInLine2=function(aryPoint,x,y)
    {
        var radius=5;
        if (this.Option) radius+=this.Option.Zoom;
        var pixel=GetDevicePixelRatio();
        radius*=pixel;

        this.Canvas.beginPath();
        this.Canvas.moveTo(aryPoint[0].X-radius,aryPoint[0].Y);
        this.Canvas.lineTo(aryPoint[0].X+radius,aryPoint[0].Y);
        this.Canvas.lineTo(aryPoint[1].X+radius,aryPoint[1].Y);
        this.Canvas.lineTo(aryPoint[1].X-radius,aryPoint[1].Y);
        this.Canvas.closePath();
        if (this.Canvas.isPointInPath(x,y))
            return true;
    }
}


//画图工具-弧形
function ChartDrawPictureArc()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureArc';

    this.Draw=function()
    {
        var drawPoint=this.CalculateDrawPoint();
        if (!drawPoint || drawPoint.length!=2) return;

        this.ClipFrame();

        //this.Canvas.beginPath();
        //this.Canvas.rect(drawPoint[0].X,drawPoint[0].Y,drawPoint[1].X-drawPoint[0].X,drawPoint[1].Y-drawPoint[0].Y);
        if (drawPoint[0].X < drawPoint[1].X && drawPoint[0].Y > drawPoint[1].Y) // 第一象限
        {
            var a = drawPoint[1].X - drawPoint[0].X;
            var b = drawPoint[0].Y - drawPoint[1].Y;
            var step = (a > b) ? 1/a : 1 / b;
            var xcenter = drawPoint[0].X;
            var ycenter = drawPoint[1].Y;
            this.Canvas.beginPath();
            this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y);
            for (var i = 1.5*Math.PI; i < 2*Math.PI; i+=step)
            {
                this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter+b*Math.sin(i)*-1);
            }
            for (var j = 0; j <= 0.5*Math.PI; j += step)
            {
                this.Canvas.lineTo(xcenter+a*Math.cos(j), ycenter+b*Math.sin(j)*-1);
            }
        }
        else if (drawPoint[0].X > drawPoint[1].X && drawPoint[0].Y > drawPoint[1].Y) // 第二象限
        {
            var a = drawPoint[0].X - drawPoint[1].X;
            var b = drawPoint[0].Y - drawPoint[1].Y;
            var step = (a > b) ? 1/a:1/b;
            var xcenter = drawPoint[1].X;
            var ycenter = drawPoint[0].Y;
            this.Canvas.beginPath();
            this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y);
            for (var i = 0; i <= Math.PI; i += step)
            {
                this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1);
            }
        }
        else if (drawPoint[0].X > drawPoint[1].X && drawPoint[0].Y < drawPoint[1].Y) // 第三象限
        {
            var a = drawPoint[0].X - drawPoint[1].X;
            var b = drawPoint[1].Y - drawPoint[0].Y;
            var step = (a > b) ? 1/a:1/b;
            var xcenter = drawPoint[0].X;
            var ycenter = drawPoint[1].Y;
            this.Canvas.beginPath();
            this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y);
            for (var i = 0.5*Math.PI; i <= 1.5*Math.PI; i += step)
            {
                this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1);
            }
        }
        else if (drawPoint[0].X < drawPoint[1].X && drawPoint[0].Y < drawPoint[1].Y) // 第四象限
        {
            var a = drawPoint[1].X - drawPoint[0].X;
            var b = drawPoint[1].Y - drawPoint[0].Y;
            var step = (a > b) ? 1/a : 1/b;
            var xcenter = drawPoint[1].X;
            var ycenter = drawPoint[0].Y;
            this.Canvas.beginPath();
            this.Canvas.moveTo(drawPoint[0].X, drawPoint[0].Y);
            for (var i = Math.PI; i <= 2*Math.PI; i += step)
            {
                this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter + b*Math.sin(i)*-1);
            }
        }


        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.stroke();
        //this.Canvas.closePath();
        this.Canvas.restore();

        //画点
        this.DrawPoint(drawPoint);
    }

    //0-10 鼠标对应的点索引   100=鼠标在正个图形上  -1 鼠标不在图形上
    this.IsPointIn=function(x,y)
    {
        if (!this.Frame || this.Status!=10) return -1;

        var data=this.Frame.Data;
        if (!data) return -1;

        //是否在点上
        var nIndex=this.IsPointInXYValue(x,y);
        if (nIndex>=0) return nIndex;

        var aryPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!aryPoint || aryPoint.length!=2) return -1;

        //是否在弧线上
        var ArcPoint=[ {X:aryPoint[0].X,Y:aryPoint[0].Y},{X:aryPoint[1].X,Y:aryPoint[1].Y}];
        if (this.IsPointInArc(ArcPoint, x, y))
            return 100;

        return -1;
    }
    this.IsPointInArc=function(aryPoint,x,y)
    {
        if (aryPoint.length != 2)
         return false;
        if (aryPoint[0].X < aryPoint[1].X && aryPoint[0].Y > aryPoint[1].Y) // 第一象限
        {
             var a = aryPoint[1].X - aryPoint[0].X;
             var b = aryPoint[0].Y - aryPoint[1].Y;
             var step = (a > b) ? 1/a : 1 / b;
             var ainer = a * 0.8;
             var biner = b * 0.8;
             var stepiner = (ainer > biner) ? 1/ainer : 1/biner;
             var xcenter = aryPoint[0].X;
             var ycenter = aryPoint[1].Y;
             this.Canvas.beginPath();
             this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y);
             for (var i = 1.5*Math.PI; i < 2*Math.PI; i+=step)
             {
                 this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter+b*Math.sin(i)*-1);
             }
             for (var j = 0; j <= 0.5*Math.PI; j += step)
             {
                 this.Canvas.lineTo(xcenter+a*Math.cos(j), ycenter+b*Math.sin(j)*-1);
             }
             for (var k = 0.5*Math.PI; k >= 0; k -= stepiner)
             {
                 this.Canvas.lineTo(xcenter+ainer*Math.cos(k), ycenter + biner*Math.sin(j)*-1);
             }
             for (var l = 2*Math.PI; l >= 1.5*Math.PI; l -= stepiner)
             {
                 this.Canvas.lineTo(xcenter + ainer*Math.cos(l), ycenter + biner*Math.sin(l)*-1);
             }
             this.Canvas.closePath();
        }
         else if (aryPoint[0].X > aryPoint[1].X && aryPoint[0].Y > aryPoint[1].Y) // 第二象限
         {
             var a = aryPoint[0].X - aryPoint[1].X;
             var b = aryPoint[0].Y - aryPoint[1].Y;
             var step = (a > b) ? 1/a:1/b;
             var ainer = a * 0.8;
             var biner = b * 0.8;
             var stepiner = (ainer > biner) ? 1 / ainer : 1 / biner;
             var xcenter = aryPoint[1].X;
             var ycenter = aryPoint[0].Y;
             this.Canvas.beginPath();
             this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y);
             for (var i = 0; i <= Math.PI; i += step)
             {
                 this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1);
             }
             for (var j = Math.PI; j >= 0; j -= stepiner)
             {
                 this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner*Math.sin(j)*-1);
             }
             this.Canvas.closePath();
         }
         else if (aryPoint[0].X > aryPoint[1].X && aryPoint[0].Y < aryPoint[1].Y) // 第三象限
         {
             var a = aryPoint[0].X - aryPoint[1].X;
             var b = aryPoint[1].Y - aryPoint[0].Y;
             var step = (a > b) ? 1/a:1/b;
             var ainer = a * 0.8;
             var biner = b * 0.8;
             var stepiner = (ainer > biner) ? 1/ainer : 1/biner;
             var xcenter = aryPoint[0].X;
             var ycenter = aryPoint[1].Y;
             this.Canvas.beginPath();
             this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y);
             for (var i = 0.5*Math.PI; i <= 1.5*Math.PI; i += step)
             {
                 this.Canvas.lineTo(xcenter + a*Math.cos(i), ycenter + b*Math.sin(i)*-1);
             }
             for (var j = 1.5*Math.PI; j >= 0.5*Math.PI; j -= stepiner)
             {
                 this.Canvas.lineTo(xcenter + ainer * Math.cos(j), ycenter + biner*Math.sin(j)*-1);
             }
             this.Canvas.closePath();
         }
         else if (aryPoint[0].X < aryPoint[1].X && aryPoint[0].Y < aryPoint[1].Y) // 第四象限
         {
             var a = aryPoint[1].X - aryPoint[0].X;
             var b = aryPoint[1].Y - aryPoint[0].Y;
             var step = (a > b) ? 1/a : 1/b;
             var ainer = a * 0.8;
             var biner = b * 0.8;
             var stepiner = (ainer > biner) ? 1/ainer : 1/biner;
             var xcenter = aryPoint[1].X;
             var ycenter = aryPoint[0].Y;
             this.Canvas.beginPath();
             this.Canvas.moveTo(aryPoint[0].X, aryPoint[0].Y);
             for (var i = Math.PI; i <= 2*Math.PI; i += step)
             {
                 this.Canvas.lineTo(xcenter+a*Math.cos(i), ycenter + b*Math.sin(i)*-1);
             }
             for (var j = 2*Math.PI; j >= Math.PI; j -= stepiner)
             {
                 this.Canvas.lineTo(xcenter + ainer*Math.cos(j), ycenter + biner*Math.sin(j)*-1);
             }
             this.Canvas.closePath();
         }
         if (this.Canvas.isPointInPath(x,y))
            return true;
         else
            return false;

    }

}

//M头W底
function ChartDrawPictureWaveMW()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureWaveMW';
    this.PointCount=2;
    this.IsPointIn=this.IsPointIn_XYValue_Line;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint) return;
        
        //var points=drawPoint.slice(0);
        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();
        
        this.CalculateLines(drawPoint);
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
        }
       
        this.DrawPoint(drawPoint); //画点
        this.Canvas.restore();
    }

    this.CalculateLines=function(points)
    {
        if (!this.Frame) return;
        if (points.length<2) return;

        if (this.Status==2)
        {
            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[0].Y;
            line.Start.X=points[0].X;
            line.End.Y=points[1].Y;
            line.End.X=points[1].X;
            this.LinePoint.push(line);
    
            var xMove=points[1].X-points[0].X;
            line={Start:new Point(), End:new Point()};
            line.Start.Y=points[1].Y;
            line.Start.X=points[1].X;
            line.End.Y=points[0].Y;
            line.End.X=points[1].X+xMove;
            this.LinePoint.push(line);

            var ptStart=line.End;
            var newPt=new Point();
            newPt.X=ptStart.X;
            newPt.Y=ptStart.Y;
            this.Point[2]=newPt;
            line={Start:new Point(), End:new Point()};
            line.Start.Y=ptStart.Y;
            line.Start.X=ptStart.X;
            line.End.Y=points[1].Y;
            line.End.X=ptStart.X+xMove;
            this.LinePoint.push(line);

            var ptStart=line.End;
            var newPt=new Point();
            newPt.X=ptStart.X;
            newPt.Y=ptStart.Y;
            this.Point[3]=newPt;
            line={Start:new Point(), End:new Point()};
            line.Start.Y=ptStart.Y;
            line.Start.X=ptStart.X;
            line.End.Y=points[0].Y;
            line.End.X=ptStart.X+xMove;
            this.LinePoint.push(line);

            var ptStart=line.End;
            var newPt=new Point();
            newPt.X=ptStart.X;
            newPt.Y=ptStart.Y;
            this.Point[4]=newPt;

            this.PointCount=this.Point.length;
        }
        else if (points.length==5)
        {
            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[0].Y;
            line.Start.X=points[0].X;
            line.End.Y=points[1].Y;
            line.End.X=points[1].X;
            this.LinePoint.push(line);
    
            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[1].Y;
            line.Start.X=points[1].X;
            line.End.Y=points[2].Y;
            line.End.X=points[2].X;
            this.LinePoint.push(line);

            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[2].Y;
            line.Start.X=points[2].X;
            line.End.Y=points[3].Y;
            line.End.X=points[3].X;
            this.LinePoint.push(line);

            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[3].Y;
            line.Start.X=points[3].X;
            line.End.Y=points[4].Y;
            line.End.X=points[4].X;
            this.LinePoint.push(line);
        }
    }
}

//平行线
function ChartDrawPictureParallelLines()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureParallelLines';
    this.IsPointIn=this.IsPointIn_XYValue_Line;
    this.PointCount=3;
    this.LastPoint;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint) return;

        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        var points=drawPoint.slice(0);
        this.CalculateLines(points);

        this.ClipFrame();

        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
        }

        this.DrawArea();
        this.DrawPoint(points);  //画点
        this.Canvas.restore(); 
    }

    this.SetLastPoint=function(obj)
    {
        this.LastPoint={X:obj.X,Y:obj.Y};
    }

    this.DrawArea=function()
    {
        if (this.LinePoint.length!=2) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(this.LinePoint[0].Start.X,this.LinePoint[0].Start.Y);
        this.Canvas.lineTo(this.LinePoint[0].End.X,this.LinePoint[0].End.Y);
        this.Canvas.lineTo(this.LinePoint[1].End.X,this.LinePoint[1].End.Y);
        this.Canvas.lineTo(this.LinePoint[1].Start.X,this.LinePoint[1].Start.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    this.CalculateLines=function(points)
    {
        if (this.PointStatus==2 && this.LastPoint)
        {
            var pt=new Point();
            pt.X=this.LastPoint.X;
            pt.Y=this.LastPoint.Y;
            points[2]=pt;
        }

        if (points.length==2)
        {
            var linePoint=this.CalculateExtendLinePoint(points[0],points[1]);
            this.LinePoint.push(linePoint);
        }
        else if (points.length==3)
        {
            var linePoint=this.CalculateExtendLinePoint(points[0],points[1]);
            this.LinePoint.push(linePoint);

            //计算平行线
            var xMove=points[2].X-points[1].X;
            var yMove=points[2].Y-points[1].Y;

            var ptStart=new Point();
            var ptEnd=new Point();
            ptStart.X=points[0].X+xMove;
            ptStart.Y=points[0].Y+yMove;
            ptEnd.X=points[1].X+xMove;
            ptEnd.Y=points[1].Y+yMove;
            linePoint=this.CalculateExtendLinePoint(ptStart,ptEnd);
            this.LinePoint.push(linePoint);
        }
    }
}

//价格通道线
function ChartDrawPicturePriceChannel()
{
    this.newMethod=ChartDrawPictureParallelLines;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPicturePriceChannel';

    this.CalculateLines=function(points)
    {
        if (this.PointStatus==2 && this.LastPoint)
        {
            var pt=new Point();
            pt.X=this.LastPoint.X;
            pt.Y=this.LastPoint.Y;
            points[2]=pt;
        }

        if (points.length==2)
        {
            var linePoint=this.CalculateExtendLinePoint(points[0],points[1]);
            this.LinePoint.push(linePoint);
        }
        else if (points.length==3)
        {
            var linePoint=this.CalculateExtendLinePoint(points[0],points[1]);
            this.LinePoint.push(linePoint);

            //计算平行线
            var xMove=points[2].X-points[1].X;
            var yMove=points[2].Y-points[1].Y;

            var ptStart=new Point();
            var ptEnd=new Point();
            ptStart.X=points[0].X+xMove;
            ptStart.Y=points[0].Y+yMove;
            ptEnd.X=points[1].X+xMove;
            ptEnd.Y=points[1].Y+yMove;
            linePoint=this.CalculateExtendLinePoint(ptStart,ptEnd);
            this.LinePoint.push(linePoint);

            var ptStart=new Point();
            var ptEnd=new Point();
            ptStart.X=points[0].X-xMove;
            ptStart.Y=points[0].Y-yMove;
            ptEnd.X=points[1].X-xMove;
            ptEnd.Y=points[1].Y-yMove;
            linePoint=this.CalculateExtendLinePoint(ptStart,ptEnd);
            this.LinePoint.push(linePoint);
        }
    }

    this.DrawArea=function()
    {
        if (this.LinePoint.length!=3) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(this.LinePoint[1].Start.X,this.LinePoint[1].Start.Y);
        this.Canvas.lineTo(this.LinePoint[1].End.X,this.LinePoint[1].End.Y);
        this.Canvas.lineTo(this.LinePoint[2].End.X,this.LinePoint[2].End.Y);
        this.Canvas.lineTo(this.LinePoint[2].Start.X,this.LinePoint[2].Start.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }
}

//平行通道
function ChartDrawPictureParallelChannel()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureParallelChannel';
    this.ChannelWidth=50;
    this.AreaColor='rgba(25,25,25,0.4)';
    this.LinePoint=[];

    //计算需要画的点的坐标
    this.CalculateDrawPoint=function()
    {
        if (this.Status<2) return null;
        if(!this.Point.length || !this.Frame) return null;
        var data=this.Frame.Data;
        if (!data) return null;

        var drawPoint=[];
        if (this.Status==10)    //完成
        {
            for(var i=0; i<2; ++i)
            {
                var item=this.Value[i];
                var pt=new Point();
                pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset);
                pt.Y=this.Frame.GetYFromData(item.YValue);
                drawPoint.push(pt);
            }
        }
        else
        {
            for(var i=0; i<this.Point.length; ++i)
            {
                var item=this.Point[i];
                var pt=new Point();
                pt.X=item.X;
                pt.Y=item.Y;
                drawPoint.push(pt);
            }
        }

        if (drawPoint.length>=2)
        {
            var linePoint={Start:new Point(), End:new Point() };
            linePoint.Start.X=drawPoint[0].X;
            linePoint.Start.Y=drawPoint[0].Y;
            linePoint.End.X=drawPoint[1].X;
            linePoint.End.Y=drawPoint[1].Y;
            this.LinePoint.push(linePoint);

            if (drawPoint.length==3 || this.Status==10)
            {
                var x=linePoint.End.X-linePoint.Start.X;
                var y=linePoint.End.Y-linePoint.Start.Y;
                var angle=Math.atan(Math.abs(x/y));
                var yMove=this.ChannelWidth/Math.sin(angle);

                //JSConsole.Chart.Log('[ChartDrawPictureParallelChannel::CalculateDrawPoint]',xMove);
                
                linePoint={Start:new Point(), End:new Point() };
                linePoint.Start.X=drawPoint[0].X;
                linePoint.Start.Y=drawPoint[0].Y-yMove;
                linePoint.End.X=drawPoint[1].X;
                linePoint.End.Y=drawPoint[1].Y-yMove;
                this.LinePoint.push(linePoint);

                var ptCenter=new Point();
                ptCenter.X=linePoint.Start.X+(linePoint.End.X-linePoint.Start.X)/2;
                ptCenter.Y=linePoint.Start.Y+(linePoint.End.Y-linePoint.Start.Y)/2;
                drawPoint[3]=ptCenter;

                this.Point[2]=ptCenter;
                var xValue=parseInt(this.Frame.GetXData(ptCenter.X))+data.DataOffset;
                var yValue=this.Frame.GetYData(ptCenter.Y);
                this.Value[2]={XValue:xValue,YValue:yValue};
                this.PointCount=this.Point.length;  //完成以后是3个点
            }
        }
    
        return drawPoint;
    }

    this.DrawLine=function(ptStart,ptEnd)
    {
        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ptStart.X,ptStart.Y);
        this.Canvas.lineTo(ptEnd.X,ptEnd.Y);
        this.Canvas.stroke();
    }

    this.DrawArea=function(pt,pt2,pt3,pt4)
    {
        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(pt.X,pt.Y);
        this.Canvas.lineTo(pt2.X,pt2.Y);
        this.Canvas.lineTo(pt3.X,pt3.Y);
        this.Canvas.lineTo(pt4.X,pt4.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint();
        if (!drawPoint) return;

        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();

        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
        }

        if (this.LinePoint.length==2)
        {
            this.DrawArea(this.LinePoint[0].Start,this.LinePoint[0].End,this.LinePoint[1].End,this.LinePoint[1].Start);
        }

        this.Canvas.restore();
       
        this.DrawPoint(drawPoint); //画点
    }

    //xStep,yStep 移动的偏移量
    this.Move=function(xStep,yStep)
    {
        if (this.Status!=20) return fasle;
        if (!this.Frame) return false;
        var data=this.Frame.Data;
        if (!data) return false;

        if (this.MovePointIndex==100)    //整体移动
        {
            for(var i in this.Point)
            {
                this.Point[i].X+=xStep;
                this.Point[i].Y+=yStep;
            }
        }
        else if (this.MovePointIndex==0 || this.MovePointIndex==1)
        {
            if (this.MovePointIndex<this.Point.length)
            {
                this.Point[this.MovePointIndex].X+=xStep;
                this.Point[this.MovePointIndex].Y+=yStep;
            }
        }
        else if (this.MovePointIndex==2)    //宽度的点要计算
        {
            this.Point[this.MovePointIndex].X+=xStep;
            this.Point[this.MovePointIndex].Y+=yStep;

            var x=this.Point[1].X-this.Point[0].X;
            var y=this.Point[1].Y-this.Point[0].Y;
            var angle=Math.atan(Math.abs(x/y));
            var yMove=this.ChannelWidth/Math.sin(angle)-yStep;
            this.ChannelWidth=Math.sin(angle)*yMove;
        }
    }

    //0-10 鼠标对应的点索引   100=鼠标在正个图形上  -1 鼠标不在图形上
    this.IsPointIn=function(x,y)
    {
        if (!this.Frame || this.Status!=10) return -1;

        var data=this.Frame.Data;
        if (!data) return -1;

        //是否在点上
        for(var i=0;i<this.Value.length; ++i)
        {
            var item=this.Value[i];
            var pt=new Point();
            if (i<2)
            {
                pt.X=this.Frame.GetXFromIndex(item.XValue-data.DataOffset);
                pt.Y=this.Frame.GetYFromData(item.YValue);
            }
            else    //第3个点使用实际坐标
            {
                if (i>=this.Point.length) continue;
                pt.X=this.Point[i].X;
                pt.Y=this.Point[i].Y;
            }

            this.Canvas.beginPath();
            this.Canvas.arc(pt.X,pt.Y,5,0,360);
            if (this.Canvas.isPointInPath(x,y))  return i;
        }

        //是否在线段上
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            var ptStart=item.Start;
            var ptEnd=item.End;
            this.Canvas.beginPath();
            this.Canvas.moveTo(ptStart.X,ptStart.Y+5);
            this.Canvas.lineTo(ptStart.X,ptStart.Y-5);
            this.Canvas.lineTo(ptEnd.X,ptEnd.Y-5);
            this.Canvas.lineTo(ptEnd.X,ptEnd.Y+5);
            this.Canvas.closePath();
            if (this.Canvas.isPointInPath(x,y))
                return 100;
        }

        return -1;
    }
}

//文本
function ChartDrawPictureText()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureText';
    this.Text='文本';
    this.PointCount=1;
    this.FontOption={ Family:'微软雅黑', Size:20, Weight:null, Style:null };    //Weight(bold 粗体), Style(italic)
    //矢量图片
    //this.Text="\ue606";
    //this.FontOption={ Family:'iconfont', Size:20, Weight:null, Style:null };    //Weight(bold 粗体), Style(italic)
    this.TextRect=null;   //文字区域
    this.IsInitialized=false;   //是否初始化了
    this.SettingMenu;
    this.HQChart;

    this.Draw=function(textFont)
    {
        this.TextRect=null;
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint || drawPoint.length!=1) return;

        this.ClipFrame();

        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="center";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=this.GetTextFont();
        this.Canvas.fillText(this.Text,drawPoint[0].X,drawPoint[0].Y);
        var textWidth=this.Canvas.measureText(this.Text).width;

        this.TextRect={};
        this.TextRect.Left=drawPoint[0].X-textWidth/2;
        this.TextRect.Top=drawPoint[0].Y-this.FontOption.Size;
        this.TextRect.Width=textWidth;
        this.TextRect.Height=this.FontOption.Size;
        //this.Canvas.strokeRect(this.TextRect.Left,this.TextRect.Top,this.TextRect.Width,this.TextRect.Height);
        this.Canvas.restore();

        if (this.IsInitialized===false) 
        {
            this.SetTextOption();
            this.IsInitialized=true;
        }
    }

    //根据设置动态生成字体
    this.GetTextFont=function()
    {
        const defaultFont=16*GetDevicePixelRatio() +"px 微软雅黑";
        if (!this.FontOption || !this.FontOption.Family || this.FontOption.Size<=0) return defaultFont;

        var font='';
        if (this.FontOption.Color) font+=this.FontOption.Color+' ';
        if (this.FontOption.Style) font+=this.FontOption.Style+' ';
        if (this.FontOption.Weight) font+=this.FontOption.Weight+' ';
        if (this.FontOption.Size>=0) font+=this.FontOption.Size*GetDevicePixelRatio()+'px ';
        font+=this.FontOption.Family;

        return font;
    }

    this.SetTextOption=function()
    {
        JSConsole.Chart.Log('[ChartDrawPictureText::SetTextOption]');
        //创建div设置窗口
        if (!this.SettingMenu) this.SettingMenu=new ChartPictureTextSettingMenu(this.Frame.ChartBorder.UIElement.parentNode);

        this.SettingMenu.ChartPicture=this;
        this.SettingMenu.HQChart=this.HQChart;
        this.SettingMenu.Position={Left:this.TextRect.Left+this.TextRect.Width,Top:this.TextRect.Top};
        this.SettingMenu.DoModal();
    }

    this.IsPointIn=function(x,y)
    {
        if (!this.Frame || this.Status!=10) return -1;

        var data=this.Frame.Data;
        if (!data) return -1;
        if (!this.TextRect) return -1;

        this.Canvas.beginPath();
        this.Canvas.rect(this.TextRect.Left,this.TextRect.Top,this.TextRect.Width,this.TextRect.Height);
        if (this.Canvas.isPointInPath(x,y)) return 100;

        return -1;
    }
}

//iconfont 图片
function ChartDrawPictureIconFont()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureIconFont';
    this.PointCount=1;
    //矢量图片
    this.Text="\ue606";
    this.FontOption={ Family:'iconfont', Size:24};    //Weight(bold 粗体), Style(italic)
    this.TextRect=null;         //文字区域
    this.SettingMenu;

    this.Draw=function()
    {
        this.TextRect=null;
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint || drawPoint.length!=1) return;
        var font=this.GetTextFont();
        if (!font) return;

        this.ClipFrame();

        var isHScreen=this.Frame.IsHScreen;
        var pixel=GetDevicePixelRatio();

        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="center";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=font;
        if (isHScreen)
        {
            this.Canvas.translate(drawPoint[0].X, drawPoint[0].Y);
            this.Canvas.rotate(90 * Math.PI / 180);
            this.Canvas.fillText(this.Text,0,0);
        }
        else
        {
            this.Canvas.fillText(this.Text,drawPoint[0].X,drawPoint[0].Y);
        }
        
        var textWidth=this.Canvas.measureText(this.Text).width;
        this.TextRect={};
        this.TextRect.Left=drawPoint[0].X-textWidth/2;
        this.TextRect.Top=drawPoint[0].Y-this.FontOption.Size*pixel;
        this.TextRect.Width=textWidth;
        this.TextRect.Height=this.FontOption.Size*pixel;

        //this.Canvas.strokeRect(this.TextRect.Left,this.TextRect.Top,this.TextRect.Width,this.TextRect.Height);
        this.Canvas.restore();
    }

    //根据设置动态生成字体
    this.GetTextFont=function()
    {
        if (!this.FontOption || !this.FontOption.Family || this.FontOption.Size<=0) return null;

        var font='';
        if (this.FontOption.Size>=0) font+=this.FontOption.Size*GetDevicePixelRatio()+'px ';
        font+=this.FontOption.Family;

        return font;
    }

    this.IsPointIn=function(x,y)
    {
        if (!this.Frame || this.Status!=10) return -1;

        var data=this.Frame.Data;
        if (!data) return -1;
        if (!this.TextRect) return -1;

        var offset=0;
        if (this.Option && this.Option.Zoom>=1)
        {
            offset=this.Option.Zoom*GetDevicePixelRatio();
        }

        this.Canvas.beginPath();
        this.Canvas.rect(this.TextRect.Left-offset,this.TextRect.Top-offset,this.TextRect.Width+offset*2,this.TextRect.Height+offset*2);
        if (this.Canvas.isPointInPath(x,y)) return 100;

        return -1;
    }
}

//江恩角度线(Gann Fan),亦又称作甘氏线的
function ChartDrawPictureGannFan()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureGannFan';
    this.IsPointIn=this.IsPointIn_XYValue_Line;
    this.LinePoint=[];
    this.Font=16*GetDevicePixelRatio() +"px 微软雅黑";


    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint) return;
        if (drawPoint.length!=2) return;

        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();
        var quadrant=this.GetQuadrant(drawPoint[0],drawPoint[1]);

        if (quadrant===1 || quadrant===4)
        {
            this.CalculateLines(drawPoint[0],drawPoint[1],quadrant);
            this.DrawArea();

            for(var i in this.LinePoint)
            {
                var item=this.LinePoint[i];
                this.DrawLine(item.Start,item.End,item.IsDottedLine);
            }

            for(var i in this.LinePoint)
            {
                var item =this.LinePoint[i];
                if (item.Text && item.PtEnd) this.DrawTitle(item.PtEnd,item.Text);
            }
        }
        else
        {
            this.DrawLine(drawPoint[0],drawPoint[1],false);
        }

        this.Canvas.restore();
        this.DrawPoint(drawPoint); //画点
    }

    //获取在第几象限
    this.GetQuadrant=function(ptStart,ptEnd)
    {
        if (ptStart.X<ptEnd.X && ptStart.Y>ptEnd.Y) return 1;
        else if (ptStart.X<ptEnd.X && ptStart.Y>ptEnd.Y) return 2;
        else if (ptStart.X < ptEnd.X && ptStart.Y< ptEnd.Y) return 4;
        else return 3;
    }

    //isDotline 是否是虚线
    this.DrawLine=function(ptStart,ptEnd,isDottedline)
    {
        if (isDottedline) this.Canvas.setLineDash([5,10]);

        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ptStart.X,ptStart.Y);
        this.Canvas.lineTo(ptEnd.X,ptEnd.Y);
        this.Canvas.stroke();

        if (isDottedline) this.Canvas.setLineDash([]);
    }

    this.DrawTitle=function(pt,text)
    {
        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=this.Font;
        this.Canvas.fillText(text,pt.X,pt.Y);
    }

    this.DrawArea=function()
    {
        var lineStart=null,lineEnd=null;
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            if (item.Text=='1:8') lineStart=this.LinePoint[i];
            else if (item.Text=='8:1') lineEnd=this.LinePoint[i];
        }

        if (!lineStart || !lineEnd) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(lineStart.End.X,lineStart.End.Y);
        this.Canvas.lineTo(lineStart.Start.X,lineStart.Start.Y);
        this.Canvas.lineTo(lineEnd.End.X,lineEnd.End.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    //计算线段
    this.CalculateLines=function(ptStart,ptEnd,quadrant)
    {
        if (!this.Frame) return false;
        var top=this.Frame.ChartBorder.GetTopEx();
        var right=this.Frame.ChartBorder.GetRight();
        var bottom=this.Frame.ChartBorder.GetBottom();

        const SPLIT_LINE_VALUE=[0.5, 1.0/3, 0.25, 0.125, 2.0/3]; 
        const SPLIT_LINE_X_TITLE=["1:2","1:3","1:4","1:8","2:3"];
	    const SPLIT_LINE_Y_TITLE=["2:1","3:1","4:1","8:1","3:2"];
        var ptLineStart=new Point();
        var ptLineEnd=new Point();
        ptLineStart.X=ptStart.X;
        ptLineStart.Y=ptStart.Y;
        ptLineEnd.X=ptEnd.X;
        ptLineEnd.Y=ptEnd.Y;
        var lineWidth=Math.abs(ptStart.X-ptEnd.X);
        var lineHeight=Math.abs(ptStart.Y-ptEnd.Y);
        if (quadrant===1)
        {
            /*
            var line={Start:ptLineStart, End:new Point(), IsDottedLine:false};
            line.End.X=ptStart.X;
            line.End.Y=top;
            this.LinePoint.push(line);

            line={Start:ptLineStart, End:new Point(), IsDottedLine:false};
            line.End.X=right;
            line.End.Y=ptStart.Y;
            this.LinePoint.push(line);
            */

            var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd);
            var line={Start:ptLineStart, End:extendLine.Start, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'};
            this.LinePoint.push(line);

            for(var i in SPLIT_LINE_VALUE)
            {
                if (lineWidth>5)
                {
                    line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(),Text:SPLIT_LINE_X_TITLE[i]};
                    line.PtEnd.Y=ptEnd.Y;
                    line.PtEnd.X=ptStart.X+lineWidth*SPLIT_LINE_VALUE[i];
                    var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd);
                    line.End=extendLine.Start;
                    this.LinePoint.push(line);
                }
                if (lineHeight>5)
                {
                    line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]};
                    line.PtEnd.Y=ptStart.Y-lineHeight*SPLIT_LINE_VALUE[i];
                    line.PtEnd.X=ptEnd.X;
                    var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd);
                    line.End=extendLine.Start;
                    this.LinePoint.push(line);
                }
            }
            
        }
        else if (quadrant==4)
        {
            /*
            var line={Start:ptLineStart, End:new Point(), IsDottedLine:false};
            line.End.X=ptStart.X;
            line.End.Y=bottom;
            this.LinePoint.push(line);

            line={Start:ptLineStart, End:new Point(), IsDottedLine:false};
            line.End.X=right;
            line.End.Y=ptStart.Y;
            this.LinePoint.push(line);
            */

            var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd);
            var line={Start:ptLineStart, End:extendLine.End, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'};
            this.LinePoint.push(line);

            for(var i in SPLIT_LINE_VALUE)
            {
                if (lineWidth>5)
                {
                    line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(),Text:SPLIT_LINE_X_TITLE[i]};
                    line.PtEnd.Y=ptEnd.Y;
                    line.PtEnd.X=ptStart.X+lineWidth*SPLIT_LINE_VALUE[i];
                    var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd);
                    line.End=extendLine.End;
                    this.LinePoint.push(line);
                }
                if (lineHeight>5)
                {
                    line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]};
                    line.PtEnd.Y=ptStart.Y+lineHeight*SPLIT_LINE_VALUE[i];
                    line.PtEnd.X=ptEnd.X;
                    var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd);
                    line.End=extendLine.End;
                    this.LinePoint.push(line);
                }
            }
        }
        else return false;

        return true;
    }
}

//阻速线  (高 3等份)
function ChartDrawPictureResistanceLine()
{
    this.newMethod=ChartDrawPictureGannFan;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureResistanceLine';

    //计算线段
    this.CalculateLines=function(ptStart,ptEnd,quadrant)
    {
        if (!this.Frame) return false;
        var top=this.Frame.ChartBorder.GetTopEx();
        var right=this.Frame.ChartBorder.GetRight();
        var bottom=this.Frame.ChartBorder.GetBottom();

        const SPLIT_LINE_VALUE=[1.0/3, 2.0/3]; 
	    const SPLIT_LINE_Y_TITLE=["3:1","3:2"];
        var ptLineStart=new Point();
        var ptLineEnd=new Point();
        ptLineStart.X=ptStart.X;
        ptLineStart.Y=ptStart.Y;
        ptLineEnd.X=ptEnd.X;
        ptLineEnd.Y=ptEnd.Y;
        var lineWidth=Math.abs(ptStart.X-ptEnd.X);
        var lineHeight=Math.abs(ptStart.Y-ptEnd.Y);
        if (quadrant===1)
        {
            var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd);
            var line={Start:ptLineStart, End:extendLine.Start, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'};
            this.LinePoint.push(line);

            for(var i in SPLIT_LINE_VALUE)
            {
                if (lineHeight>5)
                {
                    line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]};
                    line.PtEnd.Y=ptStart.Y-lineHeight*SPLIT_LINE_VALUE[i];
                    line.PtEnd.X=ptEnd.X;
                    var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd);
                    line.End=extendLine.Start;
                    this.LinePoint.push(line);
                }
            }
            
        }
        else if (quadrant==4)
        {
            var extendLine=this.CalculateExtendLinePoint(ptStart,ptEnd);
            var line={Start:ptLineStart, End:extendLine.End, IsDottedLine:false,PtEnd:ptLineEnd, Text:'1:1'};
            this.LinePoint.push(line);

            for(var i in SPLIT_LINE_VALUE)
            {
                if (lineHeight>5)
                {
                    line={Start:ptLineStart, End:null, IsDottedLine:false,PtEnd:new Point(), Text:SPLIT_LINE_Y_TITLE[i]};
                    line.PtEnd.Y=ptStart.Y+lineHeight*SPLIT_LINE_VALUE[i];
                    line.PtEnd.X=ptEnd.X;
                    var extendLine=this.CalculateExtendLinePoint(line.Start,line.PtEnd);
                    line.End=extendLine.End;
                    this.LinePoint.push(line);
                }
            }
        }
        else return false;

        return true;
    }

    this.DrawArea=function()
    {
        var lineStart=null,lineEnd=null;
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            if (item.Text=='1:1') lineStart=this.LinePoint[i];
            else if (item.Text=='3:1') lineEnd=this.LinePoint[i];
        }

        if (!lineStart || !lineEnd) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(lineStart.End.X,lineStart.End.Y);
        this.Canvas.lineTo(lineStart.Start.X,lineStart.Start.Y);
        this.Canvas.lineTo(lineEnd.End.X,lineEnd.End.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }
}

//黄金分割线
function ChartDrawPictureGoldenSection()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureGoldenSectionLine';
    this.IsPointIn=this.IsPointIn_XYValue_Line;
    this.Font=14*GetDevicePixelRatio() +"px 微软雅黑";
    
    this.GetSectionData=function()
    {
        const GOLDEN_SECTION_DATA= [0,0.236,0.382,0.5,0.618,0.80,1,1.236,1.382,1.5,1.618,1.8,2];
        return GOLDEN_SECTION_DATA;
    }

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint();
        if (!drawPoint) return;
        if (drawPoint.length!=2) return;

        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        
        this.CalculateLines(drawPoint[0],drawPoint[1]);
        this.ClipFrame();
        
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End,item.IsDottedLine);
        }

        for(var i in this.LinePoint)
        {
            var item =this.LinePoint[i];
            if (item.Text) this.DrawTitle(item.Start,item.Text);
        }

        this.DrawPoint(drawPoint); //画点
        this.Canvas.restore();
        
    }

    this.CalculateHSCreenLines=function(ptStart,ptEnd)
    {
        var sectionData=this.GetSectionData();
        var left=this.Frame.ChartBorder.GetTop();
        var right=this.Frame.ChartBorder.GetBottom();
        var lineHeight=ptStart.X-ptEnd.X;

        for(var i=0;i<sectionData.length;++i)
        {
            var yMove=lineHeight*sectionData[i];

            var line={Start:new Point(), End:new Point()};
            line.Start.X=ptStart.X-yMove;
            line.Start.Y=left;
            line.End.X=ptStart.X-yMove;
            line.End.Y=right;
            
            var text='';
            if (i==0) text='Base '
            else text=(sectionData[i]*100).toFixed(2)+'% ';

            var yValue=this.Frame.GetYData(line.Start.Y);
            text+=yValue.toFixed(2);

            line.Text=text;
           
            this.LinePoint.push(line);
        }
    }

    this.CalculateLines=function(ptStart,ptEnd)
    {
        if (this.Frame.IsHScreen)
        {
            this.CalculateHSCreenLines(ptStart,ptEnd);
            return;
        }

        var sectionData=this.GetSectionData();
        var left=this.Frame.ChartBorder.GetLeft();
        var right=this.Frame.ChartBorder.GetRight();
        var lineHeight=ptStart.Y-ptEnd.Y;
        for(var i=0;i<sectionData.length;++i)
        {
            var yMove=lineHeight*sectionData[i];

            var line={Start:new Point(), End:new Point()};
            line.Start.Y=ptStart.Y-yMove;
            line.Start.X=left;
            line.End.Y=ptStart.Y-yMove;
            line.End.X=right;
            
            var text='';
            if (i==0) text='Base '
            else text=(sectionData[i]*100).toFixed(2)+'% ';

            var yValue=this.Frame.GetYData(line.Start.Y);
            text+=yValue.toFixed(2);

            line.Text=text;
           
            this.LinePoint.push(line);
        }
    }

    this.DrawLine=function(ptStart,ptEnd)
    {
        this.Canvas.strokeStyle=this.LineColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(ptStart.X,ptStart.Y);
        this.Canvas.lineTo(ptEnd.X,ptEnd.Y);
        this.Canvas.stroke();
    }

    this.DrawTitle=function(pt,text)
    {
        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=this.Font;

        if (this.Frame.IsHScreen)
        {
            this.Canvas.save();
            this.Canvas.translate(pt.X,pt.Y);
            this.Canvas.rotate(90 * Math.PI / 180);

            this.Canvas.fillText(text,0,0);
            this.Canvas.restore();
        }
        else
        {
            this.Canvas.fillText(text,pt.X,pt.Y);
        }    
    }
}

//百分比线
function ChartDrawPicturePercentage()
{
    this.newMethod=ChartDrawPictureGoldenSection;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPicturePercentage';

    this.GetSectionData=function()
    {
        const GOLDEN_SECTION_DATA= [0, 0.25, 0.333, 0.50, 1];
        return GOLDEN_SECTION_DATA;
    }

}

//波段线
function ChartDrawPictureWaveBand()
{
    this.newMethod=ChartDrawPictureGoldenSection;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureWaveBand';

    this.GetSectionData=function()
    {
        const GOLDEN_SECTION_DATA= [0,0.125, 0.25, 0.375, 0.50, 0.625, 0.75, 0.875,1];
        return GOLDEN_SECTION_DATA;
    }
}

//三角形
function ChartDrawPictureTriangle()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureTriangle';
    this.PointCount=3;
    this.Font=16*GetDevicePixelRatio() +"px 微软雅黑";
    this.IsPointIn=this.IsPointIn_XYValue_Line;
    this.LastPoint;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint) return;
        
        var points=drawPoint.slice(0);
        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();
        
        this.CalculateLines(points);
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
        }

        this.DrawArea(points);
        this.DrawPoint(points); //画点
        this.DrawTitle(points);

        this.Canvas.restore();
        
    }

    this.DrawArea=function(points)
    {
        if (points.length!=3) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(points[0].X,points[0].Y);
        this.Canvas.lineTo(points[1].X,points[1].Y);
        this.Canvas.lineTo(points[2].X,points[2].Y);
        this.Canvas.lineTo(points[0].X,points[0].Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    //显示角度数据
    this.DrawTitle=function(points)
    {
        if (this.Status!=10) return;   //拖拽完成以后才显示角度数据

        //输出3个点的角度
        /*
        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=this.Font;
        this.Canvas.fillText('∠60',points[0].X,points[0].Y);
        */
    }

    this.SetLastPoint=function(obj)
    {
        this.LastPoint={X:obj.X,Y:obj.Y};
    }

    this.CalculateLines=function(points)
    {
        if (this.PointStatus==2 && this.LastPoint)
        {
            var pt=new Point();
            pt.X=this.LastPoint.X;
            pt.Y=this.LastPoint.Y;
            points[2]=pt;
        }

        if (points.length===2)
        {
            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[0].Y;
            line.Start.X=points[0].X;
            line.End.Y=points[1].Y;
            line.End.X=points[1].X;
            this.LinePoint.push(line);
        }
        else if (points.length===3)
        {
            var line={Start:new Point(), End:new Point()};
            line.Start.Y=points[0].Y;
            line.Start.X=points[0].X;
            line.End.Y=points[1].Y;
            line.End.X=points[1].X;
            this.LinePoint.push(line);

            line={Start:new Point(), End:new Point()};
            line.Start.Y=points[1].Y;
            line.Start.X=points[1].X;
            line.End.Y=points[2].Y;
            line.End.X=points[2].X;
            this.LinePoint.push(line);

            line={Start:new Point(), End:new Point()};
            line.Start.Y=points[2].Y;
            line.Start.X=points[2].X;
            line.End.Y=points[0].Y;
            line.End.X=points[0].X;
            this.LinePoint.push(line);
        }
    }
}

//对称角度
function ChartDrawPictureSymmetryAngle()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureSymmetryAngle';
    this.PointCount=2;
    this.Font=16*GetDevicePixelRatio() +"px 微软雅黑";
    this.IsPointIn=this.IsPointIn_XYValue_Line;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint) return;
        
        //var points=drawPoint.slice(0);
        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();
        
        this.CalculateLines(drawPoint);
        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
        }
        this.DrawArea();
        this.DrawPoint(drawPoint); //画点
        this.DrawTitle(drawPoint);
        this.Canvas.restore();
    }

    this.CalculateLines=function(points)
    {
        if (points.length!=2) return;
        if (!this.Frame) return;

        var line={Start:new Point(), End:new Point()};
        line.Start.Y=points[0].Y;
        line.Start.X=points[0].X;
        line.End.Y=points[1].Y;
        line.End.X=points[1].X;
        this.LinePoint.push(line);

        line={Start:new Point(), End:new Point()};
        line.Start.Y=points[0].Y;
        line.Start.X=points[0].X;
        line.End.Y=points[1].Y;
        line.End.X=points[0].X;
        this.LinePoint.push(line);

        var xMove=points[0].X-points[1].X;
        line={Start:new Point(), End:new Point()};
        line.Start.Y=points[0].Y;
        line.Start.X=points[0].X;
        line.End.Y=points[1].Y;
        line.End.X=points[0].X+xMove;
        this.LinePoint.push(line);
    }

    this.DrawArea=function()
    {
        if (this.LinePoint.length!=3) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(this.LinePoint[0].Start.X,this.LinePoint[0].Start.Y);
        this.Canvas.lineTo(this.LinePoint[0].End.X,this.LinePoint[0].End.Y);
        this.Canvas.lineTo(this.LinePoint[2].End.X,this.LinePoint[2].End.Y);
        this.Canvas.moveTo(this.LinePoint[0].Start.X,this.LinePoint[0].Start.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    //显示角度数据
    this.DrawTitle=function(points)
    {
        if (this.Status!=10) return;   //拖拽完成以后才显示角度数据

        //输出点的角度
        /*
        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="bottom";
        this.Canvas.font=this.Font;
        this.Canvas.fillText('∠60',points[0].X,points[0].Y);
        */
    }
}

//圆
function ChartDrawPictureCircle()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureCircle';
    this.PointCount=2;
    this.CircleData;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint || drawPoint.length!=2) return;
        
        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        this.ClipFrame();

        var x=drawPoint[0].X-drawPoint[1].X;
        var y=drawPoint[0].Y-drawPoint[1].Y;
        var r=Math.sqrt(x*x+y*y);
        
        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.strokeStyle=this.LineColor;
        this.DrawLine(drawPoint[0],drawPoint[1]);
        this.Canvas.beginPath();
        this.Canvas.arc(drawPoint[0].X,drawPoint[0].Y,r,0,2*Math.PI);
        this.Canvas.stroke();
        this.Canvas.fill();
        this.CircleData={X:drawPoint[0].X, Y:drawPoint[0].Y, R:r};

        this.DrawPoint(drawPoint); //画点
        this.Canvas.restore();
    }

    //0-10 鼠标对应的点索引   100=鼠标在正个图形上  -1 鼠标不在图形上
    this.IsPointIn=function(x,y)
    {
        if (this.Status!=10) return -1;

        var value=this.IsPointInXYValue(x,y);
        if (value>=0) return value;

        if (this.CircleData && this.CircleData.R>8)
        {
            var triangleX=this.CircleData.X-x;
            var triangleY=this.CircleData.Y-y;
            var r=Math.sqrt(triangleX*triangleX+triangleY*triangleY);   //计算直径
            if (r<this.CircleData.R && r>this.CircleData.R-8) return 100;
        }

        return -1;
    }
}

//四边形
function ChartDrawPictureQuadrangle()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureQuadrangle';
    this.IsPointIn=this.IsPointIn_XYValue_Line;
    this.PointCount=3;
    this.LastPoint;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint({IsCheckX:true, IsCheckY:true});
        if (!drawPoint) return;

        this.AreaColor=IChartDrawPicture.ColorToRGBA(this.LineColor,0.3);
        var points=drawPoint.slice(0);
        this.CalculateLines(points);

        this.ClipFrame();

        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
        }

        this.DrawArea();
        this.DrawPoint(points);  //画点
        this.Canvas.restore(); 
    }

    this.SetLastPoint=function(obj)
    {
        this.LastPoint={X:obj.X,Y:obj.Y};
    }

    this.DrawArea=function()
    {
        if (this.LinePoint.length!=4) return;

        this.Canvas.fillStyle=this.AreaColor;
        this.Canvas.beginPath();
        this.Canvas.moveTo(this.LinePoint[0].Start.X,this.LinePoint[0].Start.Y);
        this.Canvas.lineTo(this.LinePoint[0].End.X,this.LinePoint[0].End.Y);
        this.Canvas.lineTo(this.LinePoint[1].End.X,this.LinePoint[1].End.Y);
        this.Canvas.lineTo(this.LinePoint[2].End.X,this.LinePoint[2].End.Y);
        this.Canvas.closePath();
        this.Canvas.fill();
    }

    this.CalculateLines=function(points)
    {
        if (this.PointStatus==2 && this.LastPoint)
        {
            var pt=new Point();
            pt.X=this.LastPoint.X;
            pt.Y=this.LastPoint.Y;
            points[2]=pt;
        }

        if (points.length==2)
        {
            var linePoint=this.CreateLineData(points[0],points[1]);
            this.LinePoint.push(linePoint);
        }
        else if (points.length==3)
        {
            var linePoint=this.CreateLineData(points[0],points[1]);
            this.LinePoint.push(linePoint);

            var linePoint=this.CreateLineData(points[1],points[2]);
            this.LinePoint.push(linePoint);

            //计算平行线
            var xMove=points[2].X-points[1].X;
            var yMove=points[2].Y-points[1].Y;

            var pt4=new Point();    //第4个点的坐标
            pt4.X=points[0].X+xMove;
            pt4.Y=points[0].Y+yMove;
            
            var linePoint=this.CreateLineData(points[2],pt4);
            this.LinePoint.push(linePoint);

            var linePoint=this.CreateLineData(pt4,points[0]);
            this.LinePoint.push(linePoint);
        }
    }
}

//斐波那契周期线
function ChartDrawPictureFibonacci()
{
    this.newMethod=IChartDrawPicture;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ChartDrawPictureFibonacci';
    this.PointCount=1;
    this.Font=14*GetDevicePixelRatio() +"px 微软雅黑";
    this.IsPointIn=this.IsPointIn_XYValue_Line;

    this.Draw=function()
    {
        this.LinePoint=[];
        var drawPoint=this.CalculateDrawPoint();
        if (!drawPoint) return;

        this.CalculateLines();
        if (this.LinePoint.length<=0) return;

        this.ClipFrame();

        for(var i in this.LinePoint)
        {
            var item=this.LinePoint[i];
            this.DrawLine(item.Start,item.End);
            this.DrawTitle(item.Start,item.Title);
        }

        this.DrawPoint(drawPoint);  //画点
        this.Canvas.restore(); 
    }

    this.DrawTitle=function(pt,text)
    {
        this.Canvas.fillStyle=this.LineColor;
        this.Canvas.textAlign="left";
        this.Canvas.textBaseline="top";
        this.Canvas.font=this.Font;
        if (this.Frame.IsHScreen)
        {
            this.Canvas.save();
            this.Canvas.translate(pt.X,pt.Y);
            this.Canvas.rotate(90 * Math.PI / 180);

            this.Canvas.fillText(text,2,10);
            this.Canvas.restore();
        }
        else
        {
            this.Canvas.fillText(text,pt.X+2,pt.Y+10);
        }
    }

    this.CalculateHSCreenLines=function()
    {
        var data=this.Frame.Data;
        if (!data) return;

        var xStart=null;
        if (this.Status==10)
        {
            if (this.Value.length!=1) return;
            xStart=this.Value[0].XValue;
        }
        else
        {
            if (this.Point.length!=1) return;
            xStart=parseInt(this.Frame.GetXData(this.Point[0].Y))+data.DataOffset;
        }
        
        var top=this.Frame.ChartBorder.GetRightEx();
        var bottom=this.Frame.ChartBorder.GetLeftEx();
        var showCount=this.Frame.XPointCount;
        const LINE_DATA=[1,2,3,5,8,13,21,34,55,89,144,233];
        for(var i=0;i<LINE_DATA.length;++i)
        {
            var xValue=xStart+LINE_DATA[i];
            var dataIndex=xValue-data.DataOffset;
            if (dataIndex<0 || dataIndex>=showCount) continue;

            var x=this.Frame.GetXFromIndex(xValue-data.DataOffset,false);

            var line={Start:new Point(), End:new Point(), Title:LINE_DATA[i]};
            line.Start.X=top;
            line.Start.Y=x;
            line.End.X=bottom;
            line.End.Y=x;
            this.LinePoint.push(line);
        }
    }

    this.CalculateLines=function()
    {
        if (this.Status<2) return;
        if (!this.Frame) return;
        var data=this.Frame.Data;
        if (!data) return;

        if (this.Frame.IsHScreen)
        {
            this.CalculateHSCreenLines();
            return;
        }

        var xStart=null;
        if (this.Status==10)
        {
            if (this.Value.length!=1) return;
            xStart=this.Value[0].XValue;
        }
        else
        {
            if (this.Point.length!=1) return;
            xStart=parseInt(this.Frame.GetXData(this.Point[0].X))+data.DataOffset;
        }
        
        var top=this.Frame.ChartBorder.GetTopEx();
        var bottom=this.Frame.ChartBorder.GetBottom();
        var showCount=this.Frame.XPointCount;
        const LINE_DATA=[1,2,3,5,8,13,21,34,55,89,144,233];
        for(var i=0;i<LINE_DATA.length;++i)
        {
            var xValue=xStart+LINE_DATA[i];
            var dataIndex=xValue-data.DataOffset;
            if (dataIndex<0 || dataIndex>=showCount) continue;

            var x=this.Frame.GetXFromIndex(xValue-data.DataOffset,false);

            var line={Start:new Point(), End:new Point(), Title:LINE_DATA[i]};
            line.Start.Y=top;
            line.Start.X=x;
            line.End.Y=bottom;
            line.End.X=x;
            this.LinePoint.push(line);
        }

    }
}

function ChartDrawStorage()
{
    this.DrawData=new Map();    //画图工具数据 key=symbol-Period, value=Map() Key:Guid, Value:{Guid, Symbol, Period, ClassName, Value}
    this.StorageKey;

    this.Load=function(key)     //从本地读取画图工具
    {
        if (!key) return;
        this.StorageKey=key;
        var cacheValue = localStorage[this.StorageKey];
        JSConsole.Chart.Log(`[ChartDrawStorage::Save] Load to localStorage, key=${this.StorageKey}, cache=${cacheValue}`);

        if (!cacheValue) return;
        if (typeof(cacheValue) != "string") return;

        var saveData=JSON.parse(cacheValue);
        for(var i in saveData)
        {
            var item=saveData[i];
            var drawMap=new Map();

            for(var j in item.Value)
            {
                var drawItem=item.Value[j];
                drawMap.set(drawItem.Key, drawItem.Value);    
            }

            this.DrawData.set(item.Key,drawMap);
        }
    }

    this.Save=function()        //把数据保存到本地
    {
        if (!this.StorageKey) return;
        
        var saveData=[];
        for(var stock of this.DrawData)
        {
            var key=stock[0];
            var value=stock[1];
            var itemData={ Key:key, Value:[]};

            for(var drawItem of value)
            {
                itemData.Value.push({Key:drawItem[0], Value:drawItem[1]});
            }

            saveData.push(itemData)
        }

        JSConsole.Chart.Log(`[ChartDrawStorage::Save] save to localStorage, key=${this.StorageKey}`);
        var strData=JSON.stringify(saveData);
        localStorage[this.StorageKey]=strData;
    }

    this.SaveDrawData=function(drawPicture)   //保存一个画图工具
    {
        var strKey=drawPicture.Symbol+'-'+drawPicture.Period;
        var data=drawPicture.ExportStorageData();
        if (!data) return;

        if (this.DrawData.has(strKey))  //更新
        {
            JSConsole.Chart.Log('[ChartDrawStorage::SaveDrawData] Upate: key, drawPicture, data', strKey, drawPicture,data);
            this.DrawData.get(strKey).set(data.Guid, data);
        }
        else    //新增
        {
            JSConsole.Chart.Log('[ChartDrawStorage::SaveDrawData] Insert: key, drawPicture, data', strKey, drawPicture,data);
            var newData=new Map();
            newData.set(data.Guid, data);
            this.DrawData.set(strKey,newData);
        }

        JSConsole.Chart.Log('[ChartDrawStorage::SaveDrawData] All draw data: ', this.DrawData);

        this.Save();
    }

    this.DeleteDrawData=function(drawPicture)   //删除一个画图工具
    {
        var strKey=drawPicture.Symbol+'-'+drawPicture.Period;
        if (!this.DrawData.has(strKey))  return;

        this.DrawData.delete(strKey);
        this.Save();
    }

    this.Clear=function()
    {
        this.DrawData=new Map();
        this.Save();
    }

    this.GetDrawData=function(obj) //获取已有的画图工具数据{Symbol: , Period:, }
    {
        var data=[];
        var strKey=obj.Symbol+'-'+obj.Period;
        if (!this.DrawData.has(strKey)) return data;

        var stockData=this.DrawData.get(strKey);
        for(var item of stockData)
        {
            data.push(item[1]);
        }

        return data;
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//  数据分割
//  [0]=Start起始 [1]=End结束 [2]=FixInterval修正的间隔 [3]=Increase
//
function SplitData()
{
    this.Data=[
        [0.000001,	0.000002,	0.000001,	0.0000001],
        [0.000002,	0.000004,	0.000002,	0.0000002],
        [0.000004,	0.000005,	0.000004,	0.0000001],
        [0.000005,	0.00001,	0.000005,	0.0000005],

        [0.00001,	0.00002,	0.00001,	0.000001],
        [0.00002,	0.00004,	0.00002,	0.000002],
        [0.00004,	0.00005,	0.00004,	0.000001],
        [0.00005,	0.0001,		0.00005,	0.000005],

        [0.0001,		0.0002,		0.0001,	0.00001],
        [0.0002,		0.0004,		0.0002,	0.00002],
        [0.0004,		0.0005,		0.0004,	0.00001],
        [0.0005,		0.001,		0.0005,	0.00005],

        [0.001,		0.002,		0.001,	0.0001],
        [0.002,		0.004,		0.002,	0.0002],
        [0.004,		0.005,		0.004,	0.0001],
        [0.005,		0.01,		0.005,	0.0005],

        [0.01,		0.02,		0.01,	0.001],
        [0.02,		0.04,		0.02,	0.002],
        [0.04,		0.05,		0.04,	0.001],
        [0.05,		0.1,		0.05,	0.005],

        [0.1,		0.2,		0.1,	0.01],
        [0.2,		0.4,		0.2,	0.02],
        [0.4,		0.5,		0.4,	0.01],
        [0.5,		1,			0.5,	0.05],

        [1,		2,		0.5,	0.05],
        [2,		4,		0.5,	0.05],
        [4,		5,		0.5,	0.05],
        [5,		10,		0.5,	0.05],

        [10,		12,		10,	2],
        [20,		40,		20,	5],
        [40,		50,		40,	2],
        [50,		100,	50,	10],

        [100,		200,		100,	10],
        [200,		400,		200,	20],
        [400,		500,		400,	10],
        [500,		1000,		500,	50],

        [1000,		2000,		1000,	50],
        [2000,		4000,		2000,	50],
        [4000,		5000,		4000,	50],
        [5000,		10000,		5000,	100],

        [10000,		20000,		10000,	1000],
        [20000,		40000,		10000,	1000],
        [40000,		50000,		10000,	1000],
        [50000,		100000,		10000,	1000],

        [100000,		200000,		100000,	10000],
        [200000,		400000,		100000,	10000],
        [400000,		500000,		100000,	10000],
        [500000,		1000000,	100000,	10000],

        [1000000,		2000000,		1000000,	100000],
        [2000000,		4000000,		1000000,	100000],
        [4000000,		5000000,		1000000,	100000],
        [5000000,		10000000,		1000000,	100000],

        [10000000,		20000000,		10000000,	1000000],
        [20000000,		40000000,		10000000,	1000000],
        [40000000,		50000000,		10000000,	1000000],
        [50000000,		100000000,		10000000,	1000000],

        [100000000,		200000000,		100000000,	10000000],
        [200000000,		400000000,		100000000,	10000000],
        [400000000,		500000000,		100000000,	10000000],
        [500000000,		1000000000,		100000000,	10000000],

        [1000000000,		2000000000,		1000000000,	100000000],
        [2000000000,		4000000000,		1000000000,	100000000],
        [4000000000,		5000000000,		1000000000,	100000000],
        [5000000000,		10000000000,	1000000000,	100000000]
    ];

    this.Find=function(interval)
    {
        for(var i in this.Data)
        {
            var item =this.Data[i];
            if (interval>item[0] && interval<=item[1])
            {
                var result={};
                result.FixInterval=item[2];
                result.Increase=item[3];
                return result;
            }
        }

        return null;
    }
}

function PriceSplitData()
{
    this.newMethod=SplitData;   //派生
    this.newMethod();
    delete this.newMethod;

    this.Data=[
        [0.000001,	0.000002,	0.000001,	0.0000001],
        [0.000002,	0.000004,	0.000002,	0.0000002],
        [0.000004,	0.000005,	0.000004,	0.0000001],
        [0.000005,	0.00001,	0.000005,	0.0000005],

        [0.00001,	0.00002,	0.00001,	0.000001],
        [0.00002,	0.00004,	0.00002,	0.000002],
        [0.00004,	0.00005,	0.00004,	0.000001],
        [0.00005,	0.0001,		0.00005,	0.000005],

        [0.0001,		0.0002,		0.0001,	0.00001],
        [0.0002,		0.0004,		0.0002,	0.00002],
        [0.0004,		0.0005,		0.0004,	0.00001],
        [0.0005,		0.001,		0.0005,	0.00005],

        [0.001,		0.002,		0.001,	0.0001],
        [0.002,		0.004,		0.002,	0.0001],
        [0.004,		0.005,		0.004,	0.0001],
        [0.005,		0.01,		0.005,	0.0005],

        [0.01,		0.02,		0.01,	0.001],
        [0.02,		0.04,		0.02,	0.001],
        [0.04,		0.05,		0.04,	0.001],
        [0.05,		0.1,		0.05,	0.001],

        [0.1,		0.2,		0.1,	0.01],
        [0.2,		0.4,		0.2,	0.01],
        [0.4,		0.5,		0.2,	0.01],
        [0.5,		0.8,		0.2,	0.01],
        [0.8,		1,			0.5,	0.01],

        [1,		2,		0.5,	0.05],
        [2,		4,		0.5,	0.05],
        [4,		5,		0.5,	0.05],
        [5,		10,		0.5,	0.05],

        [10,		12,		10,	2],
        [20,		40,		20,	5],
        [40,		50,		40,	2],
        [50,		100,	50,	10],

        [100,		200,		100,	10],
        [200,		400,		200,	20],
        [400,		500,		400,	10],
        [500,		1000,		500,	50],

        [1000,		2000,		1000,	50],
        [2000,		4000,		2000,	50],
        [4000,		5000,		4000,	50],
        [5000,		10000,		5000,	100],

        [10000,		20000,		10000,	1000],
        [20000,		40000,		20000,	2000],
        [40000,		50000,		40000,	1000],
        [50000,		100000,		50000,	5000],

        [100000,		200000,		100000,	10000],
        [200000,		400000,		200000,	20000],
        [400000,		500000,		400000,	10000],
        [500000,		1000000,	500000,	50000],

        [1000000,		2000000,		1000000,	100000],
        [2000000,		4000000,		2000000,	200000],
        [4000000,		5000000,		4000000,	100000],
        [5000000,		10000000,		5000000,	500000],

        [10000000,		20000000,		10000000,	1000000],
        [20000000,		40000000,		20000000,	2000000],
        [40000000,		50000000,		40000000,	1000000],
        [50000000,		100000000,		50000000,	5000000],

        [100000000,		200000000,		100000000,	10000000],
        [200000000,		400000000,		200000000,	20000000],
        [400000000,		500000000,		400000000,	10000000],
        [500000000,		1000000000,		500000000,	50000000],

        [1000000000,		2000000000,		1000000000,	100000000],
        [2000000000,		4000000000,		2000000000,	200000000],
        [4000000000,		5000000000,		4000000000,	100000000],
        [5000000000,		10000000000,	5000000000,	500000000]
    ];
}

/////////////////////////////////////////////////////////////////////////////
//   全局配置颜色
//
//
function JSChartResource()
{
    this.TooltipBGColor="rgb(255, 255, 255)"; //背景色
    this.TooltipAlpha=0.92;                  //透明度

    this.SelectRectBGColor="rgba(1,130,212,0.06)"; //背景色
 //   this.SelectRectAlpha=0.06;                  //透明度

    this.UpBarColor="rgb(238,21,21)";
    this.DownBarColor="rgb(25,158,0)";
    this.UnchagneBarColor="rgb(0,0,0)";

    this.Minute={};
    this.Minute.VolBarColor="rgb(238,127,9)";
    this.Minute.PriceColor="rgb(50,171,205)";
    this.Minute.AreaPriceColor='rgba(50,171,205,0.1)';  //价格的面积图
    this.Minute.AvPriceColor="rgb(238,127,9)";
    this.Minute.BeforeBGColor='rgba(240,240,240,0.7)';  //分钟 集合竞价背景
    this.Minute.PositionColor='rgb(218,165,32)';                  //持仓量

    this.DefaultTextColor="rgb(43,54,69)";
    this.DefaultTextFont=14*GetDevicePixelRatio() +'px 微软雅黑';
    this.TitleFont=13*GetDevicePixelRatio() +'px 微软雅黑';
    this.IndexTitleBGColor='rgb(217,219,220)';  //指标名字背景色

    this.UpTextColor="rgb(238,21,21)";
    this.DownTextColor="rgb(25,158,0)";
    this.UnchagneTextColor="rgb(0,0,0)";
    this.CloseLineColor='rgb(0,191,255)';
    this.CloseLineAreaColor=['rgba(0,191,255,0.8)','rgba(0,191,255,0.2)'];

    this.FrameBorderPen="rgb(225,236,242)";
    this.FrameSplitPen="rgb(225,236,242)";          //分割线
    this.FrameDotSplitPen='rgb(105,105,105)';       //分割虚线
    this.FrameSplitTextColor="rgb(117,125,129)";    //刻度文字颜色
    this.FrameSplitTextFont=14*GetDevicePixelRatio() +"px 微软雅黑";     //坐标刻度文字字体
    this.FrameTitleBGColor="rgb(246,251,253)";  //标题栏背景色
    this.Frame={ 
        XBottomOffset:1*GetDevicePixelRatio()  //X轴文字向下偏移
    };   

    this.FrameLatestPrice = {
        TextColor:'rgb(255,255,255)',   //最新价格文字颜色
        UpBarColor:"rgb(238,21,21)",    //上涨
        DownBarColor:"rgb(25,158,0)",   //下跌
        UnchagneBarColor:"rgb(0,0,0)",   //平盘
        BGAlpha:0.6
    };

    this.OverlayFrame={
        BolderPen:'rgb(190,190,190)',
        TitleColor:'rgb(105,105,105)',
        TitleFont:11*GetDevicePixelRatio() +'px arial',
    };

    this.CorssCursorBGColor="rgb(43,54,69)";            //十字光标背景
    this.CorssCursorTextColor="rgb(255,255,255)";
    this.CorssCursorTextFont=14*GetDevicePixelRatio() +"px 微软雅黑";
    this.CorssCursorHPenColor="rgb(130,130,130)";          //十字光标线段颜色(水平)
    this.CorssCursorVPenColor="rgb(130,130,130)";          //十字光标线段颜色(垂直)

    this.LockBGColor = "rgb(220, 220, 220)";
    this.LockTextColor = "rgb(210, 34, 34)";

    this.Domain="https://opensource.zealink.com";               //API域名
    this.CacheDomain="https://opensourcecache.zealink.com";     //缓存域名
    this.PyIndexDomain='https://py.zealink.com';                //py指标计算域名

    this.KLine={
            MaxMin: {Font:12*GetDevicePixelRatio() +'px 微软雅黑',Color:'rgb(43,54,69)'},   //K线最大最小值显示
            Info:  //信息地雷
            {
                Investor:
                {
                    ApiUrl:'https://opensource.zealink.com/API/NewsInteract', //互动易
                    IconFont: { Family:'iconfont', Text:'\ue631' , HScreenText:'\ue684', Color:'#1c65db'} //SVG 文本
                },
                Announcement:                                           //公告
                {
                    ApiUrl:'https://opensource.zealink.com/API/ReportList',
                    IconFont: { Family:'iconfont', Text:'\ue633', HScreenText:'\ue685', Color:'#f5a521' }, //SVG 文本
                    IconFont2: { Family:'iconfont', Text:'\ue634', HScreenText:'\ue686', Color:'#ed7520' } //SVG 文本 //季报
                },
                Pforecast:  //业绩预告
                {
                    ApiUrl:'https://opensource.zealink.com/API/StockHistoryDay',
                    IconFont: { Family:'iconfont', Text:'\ue62e', HScreenText:'\ue687', Color:'#986cad' } //SVG 文本
                },
                Research:   //调研
                {
                    ApiUrl:'https://opensource.zealink.com/API/InvestorRelationsList',
                    IconFont: { Family:'iconfont', Text:'\ue632', HScreenText:'\ue688', Color:'#19b1b7' } //SVG 文本
                },
                BlockTrading:   //大宗交易
                {
                    ApiUrl:'https://opensource.zealink.com/API/StockHistoryDay',
                    IconFont: { Family:'iconfont', Text:'\ue630', HScreenText:'\ue689', Color:'#f39f7c' } //SVG 文本
                },
                TradeDetail:    //龙虎榜
                {
                    ApiUrl:'https://opensource.zealink.com/API/StockHistoryDay',
                    IconFont: { Family:'iconfont', Text:'\ue62f', HScreenText:'\ue68a' ,Color:'#b22626' } //SVG 文本
                }

            },
            NumIcon:
            {
                Color:'rgb(251,80,80)',Family:'iconfont',
                Text:[  '\ue649',
                        '\ue63b','\ue640','\ue63d','\ue63f','\ue645','\ue641','\ue647','\ue648','\ue646','\ue636',
                        '\ue635','\ue637','\ue638','\ue639','\ue63a','\ue63c','\ue63e','\ue642','\ue644','\ue643'
                    ]
            },
            TradeIcon:  //交易指标 图标
            {
                Family:'iconfont', 
                Buy: { Color:'rgb(255,15,4)', Text:'\ue683', HScreenText:'\ue682'}, 
                Sell: { Color:'rgb(64,122,22)', Text:'\ue681',HScreenText:'\ue680'},
            }
        };

    this.Index={};
    //指标线段颜色
    this.Index.LineColor=
    [
        "rgb(255,174,0)",
        "rgb(25,199,255)",
        "rgb(175,95,162)",
        "rgb(236,105,65)",
        "rgb(68,114,196)",
        "rgb(229,0,79)",
        "rgb(0,128,255)",
        "rgb(252,96,154)",
        "rgb(42,230,215)",
        "rgb(24,71,178)",

    ];

    this.OverlaySymbol={ Random:0 };    //Random 颜色的随机数
    this.OverlaySymbol.Color=   //叠加股票颜色
    [
        "rgb(38,198,218)",
        "rgb(103,58,183)",
        "rgb(0,191,165)",
        "rgb(130,177,255)",
    ];

    //历史数据api
    this.Index.StockHistoryDayApiUrl="https://opensource.zealink.com/API/StockHistoryDay";
    //市场多空
    this.Index.MarketLongShortApiUrl="https://opensource.zealink.com/API/FactorTiming";
    //市场关注度
    this.Index.MarketAttentionApiUrl="https://opensource.zealink.com/API/MarketAttention";
    //行业,指数热度
    this.Index.MarketHeatApiUrl="https://opensource.zealink.com/API/MarketHeat";
    //自定义指数热度
    this.Index.CustomIndexHeatApiUrl="https://opensource.zealink.com/API/QuadrantCalculate";

    //指标不支持信息
    this.Index.NotSupport={Font:"14px 微软雅黑", TextColor:"rgb(52,52,52)"};

    //画图工具
    this.DrawPicture={};
    this.DrawPicture.LineColor=
    [
        "rgb(30,144,255)",
    ];

    this.DrawPicture.PointColor=
    [
        "rgb(105,105,105)",
    ];

    this.KLineTrain = {
        Font:'bold 14px arial',
        LastDataIcon: {Color:'rgb(0,0,205)',Text:'⬇'},
        BuyIcon: {Color:'rgb(0,205,102 )',Text:'B'},
        SellIcon: {Color:'rgb(255,127,36 )',Text:'S'},

        IconFont:
        {
            Family:'iconfont', 
            Buy:{ Text:'\ue64a', HScreenText:'\ue68a' ,Color:'rgb(255,140,0)' },
            Sell:{ Text:'\ue64b', HScreenText:'\ue68a' ,Color:'rgb(6,79,18)' },
            Last:{ Text:'\ue681', HScreenText:'\ue68a' ,Color:'rgb(55,0,255)' },
        }
    };

    //手机端tooltip
    this.TooltipPaint = {
        BGColor:'rgba(250,250,250,0.8)',    //背景色
        BorderColor:'rgb(120,120,120)',     //边框颜色
        TitleColor:'rgb(120,120,120)',       //标题颜色
        TitleFont:13*GetDevicePixelRatio() +'px 微软雅黑'   //字体
    };

    //弹幕
    this.Barrage= {
        Font:16*GetDevicePixelRatio() +'px 微软雅黑',   //字体
        Height:20,
        Color:'RGB(109,109,109)'
    }

    //走势图 信息地雷
    this.MinuteInfo={
        TextColor: 'rgb(84,143,255)',
        Font: 14*GetDevicePixelRatio() +'px 微软雅黑',
        PointColor:'rgb(38,113,254)',
        LineColor:'rgb(120,167,255)',
        TextBGColor:'rgba(255,255,255,0.8)'
    }

    //自定义风格
    this.SetStyle=function(style)
    {
        if (style.TooltipBGColor) this.TooltipBGColor = style.TooltipBGColor;
        if (style.TooltipAlpha) this.TooltipAlpha = style.TooltipAlpha;
        if (style.SelectRectBGColor) this.SelectRectBGColor = style.SelectRectBGColor;
        if (style.UpBarColor) this.UpBarColor = style.UpBarColor;
        if (style.DownBarColor) this.DownBarColor = style.DownBarColor;
        if (style.UnchagneBarColor) this.UnchagneBarColor = style.UnchagneBarColor;
        if (style.Minute) 
        {
            if (style.Minute.VolBarColor) this.Minute.VolBarColor = style.Minute.VolBarColor;
            if (style.Minute.PriceColor) this.Minute.PriceColor = style.Minute.PriceColor;
            if (style.Minute.AvPriceColor) this.Minute.AvPriceColor = style.Minute.AvPriceColor;
            if (style.Minute.AreaPriceColor) this.Minute.AreaPriceColor = style.Minute.AreaPriceColor;
            if (style.Minute.PositionColor) this.Minute.PositionColor = style.Minute.PositionColor;
        }

        if (style.DefaultTextColor) this.DefaultTextColor = style.DefaultTextColor;
        if (style.DefaultTextFont) this.DefaultTextFont = style.DefaultTextFont;
        if (style.TitleFont) this.TitleFont = style.TitleFont;
        if (style.UpTextColor) this.UpTextColor = style.UpTextColor;
        if (style.DownTextColor) this.DownTextColor = style.DownTextColor;
        if (style.UnchagneTextColor) this.UnchagneTextColor = style.UnchagneTextColor;
        if (style.CloseLineColor) this.CloseLineColor = style.CloseLineColor;
        if (style.CloseLineAreaColor) this.CloseLineAreaColor = style.CloseLineAreaColor;
        if (style.FrameBorderPen) this.FrameBorderPen = style.FrameBorderPen;
        if (style.FrameSplitPen) this.FrameSplitPen = style.FrameSplitPen;
        if (style.FrameDotSplitPen) this.FrameDotSplitPen = style.FrameDotSplitPen;
        if (style.FrameSplitTextColor) this.FrameSplitTextColor = style.FrameSplitTextColor;
        if (style.FrameSplitTextFont) this.FrameSplitTextFont = style.FrameSplitTextFont;
        if (style.FrameTitleBGColor) this.FrameTitleBGColor = style.FrameTitleBGColor;

        if (style.Frame)
        {
            if (style.Frame.XBottomOffset) this.Frame.XBottomOffset=style.Frame.XBottomOffset;
        }

        if (style.FrameLatestPrice) 
        {
            if (style.FrameLatestPrice.TextColor) this.FrameLatestPrice.TextColor = style.FrameLatestPrice.TextColor;
            if (style.FrameLatestPrice.UpBarColor) this.FrameLatestPrice.UpBarColor = style.FrameLatestPrice.UpBarColor;
            if (style.FrameLatestPrice.DownBarColor) this.FrameLatestPrice.DownBarColor = style.FrameLatestPrice.DownBarColor;
            if (style.FrameLatestPrice.UnchagneBarColor) this.FrameLatestPrice.UnchagneBarColor = style.FrameLatestPrice.UnchagneBarColor;
            if (style.FrameLatestPrice.BGAlpha) this.FrameLatestPrice.BGAlpha = style.FrameLatestPrice.BGAlpha;
        }

        if (style.CorssCursorBGColor) this.CorssCursorBGColor = style.CorssCursorBGColor;
        if (style.CorssCursorTextColor) this.CorssCursorTextColor = style.CorssCursorTextColor;
        if (style.CorssCursorTextFont) this.CorssCursorTextFont = style.CorssCursorTextFont;
        if (style.CorssCursorVPenColor) this.CorssCursorVPenColor = style.CorssCursorVPenColor;
        if (style.CorssCursorHPenColor) this.CorssCursorHPenColor = style.CorssCursorHPenColor;
        if (style.KLine) this.KLine = style.KLine;

        if (style.Index) 
        {
            if (style.Index.LineColor) this.Index.LineColor = style.Index.LineColor;
            if (style.Index.NotSupport) this.Index.NotSupport = style.Index.NotSupport;
        }
        
        if (style.ColorArray) this.ColorArray = style.ColorArray;

        if (style.DrawPicture)
        {
            this.DrawPicture.LineColor = style.DrawPicture.LineColor;
            this.DrawPicture.PointColor = style.DrawPicture.PointColor;
        }

        if (style.TooltipPaint)
        {
            if (style.TooltipPaint.BGColor) this.TooltipPaint.BGColor=style.TooltipPaint.BGColor;
            if (style.TooltipPaint.BorderColor) this.TooltipPaint.BorderColor=style.TooltipPaint.BorderColor;
            if (style.TooltipPaint.TitleColor) this.TooltipPaint.TitleColor=style.TooltipPaint.TitleColor;
            if (style.TooltipPaint.TitleFont) this.TooltipPaint.TitleFont=style.TooltipPaint.TitleFont;
        }

        if (style.MinuteInfo)
        {
            if (style.MinuteInfo.TextColor) this.MinuteInfo.TextColor=style.MinuteInfo.TextColor;
            if (style.MinuteInfo.Font) this.MinuteInfo.Font=style.MinuteInfo.Font;
            if (style.MinuteInfo.PointColor) this.MinuteInfo.PointColor=style.MinuteInfo.PointColor;
            if (style.MinuteInfo.LineColor) this.MinuteInfo.LineColor=style.MinuteInfo.LineColor;
            if (style.MinuteInfo.TextBGColor) this.MinuteInfo.TextBGColor=style.MinuteInfo.TextBGColor;
        }
    }
}

var g_JSChartResource=new JSChartResource();


/////////////////////////////////////////////////////////////////////////////////
//
//
//
var JSCHART_LANGUAGE_ID=
{
    LANGUAGE_CHINESE_ID:0,
    LANGUAGE_ENGLISH_ID:1
};

function JSChartLocalization()
{
    this.TextResource=new Map([
        //内部tooltip
        ['Tooltip-Open', {CN:'开:', EN:'O:'}],
        ['Tooltip-High', {CN:'高:', EN:'H:'}],
        ['Tooltip-Low', {CN:'低:', EN:'L:'}],
        ['Tooltip-Close', {CN:'收:', EN:'C:'}],
        ['Tooltip-Increase', {CN:'幅:', EN:'I:'}],
        ['Tooltip-Vol', {CN:'量:', EN:'V:'}],
        ['Tooltip-Amount', {CN:'额:', EN:'A:'}],
        ['Tooltip-AvPrice', {CN:'均:', EN:'AP:'}],
        ['Tooltip-Price', {CN:'价:', EN:'P:'}],
        ['Tooltip-Exchange', {CN:'换:', EN:'E:'}],
        ['Tooltip-Position',{CN:'持:', EN:'P:'}],

        ['DivTooltip-Open', {CN:'开盘:', EN:'Open:'}],
        ['DivTooltip-High', {CN:'最高:', EN:'High:'}],
        ['DivTooltip-Low', {CN:'最低:', EN:'Low:'}],
        ['DivTooltip-Close', {CN:'收盘:', EN:'Close:'}],
        ['DivTooltip-Increase', {CN:'涨幅:', EN:'Increase:'}],
        ['DivTooltip-Vol', {CN:'数量:', EN:'Volume:'}],
        ['DivTooltip-Amount', {CN:'金额:', EN:'Amount:'}],
        ['DivTooltip-Exchange', {CN:'换手:', EN:'Exchange:'}],

        //K线动态标题
        ['KTitle-Open', {CN:'开:', EN:'O:'}],
        ['KTitle-High', {CN:'高:', EN:'H:'}],
        ['KTitle-Low', {CN:'低:', EN:'L:'}],
        ['KTitle-Close', {CN:'收:', EN:'C:'}],
        ['KTitle-Increase', {CN:'幅:', EN:'I:'}],
        ['KTitle-Vol', {CN:'量:', EN:'V:'}],
        ['KTitle-Amount', {CN:'额:', EN:'A:'}],
        ['KTitle-Exchange', {CN:'换:', EN:'E:'}],

        //走势图动态标题
        ['MTitle-Close', {CN:'价:', EN:'C:'}],
        ['MTitle-AvPrice', {CN:'均:', EN:'AC:'}],
        ['MTitle-Increase', {CN:'幅:', EN:'I:'}],
        ['MTitle-Vol', {CN:'量:', EN:'V:'}],
        ['MTitle-Amount', {CN:'额:', EN:'A:'}],

        //周期
        ['日线', {CN:'日线', EN:'1D'}],
        ['周线', {CN:'周线', EN:'1W'}],
        ['双周', {CN:'双周', EN:"2W"}],
        ['月线', {CN:'月线', EN:'1M'}],
        ['年线', {CN:'年线', EN:'1Y'}],
        ['1分', {CN:'1分', EN:'1Min'}],
        ['5分', {CN:'5分', EN:'5Min'}],
        ['15分', {CN:'15分', EN:'15Min'}],
        ['30分', {CN:'30', EN:'30Min'}],
        ['60分', {CN:'60分', EN:'60Min'}],
        ['季线', {CN:'季线', EN:'1Q'}],
        ['分笔', {CN:'分笔', EN:'Tick'}],
        ['2小时', {CN:'2小时', EN:'2H'}],
        ['4小时', {CN:'4小时', EN:'4H'}],

        //复权
        ['不复权', {CN:'不复权', EN:'No Right'}],
        ['前复权', {CN:'前复权', EN:'Pro Right'}],
        ['后复权', {CN:'后复权', EN:'Post Right'}],

        //week
        ['日', {CN:'日', EN:'Sun.'}],
        ['一', {CN:'一', EN:'Mon.'}],
        ['二', {CN:'二', EN:'Tues.'}],
        ['三', {CN:'三', EN:'Wed.'}],
        ['四', {CN:'四', EN:'Thur.'}],
        ['五', {CN:'五', EN:'Fri.'}],
        ['六', {CN:'六', EN:'Sat.'}],

        ['1月', {CN:'1月', EN:'Jan'}],
        ['2月', {CN:'2月', EN:'Feb'}],
        ['3月', {CN:'3月', EN:'Mar'}],
        ['4月', {CN:'4月', EN:'Apr'}],
        ['5月', {CN:'5月', EN:'May'}],
        ['6月', {CN:'6月', EN:'Jun'}],
        ['7月', {CN:'7月', EN:'Jul'}],
        ['8月', {CN:'8月', EN:'Aug'}],
        ['9月', {CN:'9月', EN:'Sept'}],
        ['10月', {CN:'10月', EN:'Oct'}],
        ['11月', {CN:'11月', EN:'Nov'}],
        ['12月', {CN:'12月', EN:'Dec'}],

        ['自定义分钟', {CN:'分', EN:'Min'}],
        ['自定义日线', {CN:'日', EN:'D'}],
        ['自定义秒', {CN:'秒', EN:'S'}]

    ]);

    this.GetText=function(key,language)
    {
        var item=this.TextResource.get(key);
        if (!item) return '';

        switch(language)
        {
            case JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID:
                return item.CN;
            case JSCHART_LANGUAGE_ID.LANGUAGE_ENGLISH_ID:
                return item.EN;
            default:
                return item.CN;
        }
    }

    this.SetTextResource=function(key,value)
    {
        this.TextResource.set(key,value)
    }
};

var g_JSChartLocalization=new JSChartLocalization();



/*
    指标列表 指标信息都在这里,不够后面再加字段
*/
function JSIndexMap()
{

}

JSIndexMap.Get=function(id)
{
    var indexMap=new Map(
    [
        //公司自己的指标
        ["市场多空",    {IsMainIndex:false,  Create:function(){ return new MarketLongShortIndex()}  }],
        ["市场择时",    {IsMainIndex:false,  Create:function(){ return new MarketTimingIndex()}  }],
        ["市场关注度",  {IsMainIndex:false,  Create:function(){ return new MarketAttentionIndex()}  }],
        ["指数热度",    {IsMainIndex:false,  Create:function(){ return new MarketHeatIndex()}  }],
        ["财务粉饰",    {IsMainIndex:false,  Create:function(){ return new BenfordIndex()}  }],

        ["自定义指数热度", {IsMainIndex:false,  Create:function(){ return new CustonIndexHeatIndex()} , Name:'自定义指数热度'} ],   //废弃

        //能图指标
        ["能图-趋势",       {IsMainIndex:false,  Create:function(){ return new LighterIndex1()},   Name:'大盘/个股趋势'  }],    //废弃
        ["能图-位置研判",   {IsMainIndex:false,  Create:function(){ return new LighterIndex2()},   Name:'位置研判'  }], //废弃
        ["能图-点位研判",   {IsMainIndex:false,  Create:function(){ return new LighterIndex3()},   Name:'点位研判'  }]  //废弃
    ]
    );

    return indexMap.get(id);
}

////////////////////////////////////////////////////////////////////////////////////////////////
//      指标计算方法
//
//
//

function HQIndexFormula()
{

}

//指数平均数指标 EMA(close,10)
HQIndexFormula.EMA=function(data,dayCount)
{
    var result = [];

    var offset=0;
    if (offset>=data.length) return result;

    //取首个有效数据
    for(;offset<data.length;++offset)
    {
        if (data[offset]!=null && !isNaN(data[offset]))
            break;
    }

    var p1Index=offset;
    var p2Index=offset+1;

    result[p1Index]=data[p1Index];
    for(var i=offset+1;i<data.length;++i,++p1Index,++p2Index)
    {
        result[p2Index]=((2*data[p2Index]+(dayCount-1)*result[p1Index]))/(dayCount+1);
    }

    return result;
}

HQIndexFormula.SMA=function(data,n,m)
{
    var result = [];

    var i=0;
    var lastData=null;
    for(;i<data.length; ++i)
    {
        if (data[i]==null || isNaN(data[i])) continue;
        lastData=data[i];
        result[i]=lastData; //第一天的数据
        break;
    }

    for(++i;i<data.length;++i)
    {
        result[i]=(m*data[i]+(n-m)*lastData)/n;
        lastData=result[i];
    }

    return result;
}


/*
    求动态移动平均.
    用法: DMA(X,A),求X的动态移动平均.
    算法: 若Y=DMA(X,A)则 Y=A*X+(1-A)*Y',其中Y'表示上一周期Y值,A必须小于1.
    例如:DMA(CLOSE,VOL/CAPITAL)表示求以换手率作平滑因子的平均价
*/
HQIndexFormula.DMA=function(data,data2)
{
    var result = [];
    if (data.length<0 || data.length!=data2.length) return result;

    var index=0;
    for(;index<data.length;++index)
    {
        if (data[index]!=null && !isNaN(data[index]) && data2[index]!=null && !isNaN(data2[index]))
        {
            result[index]=data[index];
            break;
        }
    }

    for(index=index+1;index<data.length;++index)
    {
        if (data[index]==null || data2[index]==null)
            result[index]=null;
        else
        {
            if (data[index]<1)
                result[index]=(data2[index]*data[index])+(1-data2[index])*result[index-1];
            else
                result[index]= data[index];
        }
    }

    return result;
}


HQIndexFormula.HHV=function(data,n)
{
    var result = [];
    if (n>data.length) return result;

    var max=-10000;
    for(var i=n,j=0;i<data.length;++i,++j)
    {
        if(i<n+max)
        {
            max=data[i]<data[max]?max:i;
        }
        else
        {
            for(j=(max=i-n+1)+1;j<=i;++j)
            {
                if(data[j]>data[max])
                    max = j;
            }
        }

        result[i] = data[max];
    }

    return result;
}

HQIndexFormula.LLV=function(data,n)
{
    var result = [];
    if (n>data.length) return result;

    var min=-10000;

    for(var i=n;i<data.length;++i,++j)
    {
        if(i<n+min)
        {
            min=data[i]>data[min]?min:i;
        }
        else
        {
            for(var j=(min=i-n+1)+1;j<=i;++j)
            {
                if(data[j]<data[min])
                    min = j;
            }
        }
        result[i] = data[min];
    }

    return result;
}

HQIndexFormula.REF=function(data,n)
{
    var result=[];

    if (data.length<=0) return result;
    if (n>=data.length) return result;

    result=data.slice(0,data.length-n);

    for(var i=0;i<n;++i)
        result.unshift(null);

    return result;
}

HQIndexFormula.REFDATE=function(data,n)
{
    var result=[];

    if (data.length<=0) return result;

    //暂时写死取最后一个
    n=data.length-1;
    for(var i in data)
    {
        result[i]=data[n];
    }

    return result;
}



HQIndexFormula.SUM=function(data,n)
{
    var result=[];

    if (n==0)
    {
        result[0]=data[0];

        for (var i=1; i<data.length; ++i)
        {
            result[i] = result[i-1]+data[i];
        }
    }
    else
    {

        for(var i=n-1,j=0;i<data.length;++i,++j)
        {
            for(var k=0;k<n;++k)
            {
                if (k==0) result[i]=data[k+j];
                else result[i]+=data[k+j];
            }
        }
    }

    return result;
}

//两个数组相减
HQIndexFormula.ARRAY_SUBTRACT=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            if (data[i]==null || isNaN(data[i]))
                result[i]=null;
            else
                result[i]=data[i]-data2;
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
            {
                if (data[i]==null || data2[i]==null) result[i]=null;
                else result[i]=data[i]-data2[i];
            }
            else
                result[i]=null;
        }
    }

    return result;
}

//数组 data>data2比较 返回 0/1 数组
HQIndexFormula.ARRAY_GT=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i]>data2 ? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
                result[i]=data[i]>data2[i] ? 1:0;
            else
                result[i]=null;
        }
    }

    return result;
}

//数组 data>=data2比较 返回 0/1 数组
HQIndexFormula.ARRAY_GTE=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i]>=data2 ? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
                result[i]=data[i]>=data2[i] ? 1:0;
            else
                result[i]=null;
        }
    }

    return result;
}

//数组 data<data2比较 返回 0/1 数组
HQIndexFormula.ARRAY_LT=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i]<data2 ? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
                result[i]=data[i]<data2[i] ? 1:0;
            else
                result[i]=null;
        }
    }

    return result;
}

//数组 data<=data2比较 返回 0/1 数组
HQIndexFormula.ARRAY_LTE=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i]<=data2 ? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
                result[i]=data[i]<=data2[i] ? 1:0;
            else
                result[i]=null;
        }
    }

    return result;
}

//数组 data==data2比较 返回 0/1 数组
HQIndexFormula.ARRAY_EQ=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i]==data2 ? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
                result[i]=(data[i]==data2[i] ? 1:0);
            else
                result[i]=null;
        }
    }

    return result;
}

HQIndexFormula.ARRAY_IF=function(data,trueData,falseData)
{
    var result=[];
    var IsNumber=[typeof(trueData)=="number",typeof(falseData)=="number"];
    for(var i in data)
    {
        if (data[i])
        {
            if (IsNumber[0]) result[i]=trueData;
            else result[i]=trueData[i];
        }
        else
        {
            if (IsNumber[1]) result[i]=falseData;
            else result[i]=falseData[i];
        }
    }

    return result;
}

HQIndexFormula.ARRAY_AND=function(data,data2)
{
   var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i] && data2? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
                result[i]=(data[i] && data2[i] ? 1:0);
            else
                result[i]=0;
        }
    }

    return result;
}
HQIndexFormula.ARRAY_OR=function(data, data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=(data[i] || data2? 1:0);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length)

        for(var i=0;i<count;++i)
        {
            if (i < data.length && data[i])
            {
                result[i] = 1;
                continue;
            }
            if (i < data2.length && data2[i])
            {
                result[i] = 1;
                continue;
            }
            result[i] = 0;    
        }
    }

    return result;
}
//数组相乘
//支持多个参数累乘 如:HQIndexFormula.ARRAY_MULTIPLY(data,data2,data3,data3) =data*data2*data3*data4
HQIndexFormula.ARRAY_MULTIPLY=function(data,data2)
{
    if (arguments.length==2)
    {
        var result=[];
        var IsNumber=typeof(data2)=="number";
        if (IsNumber)
        {
            for(var i in data)
            {
                if (data[i]==null || isNaN(data[i]))
                    result[i]=null;
                else
                    result[i]=data[i]*data2;
            }
        }
        else
        {
            var count=Math.max(data.length,data2.length);
            for(var i=0;i<count;++i)
            {
                if (i<data.length && i<data2.length)
                    result[i]=data[i]*data2[i];
                else
                    result[i]=null;
            }
        }

        return result;
    }

    var result=HQIndexFormula.ARRAY_MULTIPLY(arguments[0],arguments[1]);

    for(var i=2;i<arguments.length;++i)
    {
        result=HQIndexFormula.ARRAY_MULTIPLY(result,arguments[i]);
    }

    return result;
}

//数组相除
HQIndexFormula.ARRAY_DIVIDE=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            result[i]=data[i]/data2;
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length);
        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
            {
                if(data[i]==null || data2[i]==null || isNaN(data[i]) || isNaN(data2[i]))
                    result[i]=null;
                else if (data2[i]==0)
                    result[i]=null;
                else
                    result[i]=data[i]/data2[i];
            }
            else
                result[i]=null;
        }
    }

    return result;
}

//数组相加
//支持多个参数累加 如:HQIndexFormula.ARRAY_ADD(data,data2,data3,data3) =data+data2+data3+data4
HQIndexFormula.ARRAY_ADD=function(data,data2)
{
    if (arguments.length==2)
    {
        var result=[];
        var IsNumber=typeof(data2)=="number";
        if (IsNumber)
        {
            for(var i in data)
            {
                result[i]=data[i]+data2;
            }
        }
        else
        {
            var count=Math.max(data.length,data2.length);
            for(var i=0;i<count;++i)
            {
                if (i<data.length && i<data2.length)
                {
                    if (data[i]==null || data2[i]==null || isNaN(data[i]) || isNaN(data2[i])) result[i]=null
                    else result[i]=data[i]+data2[i];
                }
                else
                {
                    result[i]=null;
                }
            }
        }

        return result;
    }

    var result=HQIndexFormula.ARRAY_ADD(arguments[0],arguments[1]);

    for(var i=2;i<arguments.length;++i)
    {
        result=HQIndexFormula.ARRAY_ADD(result,arguments[i]);
    }

    return result;
}

HQIndexFormula.MAX=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            if (data[i]==null) result[i]=null;
            else result[i]=Math.max(data[i],data2);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length);
        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
            {
                if (data[i]==null || data2[i]==null) result[i]=null;
                else result[i]=Math.max(data[i],data2[i]);
            }
            else
                result[i]=null;
        }
    }

    return result;
}

HQIndexFormula.MIN=function(data,data2)
{
    var result=[];
    var IsNumber=typeof(data2)=="number";
    if (IsNumber)
    {
        for(var i in data)
        {
            if (data[i]==null) result[i]=null;
            else result[i]=Math.min(data[i],data2);
        }
    }
    else
    {
        var count=Math.max(data.length,data2.length);
        for(var i=0;i<count;++i)
        {
            if (i<data.length && i<data2.length)
            {
                if (data[i]==null || data2[i]==null) result[i]=null;
                else result[i]=Math.min(data[i],data2[i]);
            }
            else
                result[i]=null;
        }
    }

    return result;
}


HQIndexFormula.ABS=function(data)
{
    var result=[];
    for(var i in data)
    {
        if (data[i]==null) result[i]=null;
        else result[i]=Math.abs(data[i]);
    }

    return result;
}


HQIndexFormula.MA=function(data,dayCount)
{
    var result=[];

    for (var i = 0, len = data.length; i < len; i++)
    {
        if (i < dayCount)
        {
            result[i]=null;
            continue;
        }

        var sum = 0;
        for (var j = 0; j < dayCount; j++)
        {
            sum += data[i - j];
        }
        result[i]=sum / dayCount;
    }
    return result;
}

/*
    加权移动平均
    返回加权移动平均
    用法:EXPMA(X,M):X的M日加权移动平均
    EXPMA[i]=buffer[i]*para+(1-para)*EXPMA[i-1] para=2/(1+__para)
*/
HQIndexFormula.EXPMA=function(data,dayCount)
{
    var result=[];
    if (dayCount>=data.length) return result;

    var i=dayCount;
    for(;i<data.length;++i) //获取第1个有效数据
    {
        if (data[i]!=null)
        {
            result[i]=data[i];
            break;
        }
    }

    for (i=i+1; i < data.length; ++i)
    {
        if (result[i-1]!=null && data[i]!=null)
            result[i]=(2*data[i]+(dayCount-1)*result[i-1])/(dayCount+1);
        else if (result[i-1]!=null)
            result[i]=result[i-1];
    }

    return result;
}

//加权平滑平均,MEMA[i]=SMA[i]*para+(1-para)*SMA[i-1] para=2/(1+__para)
HQIndexFormula.EXPMEMA=function(data,dayCount)
{
    var result=[];
    if (dayCount>=data.length) return result;

    var index=0;
    for(;index<data.length;++index)
    {
        if (data[index] && !isNaN(data[index])) break;
    }

    var sum=0;
    for(var i=0; index<data.length && i<dayCount;++i, ++index)
    {
        if (data[index] && !isNaN(data[index]))
            sum+=data[index];
        else
            sum+=data[index-1];
    }

    result[index-1]=sum/dayCount;
    for(;index<data.length;++index)
	{
        if(result[index-1]!=null && data[index]!=null)
            result[index]=(2*data[index]+(dayCount-1)*result[index-1])/(dayCount+1);
        else if(result[index-1]!=null)
            result[index] = result[index-1];
	}

    return result;
}


HQIndexFormula.STD=function(data,n)
{
    var result=[];

    var total=0;
    var averageData=[]; //平均值
    for(var i=n-1;i<data.length;++i)
    {
        total=0;
        for(var j=0;j<n;++j)
        {
            total+=data[i-j];
        }

        averageData[i]=total/n;
    }

    for(var i=n-1;i<data.length;++i)
    {
        total=0;
        for(var j=0;j<n;++j)
        {
            total+=Math.pow((data[i-j]-averageData[i]),2);
        }

        result[i]=Math.sqrt(total/n);
    }


    return result;
}

//平均绝对方差
HQIndexFormula.AVEDEV=function(data,n)
{
    var result=[];

    var total=0;
    var averageData=[]; //平均值
    for(var i=n-1;i<data.length;++i)
    {
        total=0;
        for(var j=0;j<n;++j)
        {
            total+=data[i-j];
        }

        averageData[i]=total/n;
    }

    for(var i=n-1;i<data.length;++i)
    {
        total=0;
        for(var j=0;j<n;++j)
        {
            total+=Math.abs(data[i-j]-averageData[i]);
        }

        result[i]=total/n;
    }


    return result;
}

HQIndexFormula.COUNT=function(data,n)
{
    var result=[];


    for(var i=n-1;i<data.length;++i)
    {
        var count=0;
        for(var j=0;j<n;++j)
        {
            if (data[i-j]) ++count;
        }

        result[i]=count;
    }

    return result;
}

//上穿
HQIndexFormula.CROSS=function(data,data2)
{
    var result=[];
    if (data.length!=data2.length) return result=[];

    var index=0;
    for(;index<data.length;++index)
    {
        if (data[index]!=null && !isNaN(data[index])  && data2[index]!=null && isNaN(data2[index]))
            break;
    }

    for(++index;index<data.length;++index)
    {
        result[index]= (data[index]>data2[index]&&data[index-1]<data2[index-1])?1:0;
    }

    return result;
}

//累乘
HQIndexFormula.MULAR=function(data,n)
{
    var result=[];
    if(data.length<n) return result;

    var index=n;
    for(;index<data.length;++index)
    {
        if (data[index]!=null && !isNaN(data[index]))
        {
            result[index]=data[index];
            break;
        }
    }

    for(++index;index<data.length;++index)
    {
        result[index]=result[index-1]*data[index];
    }

    return result;
}


HQIndexFormula.STICKLINE=function(data,price1,price2)
{
    var result=[];
    if(data.length<=0) return result;

    var IsNumber=typeof(price1)=="number";
    var IsNumber2=typeof(price2)=="number";
   

    for(var i in data)
    {
        result[i]=null;
        if (isNaN(data[i])) continue;
        if (!data[i]) continue;

        if (IsNumber && IsNumber2)
        {
            result[i]={Value:price1,Value2:price2};
        }
        else if (IsNumber && !IsNumber2)
        {
            if (isNaN(price2[i])) continue;
            result[i]={Value:price1,Value2:price2[i]};
        }
        else if (!IsNumber && IsNumber2)
        {
            if (isNaN(price1[i])) continue;
            result[i]={Value:price1[i],Value2:price2};
        }
        else
        {
            if (isNaN(price1[i]) || isNaN(price2[i])) continue;
            result[i]={Value:price1[i],Value2:price2[i]};
        }
    }

    return result;
}

/////////////////////////////////////////////////////////////////////////////////////////////
//  K线图 控件
//  this.ChartPaint[0] K线画法 这个不要修改
//
//
function KLineChartContainer(uielement)
{
    var _self =this;
    this.newMethod=JSChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.ClassName='KLineChartContainer';
    this.WindowIndex=new Array();
    this.ColorIndex;                    //五彩K线
    this.TradeIndex;                    //交易指标/专家系统
    this.Symbol;
    this.Name;
    this.Period=0;                      //周期 0=日线 1=周线 2=月线 3=年线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 9=季线 10=分笔线 11=120分钟 12=240分钟
    this.IsApiPeriod=false;             //使用API计算周期
    this.Right=0;                       //复权 0 不复权 1 前复权 2 后复权
    this.SourceData;                    //原始的历史数据
    this.MaxReqeustDataCount=3000;      //数据个数
    this.MaxRequestMinuteDayCount=5;    //分钟数据请求的天数
    this.PageSize=200;                  //每页数据个数
    this.KLineDrawType=0;
    this.ScriptErrorCallback;           //脚本执行错误回调
    this.FlowCapitalReady=false;        //流通股本是否下载完成
    this.ChartDrawStorage=new ChartDrawStorage();
    this.ChartDrawStorageCache=null;    //首次需要创建的画图工具数据
    this.RightSpaceCount=0;             //右侧空白个数
    this.SourceDataLimit=new Map();     //每个周期缓存数据最大个数 key=周期 value=最大个数    

    this.CustomShow=null;               //首先显示的K线的起始日期 { Date:日期 PageSize:}
    this.OverlayIndexFrameWidth=60;     //叠加指标框架宽度

    this.Page= { 
        Day:{ Enable:false, Index:0, Finish:false },    //日线
        Minute:{ Enable:false, Index:0, Finish:false }  //分钟
    };  //分页下载 Enable:是否分页下载 Index:已下载到第几页 Finish:是否所有分页完成  

    this.DragDownload= { 
        Day:{ Enable:false, IsEnd:false, Status:0 },      //日线数据拖拽下载 Status: 0空闲 1 下载中
        Minute: { Enable:false, IsEnd:false, status:0 }   //分钟数据拖拽下载
    };

    //自动更新设置
    this.IsAutoUpdate=false;                    //是否自动更新行情数据
    this.AutoUpdateFrequency=30000;             //30秒更新一次数据
    this.AutoUpdateTimer;                       //自动更新定时器

    //this.KLineApiUrl="http://opensource.zealink.com/API/KLine2";                  //历史K线api地址
    this.KLineApiUrl=g_JSChartResource.Domain+"/API/KLine2";                        //历史K线api地址
    this.MinuteKLineApiUrl=g_JSChartResource.Domain+'/API/KLine3';                  //历史分钟数据
    this.DragMinuteKLineApiUrl=g_JSChartResource.Domain+'/API/KLine4';              //拖动1分钟K数据下载
    this.DragKLineApiUrl=g_JSChartResource.Domain+'/API/KLine5';                    //拖动日K数据下载
    this.RealtimeApiUrl=g_JSChartResource.Domain+"/API/Stock";                      //实时行情api地址
    this.KLineMatchUrl=g_JSChartResource.Domain+"/API/KLineMatch";                  //形态匹配
    this.StockHistoryDayApiUrl= g_JSChartResource.Domain+'/API/StockHistoryDay';    //股票历史数据
    this.TickApiUrl=g_JSChartResource.Domain+'/API/StockDetail';                    //当天分笔数据

    this.MinuteDialog;      //双击历史K线 弹出分钟走势图
    this.RightMenu;         //右键菜单
    this.ChartPictureMenu;  //画图工具 单个图形设置菜单

    this.ResetDragDownload=function()
    {
        this.DragDownload.Day.Status=0;
        this.DragDownload.Day.IsEnd=false;

        this.DragDownload.Minute.Status=0;
        this.DragDownload.Minute.IsEnd=false;
    }

    this.ResetPage=function()   //重置分页下载
    {
        this.Page.Day.Finish=false;
        this.Page.Day.Index=0;

        this.Page.Minute.Finish=false;
        this.Page.Minute.Index=0;
    }

    this.StopAutoUpdate=function()
    {
        this.CancelAutoUpdate();
        this.AutoUpdateEvent(false,'KLineChartContainer::StopAutoUpdate');
        if (!this.IsAutoUpdate) return;
        this.IsAutoUpdate=false;
    }

    this.ChartOperator=function(obj) //图形控制函数 {ID:JSCHART_OPERATOR_ID, ...参数 }
    {
        var id=obj.ID;
        if (id===JSCHART_OPERATOR_ID.OP_SCROLL_LEFT || id===JSCHART_OPERATOR_ID.OP_SCROLL_RIGHT )    //左右移动 { Step:移动数据个数 }
        {
            var isLeft=(id===JSCHART_OPERATOR_ID.OP_SCROLL_LEFT ? true:false);
            var step=1;
            if (obj.Step>0) step=obj.Step;
            if(this.DataMove(step*this.StepPixel,isLeft))    //每次移动一个数据
            {
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.ResetFrameXYSplit();
                this.Draw();
            }
        }
        else if (id===JSCHART_OPERATOR_ID.OP_ZOOM_IN || id===JSCHART_OPERATOR_ID.OP_ZOOM_OUT)       //缩放
        {
            var cursorIndex={};
            cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
            if (id===JSCHART_OPERATOR_ID.OP_ZOOM_IN)
            {
                if (!this.Frame.ZoomUp(cursorIndex)) return;
            }
            else
            {
                if (!this.Frame.ZoomDown(cursorIndex)) return;
            }
            this.CursorIndex=cursorIndex.Index;
            this.UpdataDataoffset();
            this.UpdatePointByCursorIndex();
            this.UpdateFrameMaxMin();
            this.Draw();
        }
        else if (id===JSCHART_OPERATOR_ID.OP_GOTO_HOME)
        {
            var hisData=null;
            if (!this.Frame.Data) hisData=this.Frame.Data;
            else hisData=this.Frame.SubFrame[0].Frame.Data;
            if (!hisData) return;  //数据还没有到达

            var showCount=this.PageSize;
            var pageSize = this.GetMaxMinPageSize();
            if (pageSize.Max < showCount) showCount = pageSize.Max;
            else if (pageSize.Min>showCount) showCount=pageSize.Min;

            for(var i in this.Frame.SubFrame)   //设置一屏显示的数据个数
            {
                var item =this.Frame.SubFrame[i].Frame;
                item.XPointCount=showCount;
            }

            var index=hisData.Data.length-showCount;
            hisData.DataOffset=index;
            this.CursorIndex=0.6;

            this.LastPoint.X=null;
            this.LastPoint.Y=null;

            JSConsole.Chart.Log(`[KLineChartContainer::ChartOperator] OP_GOTO_HOME, dataOffset=${hisData.DataOffset} CursorIndex=${this.CursorIndex} PageSize=${showCount}`);

            this.UpdataDataoffset();           //更新数据偏移
            this.UpdateFrameMaxMin();          //调整坐标最大 最小值
            this.Frame.SetSizeChage(true);
            this.Draw();
            this.UpdatePointByCursorIndex();   //更新十字光标位子
        }
    }

    this.OnWheel=function(e)
    {
        JSConsole.Chart.Log('[KLineChartContainer::OnWheel]',e);
        var x = e.clientX-this.UIElement.getBoundingClientRect().left;
        var y = e.clientY-this.UIElement.getBoundingClientRect().top;

        var isInClient=false;
        this.Canvas.beginPath();
        this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight());
        isInClient=this.Canvas.isPointInPath(x,y);
        
        if (isInClient && e.wheelDelta<0)       //缩小
        {
            var cursorIndex={};
            cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
            if (this.Frame.ZoomDown(cursorIndex))
            {
                this.CursorIndex=cursorIndex.Index;
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.Draw();
            }
        }
        else if (isInClient && e.wheelDelta>0)  //放大
        {
            var cursorIndex={};
            cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
            if (this.Frame.ZoomUp(cursorIndex))
            {
                this.CursorIndex=cursorIndex.Index;
                this.UpdatePointByCursorIndex();
                this.UpdataDataoffset();
                this.UpdateFrameMaxMin();
                this.Draw();
            }
        }

        if(e.preventDefault) e.preventDefault();
        else e.returnValue = false;
    }

    //创建
    //windowCount 窗口个数
    this.Create=function(windowCount)
    {
        this.UIElement.JSChartContainer=this;

        //创建十字光标
        this.ChartCorssCursor=new ChartCorssCursor();
        this.ChartCorssCursor.Canvas=this.Canvas;
        this.ChartCorssCursor.StringFormatX=new HQDateStringFormat();
        this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat();
        this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID;

        //创建等待提示
        this.ChartSplashPaint = new ChartSplashPaint();
        this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new HQTradeFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=30;
        this.Frame.ChartBorder.Left=5;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;
        this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架
        this.ChartSplashPaint.Frame = this.Frame;

        this.CreateChildWindow(windowCount);
        this.CreateMainKLine();

        //子窗口动态标题
        for(var i in this.Frame.SubFrame)
        {
            var titlePaint=new DynamicChartTitlePainting();
            titlePaint.Frame=this.Frame.SubFrame[i].Frame;
            titlePaint.Canvas=this.Canvas;
            titlePaint.LanguageID=this.LanguageID;

            this.TitlePaint.push(titlePaint);
        }

        this.UIElement.addEventListener("keydown", (e)=>{ this.OnKeyDown(e); }, true);            //键盘消息
        this.UIElement.addEventListener("wheel", (e)=>{ this.OnWheel(e); }, true);                //上下滚动消息
    }

    //创建子窗口
    this.CreateChildWindow=function(windowCount)
    {
        for(var i=0;i<windowCount;++i)
        {
            var border=new ChartBorder();
            border.UIElement=this.UIElement;

            var frame=new KLineFrame();
            frame.Canvas=this.Canvas;
            frame.ChartBorder=border;
            frame.Identify=i;                   //窗口序号

            if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal;        //绑定菜单事件
            if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal;

            frame.HorizontalMax=20;
            frame.HorizontalMin=10;

            if (i==0)
            {
                frame.YSplitOperator=new FrameSplitKLinePriceY();
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('price');
                frame.YSplitOperator.FrameSplitData2=this.FrameSplitData.get('double');
                var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
                border.BottomSpace=12*pixelTatio;  //主图上下留空间
                border.TopSpace=12*pixelTatio;
            }
            else
            {
                frame.YSplitOperator=new FrameSplitY();
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
                frame.YSplitOperator.LanguageID=this.LanguageID;
                //frame.IsLocked = true;
            }

            frame.YSplitOperator.Frame=frame;
            frame.YSplitOperator.ChartBorder=border;
            frame.XSplitOperator=new FrameSplitKLineX();
            frame.XSplitOperator.Frame=frame;
            frame.XSplitOperator.ChartBorder=border;
            frame.XSplitOperator.LanguageID=this.LanguageID;

            if (i!=windowCount-1) frame.XSplitOperator.ShowText=false;

            for(var j=frame.HorizontalMin;j<=frame.HorizontalMax;j+=1)
            {
                frame.HorizontalInfo[j]= new CoordinateInfo();
                frame.HorizontalInfo[j].Value=j;
                if (i==0 && j==frame.HorizontalMin) continue;

                frame.HorizontalInfo[j].Message[1]=j.toString();
                frame.HorizontalInfo[j].Font="14px 微软雅黑";
            }

            var subFrame=new SubFrameItem();
            subFrame.Frame=frame;
            if (i==0)
                subFrame.Height=20;
            else
                subFrame.Height=10;

            this.Frame.SubFrame[i]=subFrame;
        }
    }

    this.CreateSubFrameItem=function(id)
    {
        var border=new ChartBorder();
        border.UIElement=this.UIElement;

        var frame=new KLineFrame();
        frame.Canvas=this.Canvas;
        frame.ChartBorder=border;
        frame.Identify=id;                   //窗口序号

        if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal;        //绑定菜单事件
        if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal;

        frame.HorizontalMax=20;
        frame.HorizontalMin=10;
        frame.YSplitOperator=new FrameSplitY();
        frame.YSplitOperator.LanguageID=this.LanguageID;
        frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
        frame.YSplitOperator.Frame=frame;
        frame.YSplitOperator.ChartBorder=border;
        frame.XSplitOperator=new FrameSplitKLineX();
        frame.XSplitOperator.Frame=frame;
        frame.XSplitOperator.ChartBorder=border;
        frame.XSplitOperator.ShowText=false;

        //K线数据绑定
        var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount;
        frame.XPointCount=xPointCouont;
        frame.Data=this.ChartPaint[0].Data;

        for(var j=frame.HorizontalMin;j<=frame.HorizontalMax;j+=1)
        {
            frame.HorizontalInfo[j]= new CoordinateInfo();
            frame.HorizontalInfo[j].Value=j;
            frame.HorizontalInfo[j].Message[1]=j.toString();
            frame.HorizontalInfo[j].Font="14px 微软雅黑";
        }

        var subFrame=new SubFrameItem();
        subFrame.Frame=frame;
        subFrame.Height=10;

        return subFrame;
    }

    //创建主图K线画法
    this.CreateMainKLine=function()
    {
        var kline=new ChartKLine();
        kline.Canvas=this.Canvas;
        kline.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        kline.ChartFrame=this.Frame.SubFrame[0].Frame;
        kline.Name="Main-KLine";
        kline.DrawType=this.KLineDrawType;

        this.ChartPaint[0]=kline;

        this.TitlePaint[0]=new DynamicKLineTitlePainting();
        this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame;
        this.TitlePaint[0].Canvas=this.Canvas;
        this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint;    //绑定叠加
        this.TitlePaint[0].LanguageID=this.LanguageID;
    }

    //绑定主图K线数据
    this.BindMainData=function(hisData,showCount)
    {
        this.ChartPaint[0].Data=hisData;
        this.ChartPaint[0].Symbol=this.Symbol;
        for(var i in this.Frame.SubFrame)
        {
            var item =this.Frame.SubFrame[i].Frame;
            item.XPointCount=showCount+this.RightSpaceCount;
            item.Data=this.ChartPaint[0].Data;

            item.XSplitOperator.Symbol=this.Symbol;
            item.XSplitOperator.Period=this.Period;
        }

        this.TitlePaint[0].Data=this.ChartPaint[0].Data;                    //动态标题
        this.TitlePaint[0].Symbol=this.Symbol;
        this.TitlePaint[0].Name=this.Name;
        this.TitlePaint[0].Period=this.Period;

        this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data;   //十字光标
        this.Frame.Data=this.ChartPaint[0].Data;

        for(var i in this.OverlayChartPaint)   //K线叠加 主图股票数据绑定到叠加上
        {
            var item=this.OverlayChartPaint[i];
            item.MainData=this.ChartPaint[0].Data;
        }        

        var dataOffset=hisData.Data.length-showCount;
        if (dataOffset<0) dataOffset=0;
        this.ChartPaint[0].Data.DataOffset=dataOffset;
        this.ChartPaint[0].Period=this.Period;

        this.ChartCorssCursor.StringFormatY.Symbol=this.Symbol;

        this.CursorIndex=showCount;
        if (this.CursorIndex+dataOffset>=hisData.Data.length) this.CursorIndex=hisData.Data.length-1-dataOffset;
        if (this.CursorIndex<0) this.CursorIndex=0; //不一定对啊

        if (this.CustomShow) //定制显示 1次有效
        {
            this.SetCustomShow(this.CustomShow,hisData);
            this.CustomShow=null;
        }
    }

    this.UpdateMainData=function(hisData, lastDataCount)
    {
        var frameHisdata=null;
        if (!this.Frame.Data) frameHisdata=this.Frame.Data;
        else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata=this.Frame.SubFrame[0].Frame.Data;
        if (!frameHisdata) return;

        var newDataCount=0;
        if (lastDataCount>0 && hisData.Data.length>lastDataCount)
        {
            newDataCount=hisData.Data.length-lastDataCount;
            JSConsole.Chart.Log(`[KLineChartContainer::UpdateMainData]  [count=${lastDataCount}->${hisData.Data.length}], [newDataCount=${newDataCount}]`);
        }

        this.ChartPaint[0].Data=hisData;
        this.ChartPaint[0].Symbol=this.Symbol;
        this.ChartPaint[0].Data.DataOffset=frameHisdata.DataOffset+newDataCount;    //加上数据增加的个数
        for(var i in this.Frame.SubFrame)
        {
            var item =this.Frame.SubFrame[i].Frame;
            item.Data=this.ChartPaint[0].Data;
        }

        this.TitlePaint[0].Data=this.ChartPaint[0].Data;                    //动态标题
        this.TitlePaint[0].Symbol=this.Symbol;
        this.TitlePaint[0].Name=this.Name;

        this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data;   //十字光标
        this.Frame.Data=this.ChartPaint[0].Data;

        for(var i in this.OverlayChartPaint)    //主图股票数据绑定到叠加股票上
        {
            var item=this.OverlayChartPaint[i];
            item.MainData=this.ChartPaint[0].Data;
        }

        this.ChartCorssCursor.StringFormatY.Symbol=this.Symbol;
    }

    this.SetCustomShow=function(customShow,hisData)
    {
        if (!customShow || !customShow.Date || customShow.Date<20000101) return;

        var firstDate=customShow.Date;
        var index=null;
        for(var i =0;i<hisData.Data.length;++i)
        {
            var item=hisData.Data[i];
            if (item.Date>=firstDate)
            {
                index=i;
                break;
            }
        }
        if (index===null) 
        {
            JSConsole.Chart.Log(`[KLineChartContainer::SetCustomShow] Can't find first date=${firstDate}`);
            return;
        }

        var count=hisData.Data.length-index;
        if (customShow.PageSize>0) count=customShow.PageSize;
        
        var customShowCount=count;
        var pageSize=this.GetMaxMinPageSize();
        if (count>pageSize.Max) customShowCount=pageSize.Max;
        else if (count<pageSize.Min) customShowCount=pageSize.Min;
        
        for(var i in this.Frame.SubFrame)
        {
            var item =this.Frame.SubFrame[i].Frame;
            item.XPointCount=customShowCount;
        }
        
        this.ChartPaint[0].Data.DataOffset=index;
        this.CursorIndex=0.6;
    }

    //创建指定窗口指标
    this.CreateWindowIndex=function(windowIndex)
    {
        this.WindowIndex[windowIndex].Create(this,windowIndex);
    }

    this.BindIndexData=function(windowIndex,hisData)
    {
        if (!this.WindowIndex[windowIndex]) return;

        if (typeof(this.WindowIndex[windowIndex].RequestData)=="function")  //数据需要另外下载的.
        {
            this.WindowIndex[windowIndex].RequestData(this,windowIndex,hisData);
            return;
        }
        if (typeof(this.WindowIndex[windowIndex].ExecuteScript)=='function')
        {
            this.WindowIndex[windowIndex].ExecuteScript(this,windowIndex,hisData);
            return;
        }

        this.WindowIndex[windowIndex].BindData(this,windowIndex,hisData);
    }

    //叠加指标
    this.BindOverlayIndexData=function(overlayItem, windowIndex, hisData)
    {
        if (!overlayItem.Script) return;

        if (typeof(overlayItem.Script.RequestData)=='function')
        {
            overlayItem.Script.RequestData(this,windowIndex,hisData);
            return;
        }

        if (typeof(overlayItem.Script.ExecuteScript)=='function')
        {
            overlayItem.Script.ExecuteScript(this,windowIndex,hisData);
            return;
        }

        overlayItem.Script.BindData(this,windowIndex,hisData);
    }

    //执行指示(专家指示 五彩K线)
    this.BindInstructionIndexData=function(hisData)
    {
        if (this.ColorIndex && typeof(this.ColorIndex.ExecuteScript)=='function')   //五彩K线
        {
            this.ColorIndex.ExecuteScript(this,0,hisData);
        }

        if (this.TradeIndex && typeof(this.TradeIndex.ExecuteScript)=='function')   //交易指标
        {
            this.TradeIndex.ExecuteScript(this,0,hisData);
        }
    }

    //获取子窗口的所有画法
    this.GetChartPaint=function(windowIndex)
    {
        var paint=new Array();
        for(var i in this.ChartPaint)
        {
            if (i==0) continue; //第1个K线数据除外

            var item=this.ChartPaint[i];
            if (item.ChartFrame==this.Frame.SubFrame[windowIndex].Frame)
                paint.push(item);
        }

        return paint;
    }

    this.AutoUpdateEvent=function(bStart, explain)   //自定更新事件, 是给websocket使用
    {
        var eventID=bStart ? JSCHART_EVENT_ID.RECV_START_AUTOUPDATE:JSCHART_EVENT_ID.RECV_STOP_AUTOUPDATE;
        if (!this.mapEvent.has(eventID)) return;

        var self=this;
        var event=this.mapEvent.get(eventID);
        var data={ Stock:{ Symbol:this.Symbol, Name:this.Name, Right:this.Right, Period:this.Period  }, Explain: explain };
        if (bStart) 
        {
            data.Callback=function(data) //数据到达更新回调
            { 
                if (ChartData.IsDayPeriod(self.Period,true)) self.RecvRealtimeData(data); 
                else if (ChartData.IsMinutePeriod(self.Period,true)) self.RecvMinuteRealtimeData(data);
                else if (ChartData.IsSecondPeriod(self.Period)) self.RecvMinuteRealtimeData(data);
            }
        }
        event.Callback(event,data,this);
    }

    this.SendKLineUpdateEvent=function(bindData)
    {
        var event=this.GetEventCallback(JSCHART_EVENT_ID.RECV_KLINE_UPDATE_DATA);
        if (event && event.Callback)
        {
            var data={ HistoryData:bindData, Stock:{Symbol:this.Symbol, Name:this.Name } }
            event.Callback(event,data,this);
            return true;
        }

        return false;
    }
    

    this.RequestHistoryData=function()
    {
        var self=this;
        this.ChartSplashPaint.IsEnableSplash = true;
        this.FlowCapitalReady=false;
        this.ResetPage(); //重置分页
        this.ResetDragDownload();
        this.Draw();

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestHistoryData', //类名::
                Explain:'日K数据',
                Request:{ Url:self.KLineApiUrl,  Type:'POST' ,
                    Data: { symbol:self.Symbol, count:self.MaxReqeustDataCount, field: ["name","symbol","yclose","open","price","high","low","vol"] } }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.ChartSplashPaint.IsEnableSplash = false;
                self.RecvHistoryData(data);
                var page=self.Page.Day;
                if (page.Enable==true && page.Finish==false)
                {
                    self.RequestHistoryPageData();
                }
                else
                {
                    self.AutoUpdateEvent(true,'KLineChartContainer::RequestHistoryData');
                    self.AutoUpdate();
                }
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: this.KLineApiUrl,
            data:
            {
                "field": ["name","symbol","yclose","open","price","high","low","vol"],
                "symbol": self.Symbol,
                "start": -1,
                "count": self.MaxReqeustDataCount
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash = false;
                self.RecvHistoryData(data);
                var page=self.Page.Day;
                if (page.Enable==true && page.Finish==false)
                {
                    self.RequestHistoryPageData();
                }
                else
                {
                    self.AutoUpdateEvent(true,'KLineChartContainer::RequestHistoryData');
                    self.AutoUpdate();
                }
            }
        });
    }

    this.RecvHistoryData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToHistoryData(data);

        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=aryDayData;
        sourceData.DataType=0;      //0=日线数据 1=分钟数据
        sourceData.Symbol=data.symbol;

        //显示的数据
        var bindData=new ChartData();
        bindData.Data=aryDayData;
        bindData.Right=this.Right;
        bindData.Period=this.Period;
        bindData.DataType=0;
        bindData.Symbol=data.symbol;

        if (bindData.Right>0 && !this.IsApiPeriod)    //复权
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) && !this.IsApiPeriod)   //周期数据
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.SourceData=sourceData;
        this.Symbol=data.symbol;
        this.Name=data.name;
        this.BindMainData(bindData,this.PageSize);
        this.Frame.SetSizeChage(true);
        this.BindInstructionIndexData(bindData);    //执行指示脚本
        
        var firstSubFrame;
        for(var i=0; i<this.Frame.SubFrame.length; ++i) //执行指标
        {
            if (i==0) firstSubFrame=this.Frame.SubFrame[i].Frame;
            this.BindIndexData(i,bindData);
        }

        if (firstSubFrame && firstSubFrame.YSplitOperator)
        {
            firstSubFrame.YSplitOperator.Symbol=this.Symbol;
            firstSubFrame.YSplitOperator.Data=this.ChartPaint[0].Data;          //K线数据
            firstSubFrame.YSplitOperator.Period=this.Period;                    //周期
        }

        var page=this.Page.Day;
        if (page.Enable==false || (page.Enable==true && page.Finish==true))  //分页下载 这些数据迁移到分页下载完以后下载
        {
            this.RequestFlowCapitalData();          //请求流通股本数据 (主数据下载完再下载)
            this.RequestOverlayHistoryData();       //请求叠加数据 (主数据下载完再下载)
            this.CreateChartDrawPictureByStorage(); //创建画图工具
        }

        if (page.Enable) page.Index=1;    //第一页下载完成

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
        this.UpdatePointByCursorIndex();   //更新十字光标位子

        //叠加指标
        for(var i=0;i<this.Frame.SubFrame.length;++i)
        {
            var item=this.Frame.SubFrame[i];
            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                this.BindOverlayIndexData(overlayItem,i,bindData)
            }
        }

        if (this.mapEvent.has(JSCHART_EVENT_ID.RECV_HISTROY_DATA))
        {
            var event=this.mapEvent.get(JSCHART_EVENT_ID.RECV_HISTROY_DATA);
            var data={ HistoryData:bindData, Stock:{Symbol:this.Symbol, Name:this.Name } }
            event.Callback(event,data,this);
        }
    }

    this.RequestHistoryPageData=function()  //请求分页
    {
        var self=this;
        if (this.NetworkFilter)
        {
            var firstItem=this.SourceData.Data[0];   //最新的一条数据
            var obj=
            {
                Name:'KLineChartContainer::RequestHistoryPageData', //类名::
                Explain:'日K数据分页',
                Request:{ Url:'none',  Type:'POST' ,
                    Data: { symbol:self.Symbol, index:self.Page.Day.Index, field: ["name","symbol","yclose","open","price","high","low","vol"], firstDate:firstItem.Date, first:{ date: firstItem.Date } } }, 
                Page:self.Page.Day,
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvHistoryPageData(data);
                var page=self.Page.Day;
                if (page.Enable==true && page.Finish==false)
                    self.RequestHistoryPageData();  //继续下载
                else
                    self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        //模拟异步请求
        setTimeout(function()
        {
            var page=self.Page.Day;
            page.Finish=true;
            var data= //测试数据
            { 
                data: 
                [
                    [ 20150218, 12.54, 12.5, 12.74, 11.95, 12.27, 117632441, 1459671045 ],
                    [ 20150219, 12.27, 12.1, 12.23, 11.9, 12.23, 95889423, 1157078548 ],
                    [ 20150220, 12.23, 12.17, 12.17, 11.9, 12.05, 67763439, 815652761] 
                ],
                symbol:self.Symbol,
                name:self.Name
            };
    
            self.RecvHistoryPageData(data);
            if (page.Enable==true && page.Finish==false)
                self.RequestHistoryPageData();
            else
                self.AutoUpdate();

        },500)
    }

    this.RecvHistoryPageData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToHistoryData(data);
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数

        for(var i in aryDayData)    //数据往前插
        {
            var item=aryDayData[i];
            this.SourceData.Data.splice(i,0,item);
        }
        
        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;

        if (bindData.Right>0 && bindData.Period<=3)    //复权(日线数据才复权)
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.UpdateMainData(bindData,lastDataCount);
        this.BindInstructionIndexData(bindData);    //执行指示脚本
        
        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        var page=this.Page.Day;
        ++page.Index;

        if (page.Enable==true && page.Finish==true)  //分页下载 这些数据迁移到分页下载完以后下载
        {
            this.RequestFlowCapitalData();          //请求流通股本数据 (主数据下载完再下载)
            this.RequestOverlayHistoryData();       //请求叠加数据 (主数据下载完再下载)
            this.CreateChartDrawPictureByStorage(); //创建画图工具
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.ReqeustHistoryMinuteData=function()
    {
        var self=this;
        this.ChartSplashPaint.IsEnableSplash = true;
        this.FlowCapitalReady=false;
        this.ResetPage(); //重置分页
        this.ResetDragDownload();
        this.Draw();

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::ReqeustHistoryMinuteData', //类名::
                Explain:'1分钟K线数据',
                Request:{ Url:self.MinuteKLineApiUrl,  Type:'POST',
                    Data: { symbol:self.Symbol, count:self.MaxRequestMinuteDayCount, field: ["name","symbol", "yclose","open","price","high","low","vol"] }  }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.ChartSplashPaint.IsEnableSplash = false;
                self.RecvMinuteHistoryData(data);
                var page=self.Page.Minute;
                if (page.Enable==true && page.Finish==false)
                {
                    self.ReqeustHistoryMinutePageData();
                }
                else
                {
                    self.AutoUpdateEvent(true,'KLineChartContainer::ReqeustHistoryMinuteData');
                    self.AutoUpdate();
                }
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: this.MinuteKLineApiUrl,
            data:
            {
                "field": ["name","symbol", "yclose","open","price","high","low","vol"],
                "symbol": self.Symbol,
                "start": -1,
                "count": self.MaxRequestMinuteDayCount
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash = false;
                self.RecvMinuteHistoryData(data);
                var page=self.Page.Minute;
                if (page.Enable==true && page.Finish==false)
                {
                    self.ReqeustHistoryMinutePageData();
                }
                else 
                {
                    self.AutoUpdateEvent(true,'KLineChartContainer::ReqeustHistoryMinuteData');
                    self.AutoUpdate();
                }
            }
        });
    }


    this.RecvMinuteHistoryData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToMinuteHistoryData(data);
        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=aryDayData;
        sourceData.DataType=1;      //0=日线数据 1=分钟数据
        sourceData.Symbol=data.symbol;

        //显示的数据
        var bindData=new ChartData();
        bindData.Data=aryDayData;
        bindData.Right=this.Right;
        bindData.Period=this.Period;
        bindData.DataType=1; 
        bindData.Symbol=data.symbol;

        if (ChartData.IsMinutePeriod(bindData.Period,false) && !this.IsApiPeriod)   //周期数据
        {
            var periodData=sourceData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.SourceData=sourceData;
        this.Symbol=data.symbol;
        this.Name=data.name;
        this.BindMainData(bindData,this.PageSize);
        this.Frame.SetSizeChage(true);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        var firstSubFrame;
        for(var i=0; i<this.Frame.SubFrame.length; ++i) //执行指标
        {
            if (i==0) firstSubFrame=this.Frame.SubFrame[i].Frame;
            this.BindIndexData(i,bindData);
        }

        if (firstSubFrame && firstSubFrame.YSplitOperator)
        {
            firstSubFrame.YSplitOperator.Symbol=this.Symbol;
            firstSubFrame.YSplitOperator.Data=this.ChartPaint[0].Data;         //K线数据
            firstSubFrame.YSplitOperator.Period=this.Period;                    //周期
        }

        for(var i in this.OverlayChartPaint)    //分钟数据不支持叠加 清空
        {
            var item=this.OverlayChartPaint[i];
            item.Data=null;
            //item.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID;
        }

        var page=this.Page.Minute;
        if (page.Enable==false || (page.Enable==true && page.Finish==true) )
        {
            this.RequestFlowCapitalData();              //请求流通股本数据 (主数据下载完再下载)
            this.RequestOverlayHistoryMinuteData();     //请求叠加数据 (主数据下载完再下载)
            this.CreateChartDrawPictureByStorage();     //创建画图工具
        }

        if (page.Enable) ++page.Index;

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

        //叠加指标
        for(var i=0;i<this.Frame.SubFrame.length;++i)
        {
            var item=this.Frame.SubFrame[i];
            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                this.BindOverlayIndexData(overlayItem,i,bindData)
            }
        }

        if (this.mapEvent.has(JSCHART_EVENT_ID.RECV_HISTROY_DATA))
        {
            var event=this.mapEvent.get(JSCHART_EVENT_ID.RECV_HISTROY_DATA);
            var data={ HistoryData:bindData, Stock:{Symbol:this.Symbol, Name:this.Name } }
            event.Callback(event,data,this);
        }
    }

    this.ReqeustHistoryMinutePageData=function()
    {
        var self=this;
        if (this.NetworkFilter)
        {
            var firstItem=this.SourceData.Data[0];   //最新的一条数据
            var obj=
            {
                Name:'KLineChartContainer::RecvHistoryMinutePageData', //类名::
                Explain:'1分钟K线数据分页',
                Request:{ Url:'none',  Type:'POST' ,
                    Data: { symbol:self.Symbol, index:self.Page.Minute.Index, field: ["name","symbol","yclose","open","price","high","low","vol"], firstDate:firstItem.Date, first:{ date: firstItem.Date, time:firstItem.Time } } }, 
                Page:self.Page.Minute,
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvHistoryMinutePageData(data);
                var page=self.Page.Minute;
                if (page.Enable==true && page.Finish==false)
                    self.ReqeustHistoryMinutePageData();  //继续下载
                else
                    self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        //模拟异步请求
        setTimeout(function()
        {
            var page=self.Page.Minute;
            page.Finish=true;
            var data= //测试数据
            { 
                data: 
                [
                    [ 20190906,14.58,14.71,14.71,14.71,14.71,1096425,16128411,925],
                    [ 20190906,14.71,14.73,14.74,14.71,14.71,2154859,31731820,930],
                    [ 20190906,14.71,14.71,14.71,14.68,14.69,1427516,20989208,931],
                    [ 20190906,14.69,14.69,14.71,14.68,14.7, 1680503,24694143,932],
                    [ 20190906,14.7,14.69,14.7,14.65,14.65,1315900,19310964,933],
                    [ 20190906,14.65,14.66,14.69,14.65,14.68,702955,10313842,934],
                    [ 20190906,14.68,14.7,14.71,14.67, 14.67,1735266,25495875,935],
                    [ 20190906,14.67,14.68,14.7,14.67,14.67,739000,10845398,936],
                    [ 20190906,14.67,14.67,14.68,14.67,14.68,389800,5721266,937],
                    [ 20190906,14.68,14.68,14.7,14.68,14.69,648477,9527859,938],
                    [ 20190906,14.69,14.7,14.71,14.69,14.7,1128400,16589794,939],
                    [ 20190906,14.7,14.7,14.71,14.69,14.71,714858,10509708,940],
                    [ 20190906,14.71,14.71,14.71,14.69,14.69,401500,5900477,941],
                    [ 20190906,14.69,14.69,14.71,14.69,14.69,1165684,17131034,942],
                    [ 20190906,14.69, 14.69,14.7,14.67,14.67,498516, 7321024,943],
                    [ 20190906,14.67,14.68,14.68,14.67,14.67,350126,5139012,944],
                    [ 20190906,14.67,14.67,14.69,14.67,14.69,561600,8246789,945]
                ]
                //symbol:'600000.sh',
                //name:'浦发行情'
            };
    
            self.RecvHistoryMinutePageData(data);
            if (page.Enable==true && page.Finish==false)
                self.ReqeustHistoryMinutePageData();
            else
                self.AutoUpdate();

        },500)
    }

    this.RecvHistoryMinutePageData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToMinuteHistoryData(data);
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数

        for(var i in aryDayData)    //数据往前插
        {
            var item=aryDayData[i];
            this.SourceData.Data.splice(i,0,item);
        }

        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;

        if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,false))    //复权(日线数据才复权)
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.UpdateMainData(bindData,lastDataCount);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        var page=this.Page.Minute;
        if (page.Enable==false || (page.Enable==true && page.Finish==true) )
        {
            this.RequestFlowCapitalData();          //请求流通股本数据 (主数据下载完再下载)
            this.CreateChartDrawPictureByStorage(); //创建画图工具
        }

        if (page.Enable) ++page.Index;

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    //请求实时行情数据
    this.RequestRealtimeData=function()
    {
        var self=this;
        var arySymbol=[self.Symbol];
        for(var i in this.OverlayChartPaint)    //叠加的股票更新
        {
            var item=this.OverlayChartPaint[i];
            if (!item.Symbol) continue;
            if (!item.MainData) continue;   //等待主图股票数据未下载完
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue;
            arySymbol.push(item.Symbol);
        }

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestRealtimeData', //类名::函数名
                Explain:'当天最新日线数据',
                Request:{ Url:self.RealtimeApiUrl, Data:{ symbol: arySymbol, 
                    field:["name","symbol","yclose","open","price","high","low","vol","amount","date","time"] }, Type:'POST' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvRealtimeData(data);
                self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: this.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time"],
                "symbol": arySymbol,
                "start": -1
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.RecvRealtimeData(data);
                self.AutoUpdate();
            }
        });
    }

    this.RecvRealtimeData=function(data)
    {
        if (this.IsOnTouch==true) return;   //正在操作中不更新数据
        var realtimeData=KLineChartContainer.JsonDataToRealtimeData(data,this.Symbol);
        if (!realtimeData) return;
        var item=this.SourceData.Data[this.SourceData.Data.length-1];   //最新的一条数据
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数

        if (item.Date==realtimeData.Date)   //实时行情数据更新
        {
            JSConsole.Chart.Log('[KLineChartContainer::RecvRealtimeData] update kline by minute data',realtimeData);

            item.Close=realtimeData.Close;
            item.High=realtimeData.High;
            item.Low=realtimeData.Low;
            item.Vol=realtimeData.Vol;
            item.Amount=realtimeData.Amount;
        }
        else if (item.Date<realtimeData.Date)   //新增加数据
        {
            JSConsole.Chart.Log('[KLineChartContainer::RecvRealtimeData] insert kline by minute data',realtimeData);

            var newItem =new HistoryData();
            newItem.YClose=realtimeData.YClose;
            newItem.Open=realtimeData.Open;
            newItem.Close=realtimeData.Close;
            newItem.High=realtimeData.High;
            newItem.Low=realtimeData.Low;
            newItem.Vol=realtimeData.Vol;
            newItem.Amount=realtimeData.Amount;
            newItem.Date=realtimeData.Date;
            this.SourceData.Data.push(newItem);
        }
        else
        {
            return;
        }
        
        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;
        bindData.Symbol=this.Symbol;

        if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true))    //复权(日线数据才复权)
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        this.UpdateMainData(bindData,lastDataCount);//更新主图数据
        this.UpdateOverlayRealtimeData(data);       //更新叠加股票数据
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

        this.SendKLineUpdateEvent(bindData);
    }

    this.UpdateOverlayRealtimeData=function(data)
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (!item.Symbol) continue;
            if (!item.MainData) continue;   //等待主图股票数据未下载完
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue;
            var realtimeData=KLineChartContainer.JsonDataToRealtimeData(data,item.Symbol);  //获取叠加股票的最新数据
            if (!realtimeData) continue;
            var sourceData=item.SourceData; //叠加股票的所有数据
            var latestData=sourceData.Data[sourceData.Data.length-1];   //最新的一条数据

            if (latestData.Date==realtimeData.Date)   //实时行情数据更新
            {
                JSConsole.Chart.Log('[KLineChartContainer::UpdateOverlayRealtimeData] update kline by minute data',realtimeData);

                latestData.Close=realtimeData.Close;
                latestData.High=realtimeData.High;
                latestData.Low=realtimeData.Low;
                latestData.Vol=realtimeData.Vol;
                latestData.Amount=realtimeData.Amount;
            }
            else if (item.Date<realtimeData.Date)   //新增加数据
            {
                JSConsole.Chart.Log('[KLineChartContainer::UpdateOverlayRealtimeData] insert kline by minute data',realtimeData);

                var newItem =new HistoryData();
                newItem.YClose=realtimeData.YClose;
                newItem.Open=realtimeData.Open;
                newItem.Close=realtimeData.Close;
                newItem.High=realtimeData.High;
                newItem.Low=realtimeData.Low;
                newItem.Vol=realtimeData.Vol;
                newItem.Amount=realtimeData.Amount;
                newItem.Date=realtimeData.Date;
                sourceData.Data.push(newItem);
            }
            else 
            {
                continue;
            }

            var bindData=new ChartData();
            bindData.Data=sourceData.Data;
            bindData.Period=this.Period;
            bindData.Right=this.Right;
            bindData.DataType=0;

            if (bindData.Right>0 && MARKET_SUFFIX_NAME.IsSHSZStockA(data.symbol))    //复权数据 ,A股才有有复权
            {
                var rightData=bindData.GetRightDate(bindData.Right);
                bindData.Data=rightData;
            }

            var aryOverlayData=this.SourceData.GetOverlayData(bindData.Data);      //和主图数据拟合以后的数据
            bindData.Data=aryOverlayData;

            if (ChartData.IsDayPeriod(bindData.Period,false))   //周期数据
            {
                var periodData=bindData.GetPeriodData(bindData.Period);
                bindData.Data=periodData;
            }

            item.Data=bindData;
        }
    }

    this.RequestMinuteRealtimeData=function()
    {
        var self=this;
        var arySymbol=[self.Symbol];
        for(var i in this.OverlayChartPaint)    //叠加的股票更新
        {
            var item=this.OverlayChartPaint[i];
            if (!item.Symbol) continue;
            if (!item.MainData) continue;   //等待主图股票数据未下载完
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue;
            arySymbol.push(item.Symbol);
        }

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestMinuteRealtimeData', //类名::
                Explain:'当天1分钟K线数据',
                Request:{ Url:self.RealtimeApiUrl, Data:{ symbol: arySymbol, 
                    field:["name","symbol","price","yclose","minutecount","minute","date","time"] }, Type:'POST' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvMinuteRealtimeData(data);
                self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: this.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","price","yclose","minutecount","minute","date","time"],
                "symbol": arySymbol,
                "start": -1
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.RecvMinuteRealtimeData(data);
                self.AutoUpdate();
            }
        });
    }

    this.SetSourceDatatLimit=function(aryLimit)
    {
        this.SourceDataLimit=new Map();
        for(var i in aryLimit)
        {
            var item=aryLimit[i];
            this.SourceDataLimit.set(item.Period, item.MaxCount); //每个周期缓存数据最大个数 key=周期 value=最大个数 
            
            JSConsole.Chart.Log(`[KLineChartContainer::SetSourceDatatLimit] Period=${item.Period}, MaxCount=${item.MaxCount}`);
        }
    }       

    this.ReduceSourceData=function()
    {
        if (!this.SourceDataLimit) return;
        if (!this.SourceDataLimit.has(this.Period)) return;

        var limitCount=this.SourceDataLimit.get(this.Period);

        var frameHisdata=null;
        if (!this.Frame.Data) frameHisdata=this.Frame.Data;
        else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata=this.Frame.SubFrame[0].Frame.Data;
        if (!frameHisdata) return;

        if (limitCount<50) return;
        var dataOffset=frameHisdata.DataOffset;
        var removeCount=0;
        while(this.SourceData.Data.length>limitCount)
        {
            this.SourceData.Data.shift();
            --dataOffset;
            ++removeCount;
        }

        if (removeCount>0)
        {
            if (dataOffset<0) dataOffset=0;
            frameHisdata.DataOffset=dataOffset;
            JSConsole.Chart.Log(`[KLineChartContainer::ReduceSourceData] remove data ${removeCount}, dataOffset=${dataOffset}`);
        }
    }

    this.RecvMinuteRealtimeDataV2=function(data)    //新版本的
    {
        if (this.IsOnTouch==true) return;   //正在操作中不更新数据
        var aryMinuteData=KLineChartContainer.JsonDataToMinuteHistoryData(data);
        if (!aryMinuteData || aryMinuteData.length<=0) return;
        if (this.IsApiPeriod) this.ReduceSourceData();  //减少数据
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数

        if (!this.SourceData.MergeMinuteData(aryMinuteData)) return;

        JSConsole.Chart.Log(`[KLineChartContainer::RecvMinuteRealtimeDataV2] update kline by 1 minute data [${lastDataCount}->${this.SourceData.Data.length}]`);

        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;
        bindData.Symbol=this.Symbol;

        if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true) && !this.IsApiPeriod)    //复权(日线数据才复权)
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if ((ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false)) && !this.IsApiPeriod)   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.UpdateMainData(bindData,lastDataCount);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

        this.SendKLineUpdateEvent(bindData);
    }

    this.RecvMinuteRealtimeData=function(data)
    {
        if (this.IsOnTouch==true) return;   //正在操作中不更新数据
        if (data.ver==2.0) 
        {
            this.RecvMinuteRealtimeDataV2(data);
            return;
        }

        var realtimeData=KLineChartContainer.JsonDataToMinuteRealtimeData(data, this.Symbol);
        if (!realtimeData) return;
        if (this.IsApiPeriod) this.ReduceSourceData();  //减少数据
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数
        var lastSourceDataCount=this.SourceData.Data.length;
        if (!this.SourceData.MergeMinuteData(realtimeData)) return;

        JSConsole.Chart.Log(`[KLineChartContainer::RecvMinuteRealtimeData] update kline by 1 minute data [${lastSourceDataCount}->${this.SourceData.Data.length}]`);

        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;
        bindData.Symbol=this.Symbol;

        if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true))    //复权(日线数据才复权)
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.UpdateMainData(bindData,lastDataCount);
        this.UpdateOverlayMinuteRealtimeData(data);       //更新叠加股票数据
        this.BindInstructionIndexData(bindData);          //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

        this.SendKLineUpdateEvent(bindData);
    }

    //更新当天的全量分钟数据
    this.UpdateOverlayMinuteRealtimeData=function(data) 
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (!item.Symbol) continue;
            if (!item.MainData) continue;   //等待主图股票数据未下载完
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_FINISHED_ID) continue;
            var realtimeData=KLineChartContainer.JsonDataToMinuteRealtimeData(data,item.Symbol);  //获取叠加股票的最新数据
            if (!realtimeData) continue;
            var sourceData=item.SourceData; //叠加股票的所有数据
            if (!sourceData.MergeMinuteData(realtimeData)) return;

            var bindData=new ChartData();
            bindData.Data=sourceData.Data;
            bindData.Period=this.Period;
            bindData.Right=this.Right;
            bindData.DataType=0;

            var aryOverlayData=this.SourceData.GetOverlayMinuteData(bindData.Data);      //和主图数据拟合以后的数据
            bindData.Data=aryOverlayData;

            if (ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据
            {
                var periodData=bindData.GetPeriodData(bindData.Period);
                bindData.Data=periodData;
            }

            item.Data=bindData;
        }
    }

    this.GetHistoryDataCount=function()
    {
        var frameHisdata=null;
        if (!this.Frame.Data) frameHisdata=this.Frame.Data;
        else if (this.Frame.SubFrame && this.Frame.SubFrame[0]) frameHisdata=this.Frame.SubFrame[0].Frame.Data;
        if (!frameHisdata) return -1;
        var lastDataCount=frameHisdata.Data.length;  //上一个的数据长度
        return lastDataCount;
    }

    this.GetRequestDataCount=function() //K线请求数据个数 (由于可以拖拽下载历史数据,所有原来固定个数的就不能用了)
    {
        var result={MaxRequestDataCount:this.MaxReqeustDataCount, MaxRequestMinuteDayCount:this.MaxRequestMinuteDayCount };
        
        if (!this.SourceData || !this.SourceData.Data || this.SourceData.Data.length<=0) return result;

        if (ChartData.IsDayPeriod(this.Period,true))
        {
            var lCount=this.SourceData.Data.length;
            if (lCount>result.MaxRequestDataCount) result.MaxRequestDataCount=lCount;
        }
        else if (ChartData.IsMinutePeriod(this.Period,true))
        {
            var date;
            var lCount=0;
            for(var i in this.SourceData.Data)
            {
                var item=this.SourceData.Data[i];
                if (item.Date!=date) ++lCount;
                date=item.Date;
            }
            if (lCount>result.MaxRequestMinuteDayCount) result.MaxRequestMinuteDayCount=lCount;
        }

        return result;
    }

    //分笔数据
    this.RequestTickData=function()
    {
        var self=this;
        self.ChartSplashPaint.IsEnableSplash = true;
        self.Draw();
        
        //下载缓存文件
        var cacheUrl=g_JSChartResource.CacheDomain+'/cache/dealday/today/'+self.Symbol+'.json'
        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestTickData', //类名::
                Explain:'当天分笔数据',
                Request:{ Url:cacheUrl, Data:{ symbol: self.Symbol }, Type:'GET' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvTickData(data);
                self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        /*
        $.ajax({
            url: self.TickApiUrl,
            data:{"symbol":self.Symbol},
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvTickData(data);
                self.AutoUpdate();
            }
        });
        */
        JSNetwork.HttpRequest({
            url: cacheUrl,
            data:{"symbol":self.Symbol},
            type:"get",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvTickData(data);
                self.AutoUpdate(1);
            },
            error: function(http,e)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.AutoUpdate();
                //self.RecvError(http,e,param);;
            }
        });
       
    }

    this.RecvTickData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToTickData(data);

        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=aryDayData;
        sourceData.DataType=2;      //0=日线数据 1=分钟数据 2=分笔数据

        //显示的数据
        var bindData=new ChartData();
        bindData.Data=aryDayData;
        bindData.Right=this.Right;
        bindData.Period=this.Period;
        bindData.DataType=2;

        //绑定数据
        this.SourceData=sourceData;
        this.Symbol=data.symbol;
        this.Name=data.name;
        this.BindMainData(bindData,this.PageSize);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        var firstSubFrame;
        for(var i=0; i<this.Frame.SubFrame.length; ++i) //执行指标
        {
            if (i==0) firstSubFrame=this.Frame.SubFrame[i].Frame;
            this.BindIndexData(i,bindData);
        }

        if (firstSubFrame && firstSubFrame.YSplitOperator)
        {
            firstSubFrame.YSplitOperator.Symbol=this.Symbol;
            firstSubFrame.YSplitOperator.Data=this.ChartPaint[0].Data;         //K线数据
            firstSubFrame.YSplitOperator.Period=this.Period;                   //周期
            firstSubFrame.YSplitOperator.KLineChart=this.ChartPaint[0];        //绑定K线图形
        }
        
        //this.CreateChartDrawPictureByStorage(); //创建画图工具

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
        this.UpdatePointByCursorIndex();   //更新十字光标位子

        if (this.mapEvent.has(JSCHART_EVENT_ID.RECV_HISTROY_DATA))
        {
            var event=this.mapEvent.get(JSCHART_EVENT_ID.RECV_HISTROY_DATA);
            var data={ HistoryData:bindData, Stock:{Symbol:this.Symbol, Name:this.Name } }
            event.Callback(event,data,this);
        }
    }

    //请求最新的分笔数据
    this.RequestTickRealtimeData=function()
    {
        var self=this;
        var start=0;
        if (this.SourceData && this.SourceData.Data) start=this.SourceData.Data.length;
        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestTickRealtimeData', //类名::
                Explain:'当天最新分笔数据',
                Request:{ Url:self.TickApiUrl, Data:{ symbol: self.Symbol, start:start, end:start+1000 }, Type:'POST' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvTickData(data);
                self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: self.TickApiUrl,
            data:{"symbol":self.Symbol, start:start-10, end:start+1000 },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvTickRealtimeData(data);
                self.AutoUpdate();
            }
        });
    }

    this.RecvTickRealtimeData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToTickData(data);    //增量数据
        if (!aryDayData || aryDayData.length<=0) return;

        //更新原始数据
        var source=this.SourceData.Data;
        var lastDataCount=this.SourceData.Data.length;  //上一个的数据长度
        var lastTime=0;
        if (source.length>0) lastTime=source[source.length-1].Time;
        var newCount=0;
        for(var i in aryDayData)
        {
            var item=aryDayData[i];
            if (item.Time<=lastTime) continue;
            source.push(item);
            ++newCount;
        }
        if (newCount<=0) return;

        //显示的数据
        var bindData=new ChartData();
        bindData.Data=source;
        bindData.Right=this.Right;
        bindData.Period=this.Period;
        bindData.DataType=2;

        //绑定数据
        this.Symbol=data.symbol;
        this.Name=data.name;
        this.UpdateMainData(bindData,lastDataCount);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }
        
        //this.CreateChartDrawPictureByStorage(); //创建画图工具

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
        this.UpdatePointByCursorIndex();   //更新十字光标位子
    }

    //周期切换
    this.ChangePeriod=function(period)
    {
        if (this.Period==period) return;

        var isDataTypeChange=false;
        if (period>CUSTOM_DAY_PERIOD_START && period<=CUSTOM_DAY_PERIOD_START)
        {
            if (this.SourceData.DataType!=0) isDataTypeChange=true;
        }
        else if ((period>CUSTOM_MINUTE_PERIOD_START && period<=CUSTOM_MINUTE_PERIOD_END) || 
                    (period>CUSTOM_SECOND_PERIOD_START && period<=CUSTOM_SECOND_PERIOD_END))
        {
            if (this.SourceData.DataType!=1) isDataTypeChange=true;
        }
        else
        {
            switch(period)
            {
                case 0:     //日线
                case 1:     //周
                case 2:     //月
                case 3:     //年
                case 9:     //季线
                case 21:    //双周
                    if (this.SourceData.DataType!=0) isDataTypeChange=true;
                    break;
                case 4:     //1分钟
                case 5:     //5分钟
                case 6:     //15分钟
                case 7:     //30分钟
                case 8:     //60分钟
                case 11:    //2小时
                case 12:    //4小时
                    if (this.SourceData.DataType!=1) isDataTypeChange=true;
                    break;
                case 10:    //分笔线
                    if (this.SourceData.DataType!=2) isDataTypeChange=true;
                    break;
            }
        }

        this.Period=period;
        this.ReloadChartDrawPicture();   //切换周期了 清空画图工具

        if (isDataTypeChange==false && !this.IsApiPeriod)
        {
            this.Update();
            return;
        }

        if (ChartData.IsDayPeriod(this.Period,true))
        {
            this.CancelAutoUpdate();                    //先停止定时器
            this.AutoUpdateEvent(false,'KLineChartContainer::ChangePeriod');                //切换周期先停止更新
            this.ResetOverlaySymbolStatus();
            this.RequestHistoryData();                  //请求日线数据
            this.ReqeustKLineInfoData();
        }
        else if (ChartData.IsMinutePeriod(this.Period,true) || ChartData.IsSecondPeriod(this.Period))
        {
            this.CancelAutoUpdate();                    //先停止定时器
            this.AutoUpdateEvent(false,'KLineChartContainer::ChangePeriod');                //切换周期先停止更新
            this.ResetOverlaySymbolStatus();
            this.ReqeustHistoryMinuteData();            //请求分钟数据
        }  
        else if (ChartData.IsTickPeriod(this.Period))
        {
            this.CancelAutoUpdate();                    //先停止定时器
            this.AutoUpdateEvent(false,'KLineChartContainer::ChangePeriod');
            this.RequestTickData();                     //请求分笔数据
        }
    }

    //复权切换
    this.ChangeRight=function(right)
    {
        if (!MARKET_SUFFIX_NAME.IsSHSZStockA(this.Symbol)) return;  //A股股票有复权
        if (ChartData.IsTickPeriod(this.Period)) return;            //分笔没有复权

        var upperSymbol=this.Symbol.toUpperCase();
        if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) return;

        if (right<0 || right>2) return;

        if (this.Right==right) return;

        this.Right=right;

        if (!this.IsApiPeriod)
        {
            this.Update();
            return;
        }
        else    //API周期数据 重新请求数据
        {
            if (ChartData.IsDayPeriod(this.Period,true))
            {
                this.CancelAutoUpdate();                    //先停止定时器
                this.AutoUpdateEvent(false,'KLineChartContainer::ChangeRight');                //切换复权先停止更新
                this.ResetOverlaySymbolStatus();
                this.RequestHistoryData();                  //请求日线数据
            }
            else if (ChartData.IsMinutePeriod(this.Period,true) || ChartData.IsSecondPeriod(this.Period))
            {
                this.CancelAutoUpdate();                    //先停止定时器
                this.AutoUpdateEvent(false,'KLineChartContainer::ChangeRight');                //切换复权先停止更新
                this.ResetOverlaySymbolStatus();
                this.ReqeustHistoryMinuteData();            //请求分钟数据
            }  
        }
    }

    //设置第1屏的起始日期
    this.SetFirstShowDate=function(obj)
    {
        if (!obj || !obj.Date) return;

        JSConsole.Chart.Log('[KLineChartContainer::SetFirstShowDate] obj=', obj);

        var hisData=null;
        if (!this.Frame.Data) hisData=this.Frame.Data;
        else hisData=this.Frame.SubFrame[0].Frame.Data;
        if (!hisData) return;  //数据还没有到达

        var index=null;
        if (ChartData.IsDayPeriod(this.Period,true))
        {
            for(var i=0;i<hisData.Data.length;++i)
            {
                var item=hisData.Data[i];
                if (item.Date>=obj.Date)
                {
                    index=i;
                    break;
                }
            }
        }
        else if (ChartData.IsMinutePeriod(this.Period,true))
        {
            let findTime=obj.Time;
            if (!IFrameSplitOperator.IsPlusNumber(obj.Time)) findTime=0;
            for(var i=0;i<hisData.Data.length;++i)
            {
                var item=hisData.Data[i];
                if (item.Date>=obj.Date && item.Time>=findTime)
                {
                    index=i;
                    break;
                }
            }
        }

        if (index===null) 
        {
            JSConsole.Chart.Log(`[KLineChartContainer::SetFirstShowDate] an't find first date=${obj.Date} time=${obj.Time}`, obj);
            return;
        }

        var count=hisData.Data.length-index;
        var customShowCount=count;
        if (obj.PageSize>0) customShowCount=obj.PageSize;

        var pageSize = this.GetMaxMinPageSize();
        if (pageSize.Max < customShowCount) customShowCount = pageSize.Max;
        else if (pageSize.Min>customShowCount) customShowCount=pageSize.Min;

        for(var i in this.Frame.SubFrame)   //设置一屏显示的数据个数
        {
            var item =this.Frame.SubFrame[i].Frame;
            item.XPointCount=customShowCount;
        }
        
        hisData.DataOffset=index;
        this.CursorIndex=0;
        this.LastPoint.X=null;
        this.LastPoint.Y=null;

        JSConsole.Chart.Log(`[KLineChartContainer::SetFirstShowDate] dataOffset=${hisData.DataOffset} CursorIndex=${this.CursorIndex} PageSize=${customShowCount}`);

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
        this.UpdatePointByCursorIndex();   //更新十字光标位子
    }

    //删除某一个窗口的指标
    this.DeleteIndexPaint=function(windowIndex)
    {
        let paint=new Array();  //踢出当前窗口的指标画法
        for(let i in this.ChartPaint)
        {
            let item=this.ChartPaint[i];

            if (i==0 || item.ChartFrame!=this.Frame.SubFrame[windowIndex].Frame)
                paint.push(item);
        }
        
        this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=null;    //清空指定最大最小值
        this.Frame.SubFrame[windowIndex].Frame.IsLocked=false;          //解除上锁
        this.Frame.SubFrame[windowIndex].Frame.YSplitScale = null;      //清空固定刻度

        this.ChartPaint=paint;

        //清空东条标题
        var titleIndex=windowIndex+1;
        this.TitlePaint[titleIndex].Data=[];
        this.TitlePaint[titleIndex].Title=null;
    }

    //显示隐藏主图K线
    this.ShowKLine=function(isShow)
    {
        if (this.ChartPaint.length<=0 || !this.ChartPaint[0]) return;
        this.ChartPaint[0].IsShow=isShow;
    }

    this.SetInstructionData=function(type,instructionData)
    {
        if (this.ChartPaint.length<=0 || !this.ChartPaint[0]) return;

        var title=this.TitlePaint[1];
        if (type==2) //五彩K线
        {
            this.ChartPaint[0].ColorData=instructionData.Data;
            if (title) title.ColorIndex={Name:instructionData.Name};
        }
        else if (type==1)   //专家指示
        {
            this.ChartPaint[0].TradeData={Sell:instructionData.Sell, Buy:instructionData.Buy};
            if (title) title.TradeIndex={Name:instructionData.Name}
        }
    }

    this.ChangeInstructionIndex=function(indexName)
    {
        let scriptData = new JSIndexScript();
        let indexInfo = scriptData.Get(indexName);
        if (!indexInfo) return;
        if(indexInfo.InstructionType!=1 && indexInfo.InstructionType!=2) return;

        indexInfo.ID=indexName;
        this.ChangeInstructionScriptIndex(indexInfo);
        
    }

    this.ChangeInstructionScriptIndex=function(indexData)
    {
        if (indexData.InstructionType==1)       //交易系统
        {
            this.TradeIndex=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行
        }
        else if (indexData.InstructionType==2)  //五彩K线
        {
            this.ColorIndex=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行
        }
        else
        {
            return;
        }

        var bindData=this.ChartPaint[0].Data;
        this.BindInstructionIndexData(bindData);

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    this.CancelInstructionIndex=function()  //取消指示数据
    {
        if (this.ChartPaint.length<=0 || !this.ChartPaint[0]) return;

        this.ColorIndex=null;
        this.TradeIndex=null;
        this.ChartPaint[0].ColorData=null;  //五彩K线数据取消掉
        this.ChartPaint[0].TradeData=null;  //交易系统数据取消

        var title=this.TitlePaint[1];
        if (title)
        {
            title.TradeIndex=null;
            title.ColorIndex=null;
        }

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    //切换成 脚本指标
    this.ChangeScriptIndex=function(windowIndex,indexData)
    {
        this.DeleteIndexPaint(windowIndex);
        this.WindowIndex[windowIndex]=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行

        var bindData=this.ChartPaint[0].Data;
        this.BindIndexData(windowIndex,bindData);   //执行脚本

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    //切换api指标
    this.ChangeAPIIndex=function(windowIndex,indexData)
    {
        this.DeleteIndexPaint(windowIndex);
        //使用API挂接指标数据 API:{ Name:指标名字, Script:指标脚本可以为空, Args:参数可以为空, Url:指标执行地址 }
        var apiItem=indexData.API;
        this.WindowIndex[windowIndex]=new APIScriptIndex(apiItem.Name,apiItem.Script,apiItem.Args,indexData);

        var bindData=this.ChartPaint[0].Data;
        this.BindIndexData(windowIndex,bindData);   //执行脚本

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    //切换指标 指定切换窗口指标
    this.ChangeIndex=function(windowIndex,indexName,option)
    {
        if (option && option.API)   //切换api指标
            return this.ChangeAPIIndex(windowIndex,option);

        var indexItem=JSIndexMap.Get(indexName);
        if (!indexItem) 
        {
            //查找系统指标
            let scriptData = new JSIndexScript();
            let indexInfo = scriptData.Get(indexName);
            if (!indexInfo) return;
            if (indexInfo.IsMainIndex) 
            {
                windowIndex = 0;  //主图指标只能在主图显示
            }
            else 
            {
                if (windowIndex == 0) windowIndex = 1;  //幅图指标,不能再主图显示
            }
            let indexData = 
            { 
                Name:indexInfo.Name, Script:indexInfo.Script, Args: indexInfo.Args, ID:indexName ,
                //扩展属性 可以是空
                KLineType:indexInfo.KLineType,  YSpecificMaxMin:indexInfo.YSpecificMaxMin,  YSplitScale:indexInfo.YSplitScale,
                FloatPrecision:indexInfo.FloatPrecision, Condition:indexInfo.Condition, StringFormat:indexInfo.StringFormat
            };

            if (option)
            {
                if (option.FloatPrecision>=0) indexData.FloatPrecision=option.FloatPrecision;
                if (option.StringFormat>0) indexData.StringFormat=option.StringFormat;
                if (option.Args) indexData.Args=option.Args;
                if (option.TitleFont) indexData.TitleFont=option.TitleFont;
                if (option.IsShortTitle) indexData.IsShortTitle=option.IsShortTitle;
            }
            
            return this.ChangeScriptIndex(windowIndex, indexData);
        }

        //主图指标
        if (indexItem.IsMainIndex)
        {
            if (windowIndex>0)  windowIndex=0;  //主图指标只能在主图显示
        }
        else
        {
            if (windowIndex==0) windowIndex=1;  //幅图指标,不能再主图显示
        }

        var paint=new Array();  //踢出当前窗口的指标画法
        for(var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];

            if (i==0 || item.ChartFrame!=this.Frame.SubFrame[windowIndex].Frame)
                paint.push(item);
        }

        
        this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=null;    //清空指定最大最小值
        this.Frame.SubFrame[windowIndex].Frame.IsLocked=false;          //解除上锁
        this.Frame.SubFrame[windowIndex].Frame.YSplitScale = null;      //清空固定刻度

        this.ChartPaint=paint;

        //清空东条标题
        var titleIndex=windowIndex+1;
        this.TitlePaint[titleIndex].Data=[];
        this.TitlePaint[titleIndex].Title=null;

        this.WindowIndex[windowIndex]=indexItem.Create();
        this.CreateWindowIndex(windowIndex);

        var bindData=this.ChartPaint[0].Data;
        this.BindIndexData(windowIndex,bindData);

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    this.ChangePyScriptIndex=function(windowIndex,indexData)
    {
        this.DeleteIndexPaint(windowIndex);
        this.WindowIndex[windowIndex]=new PyScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行

        var bindData=this.ChartPaint[0].Data;
        this.BindIndexData(windowIndex,bindData);   //执行脚本

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    this.AddOverlayIndex=function(obj) 
    {
        var overlay=this.CreateOverlayWindowsIndex(obj);
        if (!overlay) return;

        var bindData=this.ChartPaint[0].Data;
        this.BindOverlayIndexData(overlay,obj.WindowIndex,bindData);
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    //创建一个叠加指标
    this.CreateOverlayWindowsIndex=function(obj) // {WindowIndex:, IndexName:, Identify:, ShowRightText:}
    {
        let indexName=obj.IndexName;
        let windowIndex=obj.WindowIndex;
        let scriptData = new JSIndexScript();
        let indexInfo = scriptData.Get(indexName);  //系统指标
        if (!indexInfo) 
        {
            var indexCustom=JSIndexMap.Get(indexName);  //定制指标
            if (!indexCustom)
            {
                console.warn(`[KLineChartContainer::CreateOverlayIndex] can not find index[${indexName}]`);
                return null;
            }
        }

        var subFrame=this.Frame.SubFrame[windowIndex];
        subFrame.Interval=this.OverlayIndexFrameWidth;
        var overlayFrame=new OverlayIndexItem();
        if (obj.Identify) overlayFrame.Identify=obj.Identify;   //由外部指定id
        var frame= this.ClassName==='KLineChartHScreenContainer' ? new OverlayKLineHScreenFrame() : new OverlayKLineFrame();
        frame.Canvas=this.Canvas;
        frame.MainFrame=subFrame.Frame;
        frame.ChartBorder=subFrame.Frame.ChartBorder;
        if (obj.ShowRightText===true) frame.IsShow=true;
        else if (obj.ShowRightText===false) frame.IsShow=false;

        frame.YSplitOperator=new FrameSplitY();
        frame.YSplitOperator.LanguageID=this.LanguageID;
        frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
        frame.YSplitOperator.Frame=frame;
        frame.YSplitOperator.ChartBorder=frame.ChartBorder;
        frame.YSplitOperator.SplitCount=subFrame.Frame.YSplitOperator.SplitCount;
        

        overlayFrame.Frame=frame;

        if (indexInfo)
        {
            let indexData = 
            { 
                Name:indexInfo.Name, Script:indexInfo.Script, Args: indexInfo.Args, ID:indexName , 
                //扩展属性 可以是空
                KLineType:indexInfo.KLineType,  YSpecificMaxMin:indexInfo.YSpecificMaxMin,  YSplitScale:indexInfo.YSplitScale,
                FloatPrecision:indexInfo.FloatPrecision, Condition:indexInfo.Condition,
            };

            var scriptIndex=new OverlayScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行
            scriptIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame };    //叠加指标信息
            overlayFrame.Script=scriptIndex;
        }
        else 
        {
            var scriptIndex=indexCustom.Create();
            scriptIndex.OverlayIndex={ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame };    //叠加指标信息
            scriptIndex.Create(this,windowIndex);
            overlayFrame.Script=scriptIndex;
        }

        subFrame.OverlayIndex.push(overlayFrame);
        return overlayFrame;
    }

    this.DeleteOverlayWindowsIndex=function(identify) //删除叠加指标
    {
        var findIndex=null;
        for(var i in this.Frame.SubFrame)
        {
            var item=this.Frame.SubFrame[i];
            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                if (overlayItem.Identify===identify)
                {
                    item.OverlayIndex.splice(j, 1);
                    findIndex=parseInt(i);
                    break;
                }
            }
        }

        if (findIndex==null) 
        {
            console.warn(`[KLineChartContainer::DeleteOverlayWindowsIndex] can't find overlay index. [identify=${identify}]`);
            return;
        }

        var windowsIndex=findIndex+1;
        var titlePaint=this.TitlePaint[windowsIndex];
        if (titlePaint.OverlayIndex.has(identify))
            titlePaint.OverlayIndex.delete(identify);

        this.Draw();
    }
    
    this.ChangeKLineDrawType=function(drawType)
    {
        if (this.KLineDrawType==drawType) return;

        this.KLineDrawType=drawType;
        for(var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];
            if (i==0) item.DrawType=this.KLineDrawType;
            else if (item.ClassName=='ChartVolStick') item.KLineDrawType=this.KLineDrawType
        }

        for(var i in this.OverlayChartPaint)    //叠加K线样式修改
        {
            var item=this.OverlayChartPaint[i];
            item.DrawType=this.KLineDrawType;
        } 

        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    //修改坐标类型
    this.ChangeCoordinateType=function(type)
    {
        if (!this.Frame && !this.Frame.SubFrame) return;
        if (!this.Frame.SubFrame.length) return;

        if (type==2) //反转坐标
        {
            this.Frame.SubFrame[0].Frame.CoordinateType=1;
        }
        else if(type==1)
        {
            this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=type;
        }
        else if (type==0)
        {
            this.Frame.SubFrame[0].Frame.CoordinateType=0;
            this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=0;
        }
        else
        {
            return;
        }

        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    //设置指标窗口个数
    this.ChangeIndexWindowCount=function(count)
    {
        if (count<=0) return;
        if (this.Frame.SubFrame.length==count) return;

        var currentLength=this.Frame.SubFrame.length;
        if (currentLength>count)
        {
            for(var i=currentLength-1;i>=count;--i)
            {
                this.DeleteIndexPaint(i);
                this.Frame.SubFrame[i].Frame.ClearToolbar();
            }

            this.Frame.SubFrame.splice(count,currentLength-count);
            this.WindowIndex.splice(count,currentLength-count);

            //最后一个显示X轴坐标
            for(var i=0;i<this.Frame.SubFrame.length;++i)
            {
                var item=this.Frame.SubFrame[i].Frame;
                if (i==this.Frame.SubFrame.length-1) item.XSplitOperator.ShowText=true;
                else item.XSplitOperator.ShowText=false;
            }
        }
        else
        {
            //创建新的指标窗口
            for(var i=currentLength;i<count;++i)
            {
                var subFrame=this.CreateSubFrameItem(i);
                this.Frame.SubFrame[i]=subFrame;
                var titlePaint=new DynamicChartTitlePainting();
                titlePaint.Frame=this.Frame.SubFrame[i].Frame;
                titlePaint.Canvas=this.Canvas;
                titlePaint.LanguageID=this.LanguageID;
                this.TitlePaint[i+1]=titlePaint;
            }

            //创建指标
            const indexName=["RSI","MACD","VOL","UOS","CHO","BRAR"];
            let scriptData = new JSIndexScript();
            for(var i=currentLength;i<count;++i)
            {
                var name=indexName[i%indexName.length];
                let indexInfo = scriptData.Get(name);
                this.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args,indexInfo);    //脚本执行
                var bindData=this.ChartPaint[0].Data;
                this.BindIndexData(i,bindData);   //执行脚本
            }

            //最后一个显示X轴坐标
            for(var i=0;i<this.Frame.SubFrame.length;++i)
            {
                var item=this.Frame.SubFrame[i].Frame;
                if (i==this.Frame.SubFrame.length-1) item.XSplitOperator.ShowText=true;
                else item.XSplitOperator.ShowText=false;
            }

            this.UpdataDataoffset();           //更新数据偏移
        }

        this.Frame.SetSizeChage(true);
        this.ResetFrameXYSplit();
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    this.ChangeIndexTemplate=function(option)   //切换指标模板 可以设置指标窗口个数 每个窗口的指标
    {
        if (!option.Windows) return;
        var count=option.Windows.length;
        if (count<=0) return;
        var currentLength=this.Frame.SubFrame.length;

        //清空所有的指标图型
        for(var i=0;i<currentLength;++i)
        {
            this.DeleteIndexPaint(i);
            var frame=this.Frame.SubFrame[i];
            frame.YSpecificMaxMin=null;
            frame.IsLocked=false;
            frame.YSplitScale = null;
        }
        
        if (currentLength>count)
        {
            for(var i=currentLength-1;i>=count;--i)
            {
                this.Frame.SubFrame[i].Frame.ClearToolbar();
            }

            this.Frame.SubFrame.splice(count,currentLength-count);
            this.WindowIndex.splice(count,currentLength-count);
        }
        else
        {
            for(var i=currentLength;i<count;++i)  //创建新的指标窗口
            {
                var subFrame=this.CreateSubFrameItem(i);
                this.Frame.SubFrame[i]=subFrame;
                var titlePaint=new DynamicChartTitlePainting();
                titlePaint.Frame=this.Frame.SubFrame[i].Frame;
                titlePaint.Canvas=this.Canvas;
                titlePaint.LanguageID=this.LanguageID;
                this.TitlePaint[i+1]=titlePaint;
            } 
        }

        var systemScript = new JSIndexScript();
        var bindData=this.ChartPaint[0].Data;
        for(var i=0;i<count;++i)
        {
            var windowIndex=i;
            var indexID=option.Windows[i].Index;
            var indexItem=JSIndexMap.Get(indexID);
            var titleIndex=windowIndex+1;
            this.TitlePaint[titleIndex].Data=[];
            this.TitlePaint[titleIndex].Title=null;
            if (indexItem)
            {
                this.WindowIndex[i]=indexItem.Create();
                this.CreateWindowIndex(windowIndex);
                this.BindIndexData(windowIndex,bindData);
            }
            else
            {
                var indexInfo = systemScript.Get(indexID);
                if (indexInfo)
                {
                    var args=indexInfo.Args;
                    if (option.Windows[i].Args) args=option.Windows[i].Args;
                    let indexData = 
                    { 
                        Name:indexInfo.Name, Script:indexInfo.Script, Args: args, ID:indexID ,
                        //扩展属性 可以是空
                        KLineType:indexInfo.KLineType,  YSpecificMaxMin:indexInfo.YSpecificMaxMin,  YSplitScale:indexInfo.YSplitScale,
                        FloatPrecision:indexInfo.FloatPrecision, Condition:indexInfo.Condition
                    };

                    this.WindowIndex[i]=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行
                    this.BindIndexData(windowIndex,bindData);   //执行脚本
                }
            }
        }

        //最后一个显示X轴坐标
        for(var i=0;i<this.Frame.SubFrame.length;++i)
        {
            var item=this.Frame.SubFrame[i].Frame;
            if (i==this.Frame.SubFrame.length-1) item.XSplitOperator.ShowText=true;
            else item.XSplitOperator.ShowText=false;
        }

        this.UpdataDataoffset();           //更新数据偏移
        this.Frame.SetSizeChage(true);
        this.ResetFrameXYSplit();
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    this.RemoveIndexWindow=function(id)
    {
        JSConsole.Chart.Log('[KLineChartContainer::RemoveIndexWindow] remove id', id);
        if (id==0) return;
        if (!this.Frame.SubFrame) return;
        if (id>=this.Frame.SubFrame.length) return;

        var delFrame=this.Frame.SubFrame[id].Frame;
        this.DeleteIndexPaint(id);
        this.Frame.SubFrame[id].Frame.ClearToolbar();
        this.Frame.SubFrame.splice(id,1);
        this.WindowIndex.splice(id,1);
        this.TitlePaint.splice(id+1,1); //删除对应的动态标题

        for(var i=0;i<this.Frame.SubFrame.length;++i)
        {
            var item=this.Frame.SubFrame[i].Frame;
            if (i==this.Frame.SubFrame.length-1) item.XSplitOperator.ShowText=true;
            else item.XSplitOperator.ShowText=false;

            item.Identify=i;
        }

        if (this.ChartDrawPicture.length>0)
        {
            var aryDrawPicture=[];
            for(var i in this.ChartDrawPicture)
            {
                var item=this.ChartDrawPicture[i];
                if (item.Frame==delFrame) continue;
                aryDrawPicture.push(item);
            }
            this.ChartDrawPicture=aryDrawPicture;
        }

        this.Frame.SetSizeChage(true);
        this.UpdateFrameMaxMin();
        this.ResetFrameXYSplit();
        this.Draw();
    }

    //获取当前的显示的指标
    this.GetIndexInfo=function()
    {
        var aryIndex=[];
        for(var i in this.WindowIndex)
        {
            var item=this.WindowIndex[i];
            var info={Name:item.Name};
            if (item.ID) info.ID=item.ID;
            aryIndex.push(info);
        }

        return aryIndex;
    }

    this.CreateExtendChart=function(name, option)   //创建扩展图形
    {
        var chart;
        switch(name)
        {
            case '筹码分布':
                chart=new StockChip();
                chart.Canvas=this.Canvas;
                chart.ChartBorder=this.Frame.ChartBorder;
                chart.ChartFrame=this.Frame;
                chart.HQChart=this;
                chart.Left=this.Frame.ChartBorder.Right;    //左边间距使用当前框架间距
                chart.SetOption(option);
                this.ExtendChartPaint.push(chart);
                this.Frame.ChartBorder.Right+=chart.Width;  //创建筹码需要增加右边的间距
                return chart;
            case '画图工具':
                chart=new DrawToolsButton();
                chart.Canvas=this.Canvas;
                chart.ChartBorder=this.Frame.ChartBorder;
                chart.ChartFrame=this.Frame;
                chart.HQChart=this;
                chart.Left=this.Frame.ChartBorder.Right;    //左边间距使用当前框架间距
                chart.SetOption(option);
                this.ExtendChartPaint.push(chart);
                this.Frame.ChartBorder.Right+=chart.Width;  //创建筹码需要增加右边的间距
                return chart;
            case 'KLineTooltip':
                if (option.Create && typeof(option.Create)=='function') chart=option.Create();
                else chart=new KLineTooltipPaint();
                chart.Canvas=this.Canvas;
                chart.ChartBorder=this.Frame.ChartBorder;
                chart.ChartFrame=this.Frame;
                chart.HQChart=this;
                option.LanguageID=this.LanguageID;
                chart.SetOption(option);
                this.ExtendChartPaint.push(chart);
                return chart;
            default:
                return null;
        }
    }

    this.OnTouchFinished=function()
    {
        if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
        {
            if (this.TouchStatus.CorssCursorShow==true && this.TouchDrawCount>0) return;

            this.TouchStatus.CorssCursorShow=false;
            this.DrawDynamicInfo();
            return;
        }

        if (this.CorssCursorTouchEnd===true)    //手势离开十字光标消失
        {
            if (this.TouchDrawCount>0) this.DrawDynamicInfo();
            return;
        }

        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            if (item.ClassName==='KLineTooltipPaint')
            {
                this.DrawDynamicInfo();
            }
        }
    }

    this.GetExtendChartByClassName=function(name)
    {
        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            if (item.ClassName==name) return { Index:i, Chart:item };
        }

        return null
    }

    this.DeleteExtendChart=function(data)
    {
        if (data.Index>=this.ExtendChartPaint.length) return;
        if (this.ExtendChartPaint[data.Index]!=data.Chart) return;

        if (typeof(data.Chart.Clear)=='function') data.Chart.Clear();
        this.ExtendChartPaint.splice(data.Index,1);
    }

    //锁|解锁指标 { Index:指标名字,IsLocked:是否要锁上,Callback:回调 }
    this.LockIndex=function(lockData)
    {
        if (!lockData) return;
        if (!lockData.IndexName) return;

        for(let i in this.WindowIndex)
        {
            let item=this.WindowIndex[i];
            if (!item) conintue;
            if (item.Name==lockData.IndexName)
            {
                item.SetLock(lockData);
                this.Update();
                break;
            }
        }
    }

    this.TryClickLock=function(x,y)
    {
        for(let i in this.Frame.SubFrame)
        {
            var item=this.Frame.SubFrame[i];
            if (!item.Frame.IsLocked) continue;
            if (!item.Frame.LockPaint) continue;

            var tooltip=new TooltipData();
            if (!item.Frame.LockPaint.GetTooltipData(x,y,tooltip)) continue;

            tooltip.HQChart=this;
            if (tooltip.Data.Callback) tooltip.Data.Callback(tooltip);
            return true;
        }

        return false;
    }

    this.TryClickIndexTitle=function(x,y)
    {
        for(var i in this.TitlePaint)
        {
            var item=this.TitlePaint[i];
            if (!item.IsClickTitle) continue;
            if (!item.IsClickTitle(x,y)) continue;

            var data={ Point:{X:x, Y:y}, Title:item.Title, FrameID:item.Frame.Identify };
            JSConsole.Chart.Log('[KLineChartContainer::TryClickIndexTitle] click title ', data);

            var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_INDEXTITLE);
            if (event && event.Callback) event.Callback(event,data,this);
            
            return true;
        }

        return false;
    }

    this.SetSizeChage=function(bChanged)
    {
        this.Frame.SetSizeChage(bChanged);
        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            item.SizeChange=bChanged;
        }
    }

    this.Update=function()
    {
        if (!this.SourceData) return;

        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;
        bindData.Symbol=this.Symbol;

        if (bindData.Right>0 && ChartData.IsDayPeriod(bindData.Period,true))    //复权(日线数据才复权)
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.BindMainData(bindData,this.PageSize);

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //叠加数据周期调整
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (!item.SourceData) continue;
        
            if(ChartData.IsMinutePeriod(this.Period,true))  //分钟不支持 清空掉
            {   
                var bindData=new ChartData();
                bindData.Data=item.SourceData.Data;
                bindData.Period=this.Period;
                bindData.Right=this.Right;

                var aryOverlayData=this.SourceData.GetOverlayMinuteData(bindData.Data);      //和主图数据拟合以后的数据
                bindData.Data=aryOverlayData;

                if (ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据
                {
                    var periodData=bindData.GetPeriodData(bindData.Period);
                    bindData.Data=periodData;
                }

                item.Data=bindData;
            }
            else    //日线叠加
            {   
                var bindData=new ChartData();
                bindData.Data=item.SourceData.Data;
                bindData.Period=this.Period;
                bindData.Right=this.Right;

                if (bindData.Right>0 && MARKET_SUFFIX_NAME.IsSHSZStockA(item.Symbol))       //复权数据
                {
                    var rightData=bindData.GetRightDate(bindData.Right);
                    bindData.Data=rightData;
                }

                var aryOverlayData=this.SourceData.GetOverlayData(bindData.Data);      //和主图数据拟合以后的数据
                bindData.Data=aryOverlayData;

                if (ChartData.IsDayPeriod(bindData.Period,false))   //周期数据
                {
                    var periodData=bindData.GetPeriodData(bindData.Period);
                    bindData.Data=periodData;
                }

                item.Data=bindData;
            }
        }

        this.ReqeustKLineInfoData();
        
        this.CreateChartDrawPictureByStorage(); //创建画图工具
        
        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

        //叠加指标
        for(var i=0;i<this.Frame.SubFrame.length;++i)
        {
            var item=this.Frame.SubFrame[i];
            for(var j in item.OverlayIndex)
            {
                var overlayItem=item.OverlayIndex[j];
                this.BindOverlayIndexData(overlayItem,i,bindData)
            }
        }
    }

    //切换股票代码
    this.ChangeSymbol=function(symbol)
    {
        this.CancelAutoUpdate();    //先停止定时器
        this.AutoUpdateEvent(false,'KLineChartContainer::ChangeSymbol');
        this.Symbol=symbol;
        if (MARKET_SUFFIX_NAME.IsSHSZIndex(symbol)) this.Right=0;    //指数没有复权

        //清空指标
        if (this.Frame && this.Frame.SubFrame)
        {
            for(var i=0;i<this.Frame.SubFrame.length;++i)
            {
                this.DeleteIndexPaint(i);
            }
        }

        this.ReloadChartDrawPicture();

        if (ChartData.IsDayPeriod(this.Period,true))
        {
            this.RequestHistoryData();                  //请求日线数据
            this.ReqeustKLineInfoData();
        }
        else if (ChartData.IsMinutePeriod(this.Period,true) || ChartData.IsSecondPeriod(this.Period))
        {
            this.ReqeustHistoryMinuteData();            //请求分钟数据
        } 
        else if (ChartData.IsTickPeriod(this.Period))
        {
            this.RequestTickData();
        } 
    }

    this.ReqeustKLineInfoData=function()
    {
        if (this.ChartPaint.length>0)
        {
            var klinePaint=this.ChartPaint[0];
            klinePaint.InfoData=new Map();
        }

        //信息地雷信息
        for(var i in this.ChartInfo)
        {
            this.ChartInfo[i].RequestData(this);
        }
    }

    //设置K线信息地雷
    this.SetKLineInfo=function(aryInfo,bUpdate)
    {
        this.ChartInfo=[];  //先清空
        for(var i in aryInfo)
        {
            var infoItem=JSKLineInfoMap.Get(aryInfo[i]);
            if (!infoItem) continue;
            var item=infoItem.Create();
            item.MaxReqeustDataCount=this.MaxReqeustDataCount;
            this.ChartInfo.push(item);
        }

        if (bUpdate==true) this.ReqeustKLineInfoData();
    }

    //添加信息地雷
    this.AddKLineInfo=function(infoName,bUpdate)
    {
        var classInfo=JSKLineInfoMap.GetClassInfo(infoName);
        if (!classInfo)
        {
            console.warn("[KLineChartContainer::AddKLineInfo] can't find infoname=", infoName);
            return;
        }

        for(var i in this.ChartInfo)
        {
            var item=this.ChartInfo[i];
            if (item.ClassName==classInfo.ClassName)    //已经存在
                return;
        }

        var infoItem=JSKLineInfoMap.Get(infoName);
        if (!infoItem) return;

        var item=infoItem.Create();
        item.MaxReqeustDataCount=this.MaxReqeustDataCount;
        this.ChartInfo.push(item);

        if (bUpdate==true) 
        {
            item.RequestData(this);  //信息地雷信息
        }
    }

    //删除信息地理
    this.DeleteKLineInfo=function(infoName)
    {
        var classInfo=JSKLineInfoMap.GetClassInfo(infoName);
        if (!classInfo)
        {
            console.warn("[KLineChartContainer::DeleteKLineInfo] can't find infoname=", infoName);
            return;
        }

        for(var i in this.ChartInfo)
        {
            var item=this.ChartInfo[i];
            if (item.ClassName==classInfo.ClassName)
            {
                this.ChartInfo.splice(i,1);
                this.UpdataChartInfo();
                this.Draw();
                break;
            }
        }
    }

    //清空所有的信息地理
    this.ClearKLineInfo=function()
    {
        if (!this.ChartInfo || this.ChartInfo.length<=0) return;

        this.ChartInfo=[];

        var klinePaint=this.ChartPaint[0];
        klinePaint.InfoData=null;
        this.Draw();
    }

    //增加叠加股票 只支持日线数据
    this.OverlaySymbol=function(symbol,option)
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (item.Symbol===symbol)
            {
                console.warn(`[KLineChartContainer::OverlaySymbol] overlay symbol=${symbol} exist.`)
                return false;
            }
        }

        var paint=new ChartOverlayKLine();
        paint.Canvas=this.Canvas;
        paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        paint.ChartFrame=this.Frame.SubFrame[0].Frame;
        paint.Name="Overlay-KLine";
        paint.DrawType=this.KLineDrawType;
        paint.Symbol=symbol;
        if (option && option.Color) paint.Color=option.Color;
        else paint.Color=g_JSChartResource.OverlaySymbol.Color[g_JSChartResource.OverlaySymbol.Random%g_JSChartResource.OverlaySymbol.Color.length];
        paint.SetOption(option);
        ++g_JSChartResource.OverlaySymbol.Random;
        if (this.ChartPaint[0] && this.ChartPaint[0].Data && this.SourceData) paint.MainData=this.ChartPaint[0].Data;         //绑定主图数据
        
        this.OverlayChartPaint.push(paint);

        if (ChartData.IsDayPeriod(this.Period, true)) this.RequestOverlayHistoryData();                   //请求日线数据
        else if (ChartData.IsMinutePeriod(this.Period,true)) this.RequestOverlayHistoryMinuteData();      //请求分钟历史数据   

        return true;
    }

    this.RequestOverlayHistoryData=function()
    {
        if (!this.OverlayChartPaint.length) return;
        if (!this.SourceData || !this.SourceData.Data) return;  //主图数据还没有到完

        var self = this;
        var dataCount=this.GetRequestDataCount();
        var firstDate=this.SourceData.Data[0].Date;
        for(var i in this.OverlayChartPaint)
        {
            let item=this.OverlayChartPaint[i];
            if (!item.MainData) continue;   //等待主图股票数据未下载完
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue;
            let symbol=item.Symbol;
            if (!symbol) continue;

            item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID;

            if (this.NetworkFilter)
            {
                var obj=
                {
                    Name:'KLineChartContainer::RequestOverlayHistoryData', //类名::
                    Explain:'叠加股票日K线数据',
                    Request:{ Url:self.KLineApiUrl, Data: { symbol: symbol, count: dataCount.MaxRequestDataCount,"first":{ date: firstDate },
                        field:["name","symbol","yclose","open","price","high",'vol','amount'] }, Type:'POST' }, 
                    Self:this,
                    PreventDefault:false
                };
                this.NetworkFilter(obj, function(data) 
                { 
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOverlayHistoryData(data,item);
                });

                if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
            }

            //请求数据
            JSNetwork.HttpRequest({
                url: this.KLineApiUrl,
                data:
                {
                    "field": ["name","symbol","yclose","open","price","high"],
                    "symbol": symbol,
                    "start": -1,
                    "count": dataCount.MaxRequestDataCount
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOverlayHistoryData(data,item);
                }
            });
        }
    }

    this.RecvOverlayHistoryData=function(data,paint)
    {
        if (paint.IsDelete) return;

        var aryDayData=KLineChartContainer.JsonDataToHistoryData(data);

        //原始叠加数据
        var sourceData=new ChartData();
        sourceData.Data=aryDayData;
        sourceData.DataType=0;

        var bindData=new ChartData();
        bindData.Data=aryDayData;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=0;

        if (bindData.Right>0 && MARKET_SUFFIX_NAME.IsSHSZStockA(data.symbol))    //复权数据 ,A股才有有复权
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        var aryOverlayData=this.SourceData.GetOverlayData(bindData.Data);      //和主图数据拟合以后的数据
        bindData.Data=aryOverlayData;

        if (ChartData.IsDayPeriod(bindData.Period,false))   //周期数据
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        paint.Data=bindData;
        paint.SourceData=sourceData;
        paint.Title=data.name;
        paint.Symbol=data.symbol;
        paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID;

        this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=1; //调整为百份比坐标

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

    }

    this.RequestOverlayHistoryMinuteData=function()
    {
        if (!this.OverlayChartPaint.length) return;
        if (!this.SourceData || !this.SourceData.Data) return;  //主图数据还没有到完

        var self = this;
        var dataCount=this.GetRequestDataCount();
        var firstDate=this.SourceData.Data[0].Date;
        var firstTime=this.SourceData.Data[0].Time;
        for(var i in this.OverlayChartPaint)
        {
            let item=this.OverlayChartPaint[i];
            if (!item.MainData) continue;   //等待主图股票数据未下载完
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue;
            let symbol=item.Symbol;
            if (!symbol) continue;

            item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID;

            if (this.NetworkFilter)
            {
                var obj=
                {
                    Name:'KLineChartContainer::RequestOverlayHistoryMinuteData', //类名::
                    Explain:'叠加股票分钟K线数据',
                    Request:{ Url:self.MinuteKLineApiUrl, Data: { symbol: symbol, count: dataCount.MaxRequestMinuteDayCount,"first":{ date: firstDate, time:firstTime },
                        field:["name","symbol","yclose","open","price","high",'vol','amount'] }, Type:'POST' }, 
                    Self:this,
                    PreventDefault:false
                };
                this.NetworkFilter(obj, function(data) 
                { 
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOveralyHistoryMinuteData(data,item);
                });

                if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
            }

            //请求数据
            JSNetwork.HttpRequest({
                url: this.MinuteKLineApiUrl,
                data:
                {
                    "field": ["name","symbol","yclose","open","price","high",'vol','amount'],
                    "symbol": symbol,
                    "start": -1,
                    "count": dataCount.MaxRequestMinuteDayCount
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOveralyHistoryMinuteData(data,item);
                }
            });
        }
    }

    this.RecvOveralyHistoryMinuteData=function(data,paint)
    {
        var aryDayData=KLineChartContainer.JsonDataToMinuteHistoryData(data);
        if (!aryDayData) return;

        //原始叠加数据
        var sourceData=new ChartData();
        sourceData.Data=aryDayData;
        sourceData.DataType=1;  //0=日线数据 1=分钟数据

        var bindData=new ChartData();
        bindData.Data=aryDayData;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=1;

        var aryOverlayData=this.SourceData.GetOverlayMinuteData(bindData.Data);      //和主图数据拟合以后的数据
        bindData.Data=aryOverlayData;

        if (ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        paint.Data=bindData;
        paint.SourceData=sourceData;
        paint.Title=data.name;
        paint.Symbol=data.symbol;
        paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID;  //数据下载完成

        this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=1; //调整为百份比坐标
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.ResetOverlaySymbolStatus=function()
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            item.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID;
        }
    }

    //取消叠加股票
    this.ClearOverlaySymbol=function()
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            item.IsDelete=true;
        }

        this.OverlayChartPaint=[];
        this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint;    //绑定叠加
        this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=0; //调整一般坐标
        this.UpdateFrameMaxMin();
        this.Draw();
    }

    //删除一个叠加股票
    this.DeleteOverlaySymbol=function(symbol)
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (item.Symbol===symbol)
            {
                item.IsDelete=true;
                this.OverlayChartPaint.splice(i,1);
                if (this.OverlayChartPaint.length<=0) this.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType=0; //调整一般坐标
                this.UpdateFrameMaxMin();
                this.Draw();
                return true;
            }
        }

        console.warn(`[KLineChartContainer::DeleteOverlaySymbol] overlay symbol=${symbol} not exist.`)
        return false;
    }

    this.RequestFlowCapitalData=function()
    {
        if (!this.Symbol) return;
        if (this.FlowCapitalReady==true) return;
        if (MARKET_SUFFIX_NAME.IsBIT(this.Symbol)) //数字货币不需要下载流通股本
        {
            JSConsole.Chart.Log(`[KLineChartContainer::RequestFlowCapitalData] symbol=${this.Symbol} not need download data.`);
            this.FlowCapitalReady=true;
            return;
        }

        var self = this;
        let fieldList=["name","date","symbol","capital.a"];

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestFlowCapitalData', //类名::
                Explain:'流通股本数据',
                Request:{ Url:self.StockHistoryDayApiUrl, Data: { symbol: [this.Symbol], orderfield:'date',field:fieldList }, Type:'POST' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvFlowCapitalData(data);
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }
        
        //请求数据
        JSNetwork.HttpRequest({
            url: this.StockHistoryDayApiUrl,
            data:
            {
                "field": fieldList,
                "symbol": [this.Symbol],
                "orderfield":"date"
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvFlowCapitalData(recvData);
            }
        });
    }

    this.RecvFlowCapitalData=function(data)
    {
        if (!data.stock || data.stock.length!=1) return;

        let stock=data.stock[0];
        var aryData=new Array();
        for(let i in stock.stockday)
        {
            var item=stock.stockday[i];
            let indexData=new SingleData();
            indexData.Date=item.date;
            var financeData=item.capital;
            if (!financeData) continue;
            if (financeData.a>0)
            {
                indexData.Value=financeData.a;    //流通股本(股)
                aryData.push(indexData);
            }
        }

        if (ChartData.IsMinutePeriod(this.Period,true)) //分钟数据
        {
            var aryFixedData=this.SourceData.GetMinuteFittingFinanceData(aryData);
            for(let i in this.SourceData.Data)
            {
                var item=this.SourceData.Data[i];
                item.FlowCapital=aryFixedData[i].Value;
            }

            var bindData=this.ChartPaint[0].Data;
            var newBindData=new ChartData();
            newBindData.Data=this.SourceData.Data;

            if (ChartData.IsMinutePeriod(bindData.Period,false)) //周期数据
            {
                var periodData=newBindData.GetPeriodData(bindData.Period);  
                newBindData.Data=periodData;
            }
            bindData.Data=newBindData.Data;
        }
        else
        {
            var aryFixedData=this.SourceData.GetFittingFinanceData(aryData);
            for(let i in this.SourceData.Data)
            {
                var item=this.SourceData.Data[i];
                item.FlowCapital=aryFixedData[i].Value;
            }

            var bindData=this.ChartPaint[0].Data;
            var newBindData=new ChartData();
            newBindData.Data=this.SourceData.Data;

            if (bindData.Right>0)    //复权
            {
                var rightData=newBindData.GetRightDate(bindData.Right);
                newBindData.Data=rightData;
            }

            if (ChartData.IsDayPeriod(bindData.Period,false)) //周期数据
            {
                var periodData=newBindData.GetPeriodData(bindData.Period);  
                newBindData.Data=periodData;
            }

            bindData.Data=newBindData.Data;
        }

        this.FlowCapitalReady=true;
        var bDraw=false;
        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            if (item.ClassName=='StockChip')
            {
                bDraw=true;
                break;
            }
        }

        if (bDraw) this.Draw();
    }

    this.SetChartDrawOption=function(option)
    {
        if (IFrameSplitOperator.IsBool(option.IsLockScreen)) this.ChartDrawOption.IsLockScreen=option.IsLockScreen;
        if (IFrameSplitOperator.IsNumber(option.Zoom) && option.Zoom>=0)  this.ChartDrawOption.Zoom=option.Zoom;
    }

    //创建画图工具
    this.CreateChartDrawPicture=function(name, callback)
    {
        var drawPicture=null;
        switch(name)
        {
            case "线段":
                drawPicture=new ChartDrawPictureLine();
                break;
            case "射线":
                drawPicture=new ChartDrawPictureHaflLine();
                break;
            case '水平线':
                drawPicture=new ChartDrawPictureHorizontalLine();
                break;
            case '趋势线':
                drawPicture=new ChartDrawPictureTrendLine();
                break;
            case "矩形":
                drawPicture=new ChartDrawPictureRect();
                break;
            case "圆弧线":
                drawPicture=new ChartDrawPictureArc();
                break;
            case 'M头W底':
                drawPicture=new ChartDrawPictureWaveMW();
                break;
            case '平行线':
                drawPicture=new ChartDrawPictureParallelLines();
                break;
            case '平行通道':
                drawPicture=new ChartDrawPictureParallelChannel();
                break;
            case '价格通道线':
                drawPicture=new ChartDrawPicturePriceChannel();
                break;
            case '文本':
                drawPicture=new ChartDrawPictureText();
                drawPicture.HQChart=this;
                break;
            case '江恩角度线':
                drawPicture=new ChartDrawPictureGannFan();
                break;
            case '阻速线':
                drawPicture=new ChartDrawPictureResistanceLine()
                break;
            case '黄金分割':
                drawPicture=new ChartDrawPictureGoldenSection()
                break;
            case '百分比线':
                drawPicture=new ChartDrawPicturePercentage();
                break;
            case '波段线':
                drawPicture=new ChartDrawPictureWaveBand();
                break;
            case '三角形':
                drawPicture=new ChartDrawPictureTriangle();
                break;
            case '对称角度':
                drawPicture=new ChartDrawPictureSymmetryAngle();
                break;
            case '圆':
                drawPicture=new ChartDrawPictureCircle();
                break;
            case '平行四边形':
                drawPicture=new ChartDrawPictureQuadrangle();
                break;
            case '斐波那契周期线':
                drawPicture=new ChartDrawPictureFibonacci();
                break;
            default:
                {
                    //iconfont 图标
                    const ICONFONT_LIST=new Map(
                        [
                            ["icon-arrow_up", { Text:'\ue683', Color:'#318757'}],
                            ["icon-arrow_down", { Text:'\ue681', Color:'#db563e'}],
                            ["icon-arrow_right", { Text:'\ue680', Color:'#318757'}],
                            ["icon-arrow_left", { Text:'\ue682', Color:'#318757'}],
                        ]
                    );

                    if (ICONFONT_LIST.has(name))
                    {
                        var item=ICONFONT_LIST.get(name);
                        drawPicture=new ChartDrawPictureIconFont();
                        drawPicture.FontOption.Family='iconfont';
                        drawPicture.Text=item.Text;
                        if (item.Color) drawPicture.LineColor=item.Color;
                        break;
                    }
                }
                return false;
        }

        drawPicture.Canvas=this.Canvas;
        drawPicture.Status=0;
        drawPicture.Symbol=this.Symbol;
        drawPicture.Period=this.Period;
        drawPicture.Option=this.ChartDrawOption;
        if (callback) drawPicture.FinishedCallback=callback;    //完成通知上层回调
        self=this;
        drawPicture.Update=function()   //更新回调函数
        {
            self.DrawDynamicInfo();
        };
        this.CurrentChartDrawPicture=drawPicture;
        //JSConsole.Chart.Log("[KLineChartContainer::CreateChartDrawPicture] ", name,this.CurrentChartDrawPicture);
        return true;
    }

    this.SetChartDrawPictureFirstPoint=function(x,y)
    {
        var drawPicture=this.CurrentChartDrawPicture;
        if (!drawPicture) return false;
        if (!this.Frame.SubFrame || this.Frame.SubFrame.length<=0) return false;

        //相对坐标
        var xFixed=x-this.UIElement.getBoundingClientRect().left;
        var yFixed=y-this.UIElement.getBoundingClientRect().top;
        for(var i in this.Frame.SubFrame)
        {
            var frame=this.Frame.SubFrame[i].Frame;
            var left=frame.ChartBorder.GetLeft();
            var top=frame.ChartBorder.GetTopEx();
            var height=frame.ChartBorder.GetHeight();
            var width=frame.ChartBorder.GetWidth();

            this.Canvas.beginPath();
            this.Canvas.rect(left,top,width,height);
            if (this.Canvas.isPointInPath(xFixed,yFixed))
            {
                drawPicture.Frame=frame;
                break;
            }
        }

        if (!drawPicture.Frame) return false;

        drawPicture.Point[0]=new Point();
        drawPicture.Point[0].X=x-this.UIElement.getBoundingClientRect().left;
        drawPicture.Point[0].Y=y-this.UIElement.getBoundingClientRect().top;
        drawPicture.Status=1;   //第1个点完成
        return true;
    }

    this.SetChartDrawPictureSecondPoint=function(x,y)
    {
        var drawPicture=this.CurrentChartDrawPicture;
        if (!drawPicture) return false;

        drawPicture.Point[1]=new Point();
        drawPicture.Point[1].X=x-this.UIElement.getBoundingClientRect().left;
        drawPicture.Point[1].Y=y-this.UIElement.getBoundingClientRect().top;

        drawPicture.Status=2;   //设置第2个点
        return true;
    }

    //设置第3个点
    this.SetChartDrawPictureThirdPoint=function(x,y)
    {
        var drawPicture=this.CurrentChartDrawPicture;
        if (!drawPicture) return false;

        drawPicture.Point[2]=new Point();
        drawPicture.Point[2].X=x-this.UIElement.getBoundingClientRect().left;
        drawPicture.Point[2].Y=y-this.UIElement.getBoundingClientRect().top;

        drawPicture.Status=3;   //设置第3个点
        return true;
    }

    //xStep,yStep 移动的偏移量
    this.MoveChartDrawPicture=function(xStep,yStep)
    {
        var drawPicture=this.CurrentChartDrawPicture;
        if (!drawPicture) return false;

        //JSConsole.Chart.Log("xStep="+xStep+" yStep="+yStep);
        drawPicture.Move(xStep,yStep);

        return true;
    }

    this.FinishChartDrawPicturePoint=function()
    {
        var drawPicture=this.CurrentChartDrawPicture;
        if (!drawPicture) return false;
        if (drawPicture.PointCount!=drawPicture.Point.length) return false;

        drawPicture.Status=10;  //完成
        drawPicture.PointToValue();

        this.ChartDrawPicture.push(drawPicture);
        this.CurrentChartDrawPicture=null;

        //通知上层画好了
        let event=this.GetEventCallback(JSCHART_EVENT_ID.ON_FINISH_DRAWPICTURE); //完成画图工具事件
        if (event && event.Callback)
        {
            let sendData={ DrawPicture: drawPicture };
            event.Callback(event,sendData,this);
        }
        else if (drawPicture.FinishedCallback) drawPicture.FinishedCallback(drawPicture);

        if (this.ChartDrawStorage) this.ChartDrawStorage.SaveDrawData(drawPicture);

        return true;
    }


    //注册鼠标右键事件
    this.OnRightMenu=function(x,y,e)
    {
        if (this.RightMenu)
        {
            var frameId=this.Frame.PtInFrame(x,y);
            e.data={ Chart:this, FrameID:frameId };
            this.RightMenu.DoModal(e);
        }

        var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CONTEXT_MENU);
        if (event)
        {
            var frameId=this.Frame.PtInFrame(x,y);
            var data={ X:x, Y:y, Event:e, FrameID:frameId };
            event.Callback(event,data,this);
        }
    }



    this.FinishMoveChartDrawPicture=function()
    {
        var drawPicture=this.CurrentChartDrawPicture;
        if (!drawPicture) return false;
        if (drawPicture.PointCount!=drawPicture.Point.length) return false;

        drawPicture.Status=10;  //完成
        drawPicture.PointToValue();

        if (this.ChartDrawStorage) this.ChartDrawStorage.SaveDrawData(drawPicture);

        this.CurrentChartDrawPicture=null;
        return true;
    }

    //清空所有的画线工具
    this.ClearChartDrawPicture=function(drawPicture)
    {
        if (!drawPicture)
        {
            this.ChartDrawPicture=[];
            if (this.ChartDrawStorage) this.ChartDrawStorage.Clear();
            this.Draw();
        }
        else
        {
            for(var i in this.ChartDrawPicture)
            {
                if (this.ChartDrawPicture[i]==drawPicture)
                {
                    if (this.ChartDrawStorage) this.ChartDrawStorage.DeleteDrawData(drawPicture);
                    this.ChartDrawPicture.splice(i,1);
                    this.Draw();
                }
            }
        }
    }

    //重新加载画图工具(切换股票|周期)
    this.ReloadChartDrawPicture=function()
    {
        this.ChartDrawPicture=[];
        this.ChartDrawStorageCache;
        if (this.ChartDrawStorage)
        {
            this.ChartDrawStorageCache=this.ChartDrawStorage.GetDrawData( {Symbol:this.Symbol, Period:this.Period} );
        }
    }

    this.CreateChartDrawPictureByStorage=function() //把缓存(this.ChartDrawStorageCache) 画图工具创建出来
    {
        if (!this.ChartDrawStorageCache || this.ChartDrawStorageCache.length<=0) return;

        for(var i in this.ChartDrawStorageCache)
        {
            var item=this.ChartDrawStorageCache[i];
            if (item.FrameID<0 || !this.Frame.SubFrame || this.Frame.SubFrame.length<item.FrameID) continue;

            var drawPicture=IChartDrawPicture.CreateChartDrawPrictrue(item);
            if (!drawPicture) continue;

            drawPicture.Canvas=this.Canvas;
            drawPicture.Status=10;
            drawPicture.Frame=this.Frame.SubFrame[item.FrameID].Frame;  //绑定框架坐标
            drawPicture.ValueToPoint();

            if (drawPicture.ClassName==='ChartDrawPictureText') drawPicture.IsInitialized=true;

            this.ChartDrawPicture.push(drawPicture);
        }

        this.ChartDrawStorageCache=null;    //清空缓存
    }

    //选中画图工具事件
    this.OnSelectChartPicture=function(chart)
    {
        
    }

    //形态匹配
    // scope.Plate 板块范围 scope.Symbol 股票范围
    // sample 样本数据
    this.RequestKLineMatch=function(sample,param)
    {
        var self =this;
        var scope=param.Scope;
        var waitDailog=param.WaitDialog;
        JSConsole.Chart.Log('[KLineChartContainer::RequestKLineMatch',sample,scope)

        var aryDate=new Array();
        var aryValue=new Array();

        for(var i=sample.Start;i<sample.End && i<sample.Data.Data.length;++i)
        {
            var item=sample.Data.Data[i];
            aryDate.push(item.Date);
            aryValue.push(item.Close);
        }

        var sampleData=
        {
            Stock:sample.Stock,
            Index:{Start:sample.Start, End:sample.End}, //数据索引
            Date:{Start:aryDate[0], End:aryDate[aryDate.length-1]}, //起始 结束日期
            Minsimilar:scope.Minsimilar,    //相似度
            Plate:scope.Plate,
            DayRegion:300
        };

        //请求数据
        $.ajax({
            url: this.KLineMatchUrl,
            data:
            {
                "userid": "guest",
                "plate": scope.Plate,
                "period": this.Period,
                "right": this.Right,
                "dayregion": sampleData.DayRegion,
                "minsimilar": scope.Minsimilar,
                "sampledate":aryDate,
                "samplevalue":aryValue
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                if (waitDailog) waitDailog.Close();
                self.RecvKLineMatchData(data,sampleData);
            },
            error:function(jqXHR, textStatus, errorThrown)
            {
                console.warn('[KLineChartContainer::RequestKLineMatch] failed',jqXHR,textStatus, errorThrown);
                if (waitDailog) waitDailog.Close();
            }
        });
    }

    //接收形态选股结果
    this.RecvKLineMatchData=function(data,sample)
    {
        JSConsole.Chart.Log('[KLineChartContainer::RecvKLineMatchData] recv',data,sample);
        var filterData=[]; //结果数据
        for(var i in data.match)
        {
            var item=data.match[i];
            if (item.symbol==sample.Stock.Symbol) continue;

            for(var j in item.data)
            {
                var dataItem=item.data[j];
                var newItem={Symbol:item.symbol, Name:item.name,Similar:dataItem.similar, Start:dataItem.start, End:dataItem.end};
                filterData.push(newItem);
            }
        }
        filterData.sort(function(a,b){return b.Similar-a.Similar;});    //排序
        JSConsole.Chart.Log('[KLineChartContainer::RecvKLineMatchData] filterData',filterData);

        if (this.mapEvent.has(JSCHART_EVENT_ID.RECV_KLINE_MATCH))
        {
            var item=this.mapEvent.get(JSCHART_EVENT_ID.RECV_KLINE_MATCH);
            var data={Sample:sample, Match:filterData, Source:data}
            item.Callback(item,data,this);
        }
        else
        {
            var dlg=new KLineMatchDialog(this.Frame.ChartBorder.UIElement.parentNode);

            var event={ data:{ Chart:this, MatchData:filterData, Sample:sample, Source:data} };
            dlg.DoModal(event);
        } 
    }

    //更新信息地雷
    this.UpdataChartInfo=function()
    {
        //TODO: 根据K线数据日期来做map, 不在K线上的合并到下一个k线日期里面
        var mapInfoData=null;
        if (this.Period==0) //日线数据 根据日期
        {
            mapInfoData=new Map();
            for(var i in this.ChartInfo)
            {
                var infoData=this.ChartInfo[i].Data;
                for(var j in infoData)
                {
                    var item=infoData[j];
                    if (mapInfoData.has(item.Date.toString()))
                    {
                        mapInfoData.get(item.Date.toString()).Data.push(item);
                    }
                    else
                    {

                        mapInfoData.set(item.Date.toString(),{Data:new Array(item)});
                    }
                }
            }
        }
        else if (ChartData.IsDayPeriod(this.Period,false))
        {
            mapInfoData=new Map();
            var hisData=this.ChartPaint[0].Data;
            //增加一个临时数据索引
            for(var i in this.ChartInfo)
            {
                this.ChartInfo[i].TempID=this.ChartInfo[i].Data.length-1;   
            }

            for(var i=0;i<hisData.Data.length;++i)
            {
                var kItem=hisData.Data[i];  //K线数据
                for(var j in this.ChartInfo)
                {
                    var info=this.ChartInfo[j];
                    var infoData=info.Data;
                    for(; info.TempID>=0; --info.TempID)    //信息地雷是倒叙排的
                    {
                        var infoItem=infoData[info.TempID];
                        if (infoItem.Date>kItem.Date) break;
                        
                        //信息地雷日期<K线上的日期 就是属于这个K线上的
                        if (mapInfoData.has(kItem.Date.toString()))
                        {
                            mapInfoData.get(kItem.Date.toString()).Data.push(infoItem);
                        }
                        else
                        {
                            mapInfoData.set(kItem.Date.toString(),{Data:new Array(infoItem)});
                        }
                    }
                }
                //JSConsole.Chart.Log('[KLineChartContainer::UpdataChartInfo]',item);
            }

            //清空临时变量
            for(var i in this.ChartInfo)
            {
                delete this.ChartInfo[i].TempID;
            }
        }
            
        var klinePaint=this.ChartPaint[0];
        klinePaint.InfoData=mapInfoData;
    }

    //更新窗口指标
    this.UpdateWindowIndex=function(index)
    {
        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;

        if (bindData.Right>0)    //复权
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        if (typeof(this.WindowIndex[index].ExecuteScript)=='function')
        {
            var hisData=this.ChartPaint[0].Data;
            this.WindowIndex[index].ExecuteScript(this,index,hisData);
        }
        else
        {
            this.WindowIndex[index].BindData(this,index,bindData);
        }

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    //修改参数指标
    this.ChangeWindowIndexParam=function(index)
    {
        this.WindowIndex[index].Index[0].Param+=1;
        this.WindowIndex[index].Index[1].Param+=1;

        this.UpdateWindowIndex(index);
    }


    this.OnDoubleClick=function(x,y,e)
    {
        var event=null;
        if (this.mapEvent.has(JSCHART_EVENT_ID.DBCLICK_KLINE)) event=this.mapEvent.get(JSCHART_EVENT_ID.DBCLICK_KLINE);

        if (!this.MinuteDialog && !event) return;

        var tooltip=new TooltipData();
        for(var i in this.ChartPaint)
        {
            var item=this.ChartPaint[i];
            if (item.GetTooltipData(x,y,tooltip))
            {
                break;
            }
        }

        if (!tooltip.Data) return;

        if (event)
        {
            var data={ Tooltip:tooltip, Stock:{Symbol:this.Symbol, Name:this.Name } }
            event.Callback(event,data,this);
        }

        if (this.MinuteDialog)
        {
            e.data={Chart:this,Tooltip:tooltip};
            this.MinuteDialog.DoModal(e);
        }
    }

    //选中画图工具 出现单个图形设置菜单
    this.OnSelectChartPicture=function(chart)
    {   
        JSConsole.Chart.Log('[KLineChartContainer::OnSelectChartPicture',chart);
        if (!this.ChartPictureMenu) this.ChartPictureMenu=new ChartPictureSettingMenu(this.UIElement.parentNode);

        var event={ data: { ChartPicture:chart, HQChart:this}};
        this.ChartPictureMenu.DoModal(event);
    }

    this.CancelAutoUpdate=function()    //关闭停止更新
    {
        if (typeof (this.AutoUpdateTimer) == 'number') 
        {
            clearTimeout(this.AutoUpdateTimer);
            this.AutoUpdateTimer = undefined;
        }
    }

    //数据自动更新
    this.AutoUpdate=function(waitTime)  //waitTime 更新时间
    {
        this.CancelAutoUpdate();
        if (!this.IsAutoUpdate) return;
        if (!this.Symbol) return;

        var self = this;
        var marketStatus=MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol);
        if (marketStatus==0 || marketStatus==3) return; //闭市,盘后

        var frequency=this.AutoUpdateFrequency;
        if (marketStatus==1) //盘前
        {
            this.AutoUpdateTimer=setTimeout(function() 
            { 
                self.AutoUpdate(); 
            },frequency);
        }
        else if (marketStatus==2) //盘中
        {
            this.AutoUpdateTimer=setTimeout(function()
            {
                if (ChartData.IsDayPeriod(self.Period,true))
                {
                    self.RequestRealtimeData();               //更新最新行情
                    //self.ReqeustKLineInfoData();
                }
                else if (ChartData.IsMinutePeriod(self.Period,true) || ChartData.IsSecondPeriod(self.Period))
                {
                    self.RequestMinuteRealtimeData();         //请求分钟数据
                }  
                else if (ChartData.IsTickPeriod(self.Period))
                {
                    self.RequestTickRealtimeData();           //请求最新分笔
                }
            },frequency);
        }
    }

    this.GetMaxMinPageSize = function () 
    {
        let pageSize={};
        let width = this.Frame.ChartBorder.GetWidth();
        let barWidth = (ZOOM_SEED[ZOOM_SEED.length - 1][0] + ZOOM_SEED[ZOOM_SEED.length - 1][1]);
        pageSize.Max=parseInt(width / barWidth) - 2;

        barWidth= (ZOOM_SEED[0][0] + ZOOM_SEED[0][1]);
        pageSize.Min=parseInt(width / barWidth) - 2;

        JSConsole.Chart.Log(`[KLineChartContainer::GetMaxMinPageSize] Max=${pageSize.Max} Min=${pageSize.Min}`);

        return pageSize;
    }

    //获取图形控件的状态
    this.GetChartStatus=function()
    {
        var subFrame=this.Frame.SubFrame[0].Frame;
        if (!subFrame) return null;
        var hisData=subFrame.Data;
        if (!hisData) return null;

        var status={ KLine:{ }, Zoom:{} };
        status.KLine.Count=hisData.Data.length;
        status.KLine.Offset=hisData.DataOffset;
        status.KLine.PageSize=subFrame.XPointCount;
        status.Zoom.Index=subFrame.ZoomIndex;
        status.Zoom.Max=ZOOM_SEED.length;
        return status;
    }

    //数据拖拽下载
    this.DragDownloadData=function()
    {
        var data=null;
        if (!this.Frame.Data) data=this.Frame.Data;
        else data=this.Frame.SubFrame[0].Frame.Data;
        if (!data) return false;
        if (data.DataOffset>0) return;

        if (ChartData.IsMinutePeriod(this.Period,true)) //下载分钟数据
        {
            JSConsole.Chart.Log(`[KLineChartContainer.DragDownloadData] Minute:[Enable=${this.DragDownload.Minute.Enable}, IsEnd=${this.DragDownload.Minute.IsEnd}, Status=${this.DragDownload.Minute.Status}]`);
            if (!this.DragDownload.Minute.Enable) return;
            if (this.DragDownload.Minute.IsEnd) return; //全部下载完了
            if (this.DragDownload.Minute.Status!=0) return;
            this.RequestDragMinuteData();
        }
        else if (ChartData.IsDayPeriod(this.Period,true))
        {
            JSConsole.Chart.Log(`[KLineChartContainer.DragDownloadData] Day:[Enable=${this.DragDownload.Minute.Enable}, IsEnd=${this.DragDownload.Minute.IsEnd}, Status=${this.DragDownload.Minute.Status}]`);
            if (!this.DragDownload.Day.Enable) return;
            if (this.DragDownload.Day.IsEnd) return; //全部下载完了
            if (this.DragDownload.Day.Status!=0) return;
            this.RequestDragDayData();
        }
    }

    this.RequestDragMinuteData=function()
    {
        var self=this;
        this.AutoUpdateEvent(false,'KLineChartContainer::RequestDragMinuteData');   //停止自动更新
        this.CancelAutoUpdate();
        var download=this.DragDownload.Minute;
        download.Status=1;
        var firstItem=this.SourceData.Data[0];   //最新的一条数据
        var postData=
        {
            "field": ["name","symbol", "yclose","open","price","high","low","vol"],
            "symbol": self.Symbol,
            "enddate": firstItem.Date,
            "endtime" :firstItem.Time,
            "count": self.MaxRequestMinuteDayCount,
            "first":{ date: firstItem.Date, time:firstItem.Time }
        };

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestDragMinuteData', //类名::函数
                Explain:'拖拽1分钟K线数据下载',
                Request:{ Url:this.DragMinuteKLineApiUrl,  Type:'POST' , Data: postData  }, 
                DragDownload:download,
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            {
                self.RecvDragMinuteData(data);
                download.Status=0;
                self.AutoUpdateEvent(true,'KLineChartContainer::RequestDragMinuteData');   //自动更新
                self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: this.DragMinuteKLineApiUrl,
            data:postData,
            type:"post",
            dataType: "json",
            async:true,
            success: function(data)
            {
                self.RecvDragMinuteData(data);
                download.Status=0;
                self.AutoUpdateEvent(true,'KLineChartContainer::RequestDragMinuteData');   //自动更新
                self.AutoUpdate();
            }
        });
    }

    this.RecvDragMinuteData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToMinuteHistoryData(data);
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数

        for(var i in aryDayData)    //数据往前插
        {
            var item=aryDayData[i];
            this.SourceData.Data.splice(i,0,item);
        }

        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;
        bindData.Symbol=this.Symbol;

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.UpdateMainData(bindData,lastDataCount);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.RequestDragDayData=function()
    {
        var self=this;
        this.AutoUpdateEvent(false,'KLineChartContainer::RequestDragDayData');   //停止自动更新
        this.CancelAutoUpdate();
        var download=this.DragDownload.Day;
        download.Status=1;
        var firstItem=this.SourceData.Data[0];   //最新的一条数据
        var postData=
        {
            "field": ["name","symbol", "yclose","open","price","high","low","vol"],
            "symbol": self.Symbol,
            "enddate": firstItem.Date,
            "count": self.MaxReqeustDataCount,
            "first":{ date: firstItem.Date }
        };

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'KLineChartContainer::RequestDragDayData', //类名::函数
                Explain:'拖拽日K数据下载',
                Request:{ Url:this.DragKLineApiUrl,  Type:'POST' , Data: postData  }, 
                DragDownload:download,
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            {
                self.RecvDragDayData(data);
                download.Status=0;
                self.AutoUpdateEvent(true,'KLineChartContainer::RequestDragDayData');   //自动更新
                self.AutoUpdate();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: this.DragKLineApiUrl,
            data:postData,
            type:"post",
            dataType: "json",
            async:true,
            success: function(data)
            {
                self.RecvDragDayData(data);
                download.Status=0;
                self.AutoUpdateEvent(true,'KLineChartContainer::RequestDragDayData');   //自动更新
                self.AutoUpdate();
            }
        });
    }

    this.RecvDragDayData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToHistoryData(data);
        if (aryDayData.length<=0)
        {
            this.DragDownload.Day.IsEnd=true;   //下完了
            return;
        }
        var lastDataCount=this.GetHistoryDataCount();   //保存下上一次的数据个数

        for(var i in aryDayData)    //数据往前插
        {
            var item=aryDayData[i];
            this.SourceData.Data.splice(i,0,item);
        }

        var bindData=new ChartData();
        bindData.Data=this.SourceData.Data;
        bindData.Period=this.Period;
        bindData.Right=this.Right;
        bindData.DataType=this.SourceData.DataType;
        bindData.Symbol=this.Symbol;

        if (ChartData.IsDayPeriod(bindData.Period,false) || ChartData.IsMinutePeriod(bindData.Period,false))   //周期数据 (0= 日线,4=1分钟线 不需要处理)
        {
            var periodData=bindData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.UpdateMainData(bindData,lastDataCount);
        this.BindInstructionIndexData(bindData);    //执行指示脚本

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.SetCustomVerical=function(windowId, data)
    {
        if (!this.Frame) return;
        if (windowId>=this.Frame.SubFrame.length) return;

        var item=this.Frame.SubFrame[windowId];
        if (item.Frame) item.Frame.CustomVerticalInfo=data;
    }

}

//API 返回数据 转化为array[]
KLineChartContainer.JsonDataToHistoryData=function(data)
{
    var aryDayData=[];
    if (!data.data) return aryDayData;
    var list = data.data;
    var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7;
    for (var i = 0; i < list.length; ++i)
    {
        var item = new HistoryData();

        item.Date = list[i][date];
        item.Open = list[i][open];
        item.YClose = list[i][yclose];
        item.Close = list[i][close];
        item.High = list[i][high];
        item.Low = list[i][low];
        item.Vol = list[i][vol];    //原始单位股
        item.Amount = list[i][amount];

        if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除

        aryDayData.push(item);
    }

    return aryDayData;
}

KLineChartContainer.JsonDataToRealtimeData=function(data, symbol)
{
    if (!data.stock) return null;

    var stock;
    for(var i in data.stock)    //查找对应的股票数据
    {
        var stockItem=data.stock[i];
        if (stockItem && stockItem.symbol==symbol)
        {
            stock=stockItem;
            break;
        }
    }
    if (!stock) return null;

    var upperSymbol=symbol.toUpperCase();
    var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol);

    var item=new HistoryData();
    item.Date=stock.date;
    item.Open=stock.open;
    item.YClose=stock.yclose;
    item.High=stock.high;
    item.Low=stock.low;
    if (isSHSZ) item.Vol=stock.vol/100; //原始单位股
    else item.Vol=stock.vol;
    item.Amount=stock.amount;
    item.Close=stock.price;
    return item;
}

KLineChartContainer.JsonDataToMinuteRealtimeData=function(data,symbol)
{
    if (!data.stock) return null;
    var stock;
    for(var i in data.stock)    //查找对应的股票数据
    {
        var stockItem=data.stock[i];
        if (stockItem && stockItem.symbol==symbol)
        {
            stock=stockItem;
            break;
        }
    }
    if (!stock) return null;

    var upperSymbol=symbol.toUpperCase();
    var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol);
    var isFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol);
    var aryMinuteData=new Array();
    var preClose=stock.yclose;      //前一个数据价格
    var date=stock.date;
    if (isFutures && stock.yclearing)preClose=stock.yclearing;  //期货使用昨结算价
    
    for(var i in stock.minute)
    {
        var jsData=stock.minute[i];
        var item = new HistoryData();

        item.Close=jsData.price;
        item.Open=jsData.open;
        item.High=jsData.high;
        item.Low=jsData.low;
        if (isSHSZ) item.Vol=jsData.vol/100; //沪深股票原始单位股
        else item.Vol=jsData.vol;
        item.Amount=jsData.amount;
        if (jsData.date>0) item.Date=jsData.date;
        else item.Date=date;
        item.Time=jsData.time;
        item.YClose=preClose;

        if (!item.Close) //当前没有价格 使用上一个价格填充
        {
            item.Close=preClose;
            item.Open=item.High=item.Low=item.Close;
        }

        //价格是0的 都用空
        if (item.Open<=0) item.Open=null;
        if (item.Close<=0) item.Close=null;
        if (item.High<=0) item.High=null;
        if (item.Low<=0) item.Low=null;

        //上次价格
        if (jsData.price>0) preClose=jsData.price;

        aryMinuteData[i]=item;
    }

    return aryMinuteData;
}

//API 返回数据 转化为array[]
KLineChartContainer.JsonDataToMinuteHistoryData=function(data)
{
    var upperSymbol=null;
    if (data.symbol) upperSymbol=data.symbol.toUpperCase();
    var isSHSZ=false;
    if (upperSymbol) isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol);
    var list = data.data;
    var aryDayData=new Array();
    var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8;
    for (var i = 0; i < list.length; ++i)
    {
        var item = new HistoryData();

        item.Date = list[i][date];
        item.Open = list[i][open];
        item.YClose = list[i][yclose];
        item.Close = list[i][close];
        item.High = list[i][high];
        item.Low = list[i][low];
        if (isSHSZ) item.Vol = list[i][vol]/100;    //原始单位股
        else item.Vol = list[i][vol];
        item.Amount = list[i][amount];
        item.Time=list[i][time];

       // if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除

        aryDayData.push(item);
    }
    // 无效数据处理
    for(var i = 0; i < aryDayData.length; ++i)
    {
        var minData = aryDayData[i];
        if (minData == null) coninue;
        if (isNaN(minData.Open) || minData.Open <= 0 || isNaN(minData.High) || minData.High <= 0 || isNaN(minData.Low) || minData.Low <= 0 
            || isNaN(minData.Close) || minData.Close <= 0 )
        {
            if (i == 0)
            {
                if (minData.YClose > 0)
                {
                    minData.Open = minData.YClose;
                    minData.High = minData.YClose;
                    minData.Low = minData.YClose;
                    minData.Close = minData.YClose;
                }
            }
            else // 用前一个有效数据填充
            {
                for(var j = i-1; j >= 0; --j)
                {
                    var minData2 = aryDayData[j];
                    if (minData2 == null) coninue;
                    if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0)
                    {
                        if (minData.YClose <= 0) minData.YClose = minData2.Close;
                        minData.Open = minData2.Open;
                        minData.High = minData2.High;
                        minData.Low = minData2.Low;
                        minData.Close = minData2.Close;
                        break;
                    }
                }
            }    
        }
    }
    return aryDayData;
}

KLineChartContainer.JsonDataToTickData=function(data)
{
    /* 历史分笔数据格式
    var aryDayData=[];
    if (!data.deal || !data.day) return aryDayData;
    var time=data.deal.time;
    if (!time) return aryDayData;

    var date=data.date;
    var amount=data.deal.amount;
    var flag=data.deal.flag;
    var price=data.deal.price;
    var vol=data.deal.vol;
    var yClose=data.day.yclose;
    for(var i in time)
    {
        var item = new HistoryData();

        item.Date = date;
        item.Low=item.High=item.Close=item.Open = price[i];
        item.YClose = yClose;
        item.Vol = vol[i];    //原始单位股
        item.Amount = amount[i];
        item.Flag=flag[i];
        item.Time=time[i];
        if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除

        yClose=item.Close;
        aryDayData.push(item);
    }

    return aryDayData;
    */

   var aryDayData=[];
   if (!data.detail) return aryDayData;

   var date=data.date;
   var yClose=data.yclose;
   for(var i in data.detail)
   {
       var item = new HistoryData();
       var tick=data.detail[i];
       if (!tick) continue;

       item.Date = date;
       item.Time=tick[0];
       item.Low=item.High=item.Close=item.Open = tick[1];
       item.YClose = yClose;
       item.Vol = tick[2];    //原始单位股
       item.Amount = tick[3];
       item.Flag=tick[4];
      
       if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除

       yClose=item.Close;
       aryDayData.push(item);
   }

   return aryDayData;
}


///////////////////////////////////////////////////////////////////////////////////////////
//  走势图
//
function MinuteChartContainer(uielement)
{
    this.newMethod=JSChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.ClassName='MinuteChartContainer';
    this.WindowIndex=new Array();
    this.Symbol;
    this.Name;
    this.SourceData;                          //原始的历史数据
    this.IsAutoUpdate=true;                   //是否自动更新行情数据
    this.AutoUpdateFrequency=30000;           //30秒更新一次数据
    this.AutoUpdateTimer;                     //自动更新定时器
    this.TradeDate=0;                         //行情交易日期
    this.DayCount=1;                          //显示几天的数据
    this.DayData;                             //多日分钟数据
    this.LimitPrice;                          //涨停价格 { Max:null, Min:null };  

    this.IsShowBeforeData=false;              //是否显示盘前数据 (当日)
    this.BeforeData=null;                     //盘前数据
    this.IsBeforeData=false;                  //是否支持显示盘前数据

    this.IsShowLead=true;                     //指数是否显示领先指标

    this.MinuteApiUrl=g_JSChartResource.Domain+"/API/Stock";
    this.HistoryMinuteApiUrl=g_JSChartResource.Domain+'/API/StockMinuteData';  //历史分钟数据

    this.StopAutoUpdate=function()
    {
        this.CancelAutoUpdate();
        if (!this.IsAutoUpdate) return;
        this.IsAutoUpdate=false;
    }

    //手势
    this.OnTouchStart=function(e)
    {
        if(this.DragMode==0) return;

        this.IsOnTouch=true;
        this.TouchDrawCount=0;
        this.PhonePinch=null;
        this.StopDragTimer();

        if (this.EnableScrollUpDown==false) e.preventDefault();  //上下拖动图形不能阻止事件

        if (this.IsPhoneDragging(e))
        {
            var drag= { Click:{}, LastMove:{} };//LastMove=最后移动的位置
            var touches=this.GetToucheData(e,this.IsForceLandscape);

            drag.Click.X=touches[0].clientX;
            drag.Click.Y=touches[0].clientY;
            drag.LastMove.X=touches[0].clientX;
            drag.LastMove.Y=touches[0].clientY;
            var self=this;

            var T_ShowCorssCursor=function() //临时函数(Temp_) T_开头
            {
                if (self.ChartCorssCursor.IsShow === true)    //移动十字光标
                {
                    var pixelTatio = GetDevicePixelRatio();
                    var x = drag.Click.X-uielement.getBoundingClientRect().left*pixelTatio;
                    var y = drag.Click.Y-uielement.getBoundingClientRect().top*pixelTatio;
                    self.OnMouseMove(x, y, e);
                }
            }

            if (this.EnableScrollUpDown==true)
            {
                this.DragTimer=setTimeout(function()
                {
                    if (drag.Click.X==drag.LastMove.X && drag.Click.Y==drag.LastMove.Y)
                    {
                        var mouseDrag=self.MouseDrag;
                        self.MouseDrag=null;
                        T_ShowCorssCursor();
                        self.PreventTouchEvent(e)
                    }
                }, 800);
            }

            this.MouseDrag=drag;
            
            this.SelectChartDrawPicture=null;

            if (this.EnableScrollUpDown==false)
                T_ShowCorssCursor();    //移动十字光标
        }
    }

    this.OnTouchMove=function(e)
    {
        var touches=this.GetToucheData(e,this.IsForceLandscape);
        if (this.IsPhoneDragging(e))
        {
            var drag=this.MouseDrag;
            if (drag==null)
            {
                var pixelTatio = GetDevicePixelRatio();
                var x = touches[0].clientX-uielement.getBoundingClientRect().left*pixelTatio;
                var y = touches[0].clientY-uielement.getBoundingClientRect().top*pixelTatio;
                this.OnMouseMove(x,y,e);
            }
            else
            {
                var moveAngle=this.GetMoveAngle(drag.LastMove,{X:touches[0].clientX, Y:touches[0].clientY});
                var moveSetp=Math.abs(drag.LastMove.X-touches[0].clientX);
                var moveUpDown=Math.abs(drag.LastMove.Y-touches[0].clientY);
                moveSetp=parseInt(moveSetp);

                //上下滚动
                if ( ((moveUpDown>0 && moveSetp<=3) || moveAngle<=this.TouchMoveMinAngle) && this.EnableScrollUpDown==true ) 
                {
                    this.StopDragTimer();
                    return;
                }

                this.PreventTouchEvent(e);
                this.MouseDrag=null;
                var pixelTatio = GetDevicePixelRatio();
                var x = touches[0].clientX-uielement.getBoundingClientRect().left*pixelTatio;
                var y = touches[0].clientY-uielement.getBoundingClientRect().top*pixelTatio;
                this.OnMouseMove(x,y,e);
            }
        }

        if (this.EnableScrollUpDown==false)
        {
            e.preventDefault();
        }
        else
        {
            if (drag==null) 
            {
                this.PreventTouchEvent(e); //十字光标出来了,阻止消息
            }  
            else 
            {
                this.StopDragTimer();   //上下推动图片,停止定时器,消息传递下去
            }
        }
    }

    this.OnTouchEnd=function(e)
    {
        JSConsole.Chart.Log('[MinuteChartContainer::OnTouchEnd]',e);
        this.IsOnTouch = false;
        this.StopDragTimer();
        this.OnTouchFinished();
        this.TouchDrawCount=0;
    }

    
    //键盘左右移动十字光标
    this.OnKeyDown=function(e)
    {
        var keyID = e.keyCode ? e.keyCode :e.which;
        switch(keyID)
        {
            case 37: //left
                this.CursorIndex=parseInt(this.CursorIndex);
                if (this.CursorIndex<=0.99999)
                {
                    if (!this.DataMoveLeft()) break;
                    this.UpdataDataoffset();
                    this.UpdatePointByCursorIndex();
                    this.UpdateFrameMaxMin();
                    this.Draw();
                }
                else
                {
                    --this.CursorIndex;
                    this.UpdatePointByCursorIndex();
                    this.DrawDynamicInfo();
                }
                break;
            case 39: //right
                var xPointcount=0;
                if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount;
                else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount;
                this.CursorIndex=parseInt(this.CursorIndex);
                if (this.CursorIndex+1>=xPointcount)
                {
                    if (!this.DataMoveRight()) break;
                    this.UpdataDataoffset();
                    this.UpdatePointByCursorIndex();
                    this.UpdateFrameMaxMin();
                    this.Draw();
                }
                else
                {
                    //判断是否在最后一个数据上
                    var data=null;
                    if (this.Frame.Data) data=this.Frame.Data;
                    else data=this.Frame.SubFrame[0].Frame.Data;
                    if (!data) break;
                    if (this.CursorIndex+data.DataOffset+1>=data.Data.length) break;

                    ++this.CursorIndex;
                    this.UpdatePointByCursorIndex();
                    this.DrawDynamicInfo();
                }
                break;
            default:
                return;
        }

        //不让滚动条滚动
        if(e.preventDefault) e.preventDefault();
        else e.returnValue = false;
    }

    //注册鼠标右键事件
    this.OnRightMenu=function(x,y,e)
    {
        if (this.RightMenu)
        {
            var frameId=this.Frame.PtInFrame(x,y);
            e.data={ Chart:this, FrameID:frameId };
            this.RightMenu.DoModal(e);
        }

        var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CONTEXT_MENU);
        if (event)
        {
            var frameId=this.Frame.PtInFrame(x,y);
            var data={ X:x, Y:y, Event:e, FrameID:frameId };
            event.Callback(event,data,this);
        }
    }

    this.UpdatePointByCursorIndex=function()
    {
        this.LastPoint.X=this.Frame.GetXFromIndex(this.CursorIndex);

        var index=this.CursorIndex;
        index=parseInt(index.toFixed(0));
        var data=this.Frame.SourceData;
        if (data.DataOffset+index>=data.Data.length)
        {
            return;
        }
        var item=data.Data[data.DataOffset+index];
        var close=null;
        if (item.Before) close=item.Before.Close;
        else close=item.Close

        this.LastPoint.Y=this.Frame.GetYFromData(close);
    }



    //创建
    //windowCount 窗口个数
    this.Create=function(windowCount)
    {
        this.UIElement.JSChartContainer=this;

        //创建十字光标
        this.ChartCorssCursor=new ChartCorssCursor();
        this.ChartCorssCursor.Canvas=this.Canvas;
        this.ChartCorssCursor.StringFormatX=new HQMinuteTimeStringFormat();
        this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat();
        this.ChartCorssCursor.StringFormatY.LanguageID=this.LanguageID;

        //创建等待提示
        this.ChartSplashPaint = new ChartSplashPaint();
        this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new HQTradeFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=25;
        this.Frame.ChartBorder.Left=50;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;
        this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架
        this.ChartSplashPaint.Frame = this.Frame;

        this.CreateChildWindow(windowCount);
        this.CreateMainKLine();

        //子窗口动态标题
        for(var i in this.Frame.SubFrame)
        {
            var titlePaint=new DynamicChartTitlePainting();
            titlePaint.Frame=this.Frame.SubFrame[i].Frame;
            titlePaint.Canvas=this.Canvas;
            titlePaint.LanguageID=this.LanguageID;

            this.TitlePaint.push(titlePaint);
        }

        this.ChartCorssCursor.StringFormatX.Frame=this.Frame.SubFrame[0].Frame;

        this.UIElement.addEventListener("keydown", OnKeyDown, true);    //键盘消息
    }

    //创建子窗口
    this.CreateChildWindow=function(windowCount)
    {
        for(var i=0;i<windowCount;++i)
        {
            var border=new ChartBorder();
            border.UIElement=this.UIElement;

            var frame=new MinuteFrame();
            frame.Canvas=this.Canvas;
            frame.ChartBorder=border;
            if (i<2) frame.ChartBorder.TitleHeight=0;
            frame.XPointCount=243;

            var DEFAULT_HORIZONTAL=[9,8,7,6,5,4,3,2,1];
            frame.HorizontalMax=DEFAULT_HORIZONTAL[0];
            frame.HorizontalMin=DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length-1];

            if (i==0)
            {
                frame.YSplitOperator=new FrameSplitMinutePriceY();
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('price');
            }
            else
            {
                frame.YSplitOperator=new FrameSplitY();
                frame.YSplitOperator.LanguageID=this.LanguageID;
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
            }

            frame.YSplitOperator.Frame=frame;
            frame.YSplitOperator.ChartBorder=border;
            frame.XSplitOperator=new FrameSplitMinuteX();
            frame.XSplitOperator.Frame=frame;
            frame.XSplitOperator.ChartBorder=border;
            if (i!=windowCount-1) frame.XSplitOperator.ShowText=false;
            frame.XSplitOperator.Operator();

            for(var j in DEFAULT_HORIZONTAL)
            {
                frame.HorizontalInfo[j]= new CoordinateInfo();
                frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j];
                if (i==0 && j==frame.HorizontalMin) continue;

                frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString();
                frame.HorizontalInfo[j].Font="14px 微软雅黑";
            }

            var subFrame=new SubFrameItem();
            subFrame.Frame=frame;
            if (i==0)
                subFrame.Height=20;
            else
                subFrame.Height=10;

            this.Frame.SubFrame[i]=subFrame;
        }
    }

    this.CreateSubFrameItem=function(id)
    {
        var border=new ChartBorder();
        border.UIElement=this.UIElement;

        var frame=new MinuteFrame();
        frame.Canvas=this.Canvas;
        frame.ChartBorder=border;
        frame.Identify=id;                   //窗口序号
        frame.XPointCount=243;

        var DEFAULT_HORIZONTAL=[9,8,7,6,5,4,3,2,1];
        frame.HorizontalMax=DEFAULT_HORIZONTAL[0];
        frame.HorizontalMin=DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length-1];

        frame.YSplitOperator=new FrameSplitY();
        frame.YSplitOperator.LanguageID=this.LanguageID;
        frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
        frame.YSplitOperator.Frame=frame;
        frame.YSplitOperator.ChartBorder=border;
        frame.XSplitOperator=new FrameSplitMinuteX();
        frame.XSplitOperator.Frame=frame;
        frame.XSplitOperator.ChartBorder=border;
        frame.XSplitOperator.ShowText=false;

        //K线数据绑定
        var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount;
        frame.XPointCount=xPointCouont;
        frame.Data=this.ChartPaint[0].Data;

        for(var j in DEFAULT_HORIZONTAL)
        {
            frame.HorizontalInfo[j]= new CoordinateInfo();
            frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j];
            frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString();
            frame.HorizontalInfo[j].Font="14px 微软雅黑";
        }

        var subFrame=new SubFrameItem();
        subFrame.Frame=frame;
        subFrame.Height=10;

        return subFrame;
    }

    //删除某一个窗口的指标
    this.DeleteIndexPaint=function(windowIndex)
    {
        let paint=new Array();          //踢出当前窗口的指标画法
        for(let i in this.ChartPaint)
        {
            let item=this.ChartPaint[i];

            if (i==0 || item.ChartFrame!=this.Frame.SubFrame[windowIndex].Frame)
                paint.push(item);
        }

        //清空指定最大最小值
        this.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=null;
        this.Frame.SubFrame[windowIndex].Frame.IsLocked=false;          //解除上锁

        this.ChartPaint=paint;

         //清空东条标题
         var titleIndex=windowIndex+1;
         this.TitlePaint[titleIndex].Data=[];
         this.TitlePaint[titleIndex].Title=null;
    }

    this.CreateStockInfo=function()
    {
        this.ExtendChartPaint[0]=new StockInfoExtendChartPaint();
        this.ExtendChartPaint[0].Canvas=this.Canvas;
        this.ExtendChartPaint[0].ChartBorder=this.Frame.ChartBorder;
        this.ExtendChartPaint[0].ChartFrame=this.Frame;

        this.Frame.ChartBorder.Right=300;
    }

    //创建主图K线画法
    this.CreateMainKLine=function()
    {
        //分钟线
        var minuteLine=new ChartMinutePriceLine();
        minuteLine.Canvas=this.Canvas;
        minuteLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        minuteLine.ChartFrame=this.Frame.SubFrame[0].Frame;
        minuteLine.Name="Minute-Line";
        minuteLine.Color=g_JSChartResource.Minute.PriceColor;
        minuteLine.AreaColor=g_JSChartResource.Minute.AreaPriceColor;

        this.ChartPaint[0]=minuteLine;

        //分钟线均线
        var averageLine=new ChartMinutePriceLine();
        averageLine.Canvas=this.Canvas;
        averageLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        averageLine.ChartFrame=this.Frame.SubFrame[0].Frame;
        averageLine.Name="Minute-Average-Line";
        averageLine.Color=g_JSChartResource.Minute.AvPriceColor;
        averageLine.IsDrawArea=false;
        this.ChartPaint[1]=averageLine;

        //成交量
        var volLine=new ChartMinuteVolumBar();  
        volLine.Color=g_JSChartResource.Minute.VolBarColor;
        volLine.Canvas=this.Canvas;
        volLine.ChartBorder=this.Frame.SubFrame[1].Frame.ChartBorder;
        volLine.ChartFrame=this.Frame.SubFrame[1].Frame;
        volLine.Name="Minute-Vol-Bar";
        this.ChartPaint[2]=volLine;

        this.TitlePaint[0]=new DynamicMinuteTitlePainting();
        this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame;
        this.TitlePaint[0].Canvas=this.Canvas;
        this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint;    //绑定叠加
        this.TitlePaint[0].LanguageID=this.LanguageID;
    }

    //切换成 脚本指标
    this.ChangeScriptIndex=function(windowIndex,indexData)
    {
        this.DeleteIndexPaint(windowIndex);
        this.WindowIndex[windowIndex]=new ScriptIndex(indexData.Name,indexData.Script,indexData.Args,indexData);    //脚本执行

        var bindData=this.SourceData;
        this.BindIndexData(windowIndex,bindData);   //执行脚本

        this.UpdataDataoffset();           //更新数据偏移
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    this.ChangeIndex=function(windowIndex,indexName,option)
    {
        if (this.Frame.SubFrame.length<3) return;

        //查找系统指标
        let scriptData = new JSIndexScript();
        let indexInfo = scriptData.Get(indexName);
        if (!indexInfo) return;
        if (windowIndex<2) windowIndex=2;
        if (windowIndex>=this.Frame.SubFrame.length) windowIndex=2;

        let indexData = 
        { 
            Name:indexInfo.Name, Script:indexInfo.Script, Args: indexInfo.Args, ID:indexName ,
            //扩展属性 可以是空
            KLineType:indexInfo.KLineType,  YSpecificMaxMin:indexInfo.YSpecificMaxMin,  YSplitScale:indexInfo.YSplitScale,
            FloatPrecision:indexInfo.FloatPrecision, Condition:indexInfo.Condition,StringFormat:indexInfo.StringFormat
        };

        if (option)
        {
            if (option.FloatPrecision>=0) indexData.FloatPrecision=option.FloatPrecision;
            if (option.StringFormat>0) indexData.StringFormat=option.StringFormat;
            if (option.Args) indexData.Args=option.Args;
        }
        
        return this.ChangeScriptIndex(windowIndex, indexData);
    }

    //设置指标窗口个数
    this.ChangeIndexWindowCount=function(count)
    {
        if (count<2) return;   //1,2个窗口固定的不能动
        if (this.Frame.SubFrame.length==count) return;

        var currentLength=this.Frame.SubFrame.length;
        if (currentLength>count)
        {
            for(var i=currentLength-1;i>=count;--i)
            {
                this.DeleteIndexPaint(i);
                var item=this.Frame.SubFrame[i].Frame;
                if (item.ClearToolbar) item.ClearToolbar();
            }

            this.Frame.SubFrame.splice(count,currentLength-count);
            this.WindowIndex.splice(count,currentLength-count);
        }
        else
        {
            //创建新的指标窗口
            for(var i=currentLength;i<count;++i)
            {
                var subFrame=this.CreateSubFrameItem(i);
                this.Frame.SubFrame[i]=subFrame;
                var titlePaint=new DynamicChartTitlePainting();
                titlePaint.Frame=this.Frame.SubFrame[i].Frame;
                titlePaint.Canvas=this.Canvas;
                titlePaint.LanguageID=this.LanguageID;
                this.TitlePaint[i+1]=titlePaint;
            }

            //创建指标
            const indexName=["RSI","MACD","DMA","DMI","KDJ","WR"];
            let scriptData = new JSIndexScript();
            for(var i=currentLength;i<count;++i)
            {
                var name=indexName[i%indexName.length];
                let indexInfo = scriptData.Get(name);
                this.WindowIndex[i] = new ScriptIndex(indexInfo.Name, indexInfo.Script, indexInfo.Args,indexInfo);    //脚本执行
                var bindData=this.SourceData;
                this.BindIndexData(i,bindData);   //执行脚本
            }

            //最后一个显示X轴坐标
            for(var i=0;i<this.Frame.SubFrame.length;++i)
            {
                var item=this.Frame.SubFrame[i].Frame;
                if (i==this.Frame.SubFrame.length-1) item.XSplitOperator.ShowText=true;
                else item.XSplitOperator.ShowText=false;
            }

            this.UpdataDataoffset();           //更新数据偏移
        }

        this.Frame.SetSizeChage(true);
        this.ResetFrameXYSplit();
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Draw();
    }

    //切换股票代码
    this.ChangeSymbol=function(symbol)
    {
        this.CancelAutoUpdate();
        this.Symbol=symbol;
        this.ResetOverlaySymbolStatus();

        this.ChartSplashPaint.IsEnableSplash = true;    //增加下载动画
        this.Draw();
        this.RequestData();
    }

    this.ChangeDayCount=function(count)
    {
        if (count<0 || count>10) return;
        this.DayCount=count;

        this.ResetOverlaySymbolStatus();
        this.RequestData();
    }

    //叠加股票
    this.OverlaySymbol=function(symbol,option)
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (item.Symbol==symbol)
            {
                console.warn(`[MinuteChartContainer::OverlaySymbol] overlay symbol=${symbol} exist.`);
                return false;
            }
        }

        var paint=new ChartOverlayMinutePriceLine();
        paint.Canvas=this.Canvas;
        paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        paint.ChartFrame=this.Frame.SubFrame[0].Frame;
        paint.Name="Overlay-Minute";
        paint.Symbol=symbol;
        if (option && option.Color) paint.Color=option.Color; //外部设置颜色
        else paint.Color=g_JSChartResource.OverlaySymbol.Color[g_JSChartResource.OverlaySymbol.Random%g_JSChartResource.OverlaySymbol.Color.length];
        ++g_JSChartResource.OverlaySymbol.Random;
        if (this.ChartPaint[0].YClose>0 && this.ChartPaint[0].Data) //绑定主图数据
        {
            paint.MainData=this.ChartPaint[0].Data; 
            paint.MainYClose=this.ChartPaint[0].YClose;        
        }

        this.OverlayChartPaint.push(paint);

        if (this.DayCount<=1) this.RequestOverlayMinuteData();               //请求数据
        else this.RequestOverlayHistoryMinuteData();
        
        return true;
    }

    this.ResetOverlaySymbolStatus=function()
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            item.Status=OVERLAY_STATUS_ID.STATUS_NONE_ID;
        }
    }

    //删除一个叠加股票
    this.DeleteOverlaySymbol=function(symbol)
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            if (item.Symbol===symbol)
            {
                item.IsDelete=true;
                this.OverlayChartPaint.splice(i,1);
                this.UpdateFrameMaxMin();
                this.Draw();
                return true;
            }
        }

        console.warn(`[MinuteChartContainer::DeleteOverlaySymbol] overlay symbol=${symbol} not exist.`)
        return false;
    }

    //取消叠加股票
    this.ClearOverlaySymbol=function()
    {
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            item.IsDelete=true;
        }
        this.OverlayChartPaint=[];
        this.Frame.SubFrame[0].Frame.YSplitOperator.OverlayChartPaint=this.OverlayChartPaint;
        this.TitlePaint[0].OverlayChartPaint=this.OverlayChartPaint;    //绑定叠加
        this.UpdateFrameMaxMin();
        this.Draw();
    }

    this.ShowBeforeData=function(isShow)
    {
        if (this.IsShowBeforeData==isShow) return;

        this.IsShowBeforeData=isShow;
        this.RequestData();
    }

    this.RequestData=function()
    {
        if (this.DayCount<=1) this.RequestMinuteData();               
        else this.RequestHistoryMinuteData();
    }

    this.ChangeDrawType=function(type)
    {
        if (this.ChartPaint.length<=0) return;

        if (type==0) this.ChartPaint[0].IsDrawArea=true;
        else if (type==1) this.ChartPaint[0].IsDrawArea=false;
        else return;

        this.Draw();
    } 

    //请求历史分钟数据
    this.RequestHistoryMinuteData=function()
    {
        var self=this;
        this.IsBeforeData=false;
        this.ChartSplashPaint.IsEnableSplash = true;
        this.Draw();

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'MinuteChartContainer::RequestHistoryMinuteData', //类名::函数
                Explain:'多日分时数据',
                Request:{ Url:self.HistoryMinuteApiUrl, Data:{daycount:self.DayCount, symbol:self.Symbol}, Type:'POST' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvHistoryMinuteData(data);
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: self.HistoryMinuteApiUrl,
            data:
            {
                "symbol": self.Symbol,
                "daycount": self.DayCount
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvHistoryMinuteData(data);
            }
        });
    }

    this.RecvHistoryMinuteData=function(data)
    {
        this.DayData=MinuteChartContainer.JsonDataToMinuteDataArray(data);
        this.Symbol=data.symbol;
        this.Name=data.name;

        this.CaclutateLimitPrice(this.DayData[0].YClose, data.data[0].limitprice); //计算涨停价格
        this.UpdateHistoryMinuteUI();
        this.RecvMinuteDataEvent();
        this.RequestOverlayHistoryMinuteData();
        this.AutoUpdate();
    }

    this.UpdateHistoryMinuteUI=function()
    {
        var allMinuteData=this.HistoryMinuteDataToArray(this.DayData);

        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=allMinuteData;

        this.SourceData=sourceData;
        this.TradeDate=this.DayData[0].Date;

        this.BindMainData(sourceData,this.DayData[0].YClose);
        var upperSymbol=this.Symbol.toUpperCase();
         //外汇 均线暂时不用
        if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) this.ChartPaint[1].Data=null;  

        if (this.Frame.SubFrame.length>2)
        {
            var bindData=new ChartData();
            bindData.Data=allMinuteData;
            for(var i=2; i<this.Frame.SubFrame.length; ++i)
            {
                this.BindIndexData(i,bindData);
            }
        }

        for(let i in this.Frame.SubFrame)
        {
            var item=this.Frame.SubFrame[i];
            item.Frame.XSplitOperator.Symbol=this.Symbol;
            item.Frame.XSplitOperator.DayCount=this.DayData.length;
            item.Frame.XSplitOperator.DayData=this.DayData;
            item.Frame.XSplitOperator.Operator();   //调整X轴个数
            item.Frame.XSplitOperator.IsBeforeData=this.IsBeforeData;
            item.Frame.YSplitOperator.Symbol=this.Symbol;
            item.Frame.IsBeforeData=this.IsBeforeData;

            for(var j in item.OverlayIndex) //子坐标X轴个数同步
            {
                var overlayItem=item.OverlayIndex[j];
                overlayItem.Frame.XPointCount=item.Frame.XPointCount;
                overlayItem.Frame.MinuteCount=item.Frame.MinuteCount;
            }
        }

        this.ChartCorssCursor.StringFormatY.Symbol=this.Symbol;
        this.ChartCorssCursor.StringFormatX.Symbol=this.Symbol;
        this.ChartCorssCursor.StringFormatX.IsBeforeData=this.IsBeforeData;
        this.TitlePaint[0].IsShowDate=true;
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.HistoryMinuteDataToArray=function(data)
    {
        var result=[];
        for(var i=data.length-1; i>=0;--i)
        {
            var item=data[i];
            for(var j in item.Data)
            {
                result.push(item.Data[j]);
            }
        }
        return result;
    }

    this.UpdateLatestMinuteData=function(data,date)
    {
        for(var i in this.DayData)
        {
            var item=this.DayData[i];
            if (item.Date===date)
            {
                item.Data=data;
                break;
            }
        }
    }

    //请求分钟数据
    this.RequestMinuteData=function()
    {
        var self=this;

        var fields=
        [
            "name","symbol",
            "yclose","open","price","high","low",
            "vol","amount",
            "date","time",
            "minute","minutecount"
        ];

        var upperSymbol=this.Symbol.toUpperCase();
        if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol))
        {   //期货的需要加上结算价
            fields.push("clearing");
            fields.push("yclearing");
        }

        //  盘前数据(A股)
        this.IsBeforeData=false;
        if (this.IsShowBeforeData && this.DayCount===1 && MARKET_SUFFIX_NAME.IsSHSZStockA(self.Symbol)) 
        {
            this.IsBeforeData=true;
            fields.push('before');
        }

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'MinuteChartContainer::RequestMinuteData', //类名::函数名
                Explain:'最新分时数据',
                Request:{ Url:self.MinuteApiUrl, Data:{field:fields, symbol:[self.Symbol]}, Type:'POST' }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvMinuteData(data);
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }
        
        JSNetwork.HttpRequest({
            url: self.MinuteApiUrl,
            data:
            {
                "field": fields,
                "symbol": [self.Symbol],
                "start": -1
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvMinuteData(data);
            }
        });
    }

    this.RecvMinuteDataEvent=function()
    {
        if (!this.mapEvent.has(JSCHART_EVENT_ID.RECV_MINUTE_DATA)) return;

        var event=this.mapEvent.get(JSCHART_EVENT_ID.RECV_MINUTE_DATA);
        var data={ MinuteData:this.SourceData, Stock:{Symbol:this.Symbol, Name:this.Name } }
        event.Callback(event,data,this);
    }

    this.RecvMinuteData=function(data)
    {
        if (this.IsBeforeData) 
            var aryMinuteData=MinuteChartContainer.JsonDataToMinuteData2(data);
        else
            var aryMinuteData=MinuteChartContainer.JsonDataToMinuteData(data);

        if (this.DayCount>1)    //多日走势图
        {
            this.UpdateLatestMinuteData(aryMinuteData,data.stock[0].date);
            this.UpdateHistoryMinuteUI();
            this.RecvMinuteDataEvent();
            this.RequestOverlayMinuteData();    //请求叠加数据 (主数据下载完再下载)
            this.AutoUpdate();
            return;
        }

        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=aryMinuteData;

        this.TradeDate=data.stock[0].date;

        this.SourceData=sourceData;
        this.Symbol=data.stock[0].symbol;
        this.Name=data.stock[0].name;

        var yClose=data.stock[0].yclose;
        var upperSymbol=this.Symbol.toUpperCase();
        if (data.stock[0].yclearing && MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) yClose=data.stock[0].yclearing; //期货使用前结算价
        this.CaclutateLimitPrice(yClose, data.stock[0].limitprice); //计算涨停价格
        var extendData={ High:data.stock[0].high, Low:data.stock[0].low };
        this.BindMainData(sourceData,yClose, extendData);

        if (this.Frame.SubFrame.length>2)
        {
            var bindData=new ChartData();
            bindData.Data=aryMinuteData;
            for(var i=2; i<this.Frame.SubFrame.length; ++i)
            {
                this.BindIndexData(i,bindData);
            }
        }

        for(let i in this.Frame.SubFrame)
        {
            var item=this.Frame.SubFrame[i];
            item.Frame.XSplitOperator.Symbol=this.Symbol;
            item.Frame.XSplitOperator.DayCount=1;
            item.Frame.XSplitOperator.Operator();   //调整X轴个数
            item.Frame.XSplitOperator.IsBeforeData=this.IsBeforeData;
            item.Frame.YSplitOperator.Symbol=this.Symbol;
            item.Frame.IsBeforeData=this.IsBeforeData;

            for(var j in item.OverlayIndex) //子坐标X轴个数同步
            {
                var overlayItem=item.OverlayIndex[j];
                overlayItem.Frame.XPointCount=item.Frame.XPointCount;
                overlayItem.Frame.MinuteCount=item.Frame.MinuteCount;
            }
        }

        this.ChartCorssCursor.StringFormatY.Symbol=this.Symbol;
        this.ChartCorssCursor.StringFormatX.Symbol=this.Symbol;
        this.ChartCorssCursor.StringFormatX.IsBeforeData=this.IsBeforeData;

        if (MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol)) this.TitlePaint[0].IsShowDate=false;

        var chartInfo=this.GetChartMinuteInfo();
        if (chartInfo) chartInfo.SourceData=this.SourceData;    //数据绑定到信息地雷上

        this.RecvMinuteDataEvent();
        this.RequestMinuteInfoData();
        this.RequestOverlayMinuteData();//请求叠加数据 (主数据下载完再下载))

        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
        
        this.AutoUpdate();
    }

    this.CaclutateLimitPrice=function(yClose, limitData)
    {
        this.LimitPrice=null;
        //var limitData=data.stock[0].limitprice;
        if (limitData && limitData.max>0 && limitData.min>0)    //API里带涨停价格 直接使用
        {
            this.LimitPrice={ Max:limitData.max, Min:limitData.min };
            return;
        }
        
        var range=MARKET_SUFFIX_NAME.GetLimitPriceRange(this.Symbol, this.Name);   //通过规则获取涨停价格
        if (!range) 
        {
            JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} no limit price.`)
            return;
        }

        //var yClose=data.stock[0].yclose;
        if (yClose<=0) return;
        this.LimitPrice={ Max:yClose*(1+range.Max), Min:yClose*(1+range.Min) };

        JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} yClose:${yClose} max:${this.LimitPrice.Max} min:${this.LimitPrice.Min}`);

        this.LimitPrice.Max=parseFloat(this.LimitPrice.Max.toFixed(2));
        this.LimitPrice.Min=parseFloat(this.LimitPrice.Min.toFixed(2));
        JSConsole.Chart.Log(`[MinuteChartContainer::CaclutateLimitPrice] ${this.Symbol} tofixed(2) max:${this.LimitPrice.Max} min:${this.LimitPrice.Min}`);
    }

    //请求叠加数据 (主数据下载完再下载))
    this.RequestOverlayMinuteData=function()
    {
        var self = this;
        var date=this.TradeDate;    //最后一个交易日期

        for(var i in this.OverlayChartPaint)
        {
            let item=this.OverlayChartPaint[i];
            if (!item.MainData || !(item.MainYClose>0) ) continue;
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue;
            var symbol=item.Symbol;
            if (!symbol) return;

            item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID;

            if (this.NetworkFilter)
            {
                var obj=
                {
                    Name:'MinuteChartContainer::RequestOverlayMinuteData', //类名::函数名
                    Explain:'叠加股票最新分时数据',
                    Request:{ Url:self.HistoryMinuteApiUrl, Data:{days:[date], symbol:symbol}, Type:'POST' }, 
                    Self:this,
                    PreventDefault:false
                };
                this.NetworkFilter(obj, function(data) 
                { 
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOverlayMinuteData(data,item);
                });

                if (obj.PreventDefault==true) continue;   //已被上层替换,不调用默认的网络请求
            }

            //请求数据
            JSNetwork.HttpRequest({
                url: self.HistoryMinuteApiUrl,
                data:
                {
                    "symbol":symbol,
                    "days": [date],
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOverlayMinuteData(data,item);
                }
            });
        }
    }

    this.RecvOverlayMinuteData=function(data,paint)
    {
        if (paint.IsDelete) return;

        var aryMinuteData=MinuteChartContainer.JsonDataToMinuteDataArray(data);

        var sourceData=null;
        var yClose;
        if (this.DayCount>1)    //多日数据
        {
            if (aryMinuteData.length<=0) return;

            var minuteData=aryMinuteData[0];
            for(var i in paint.SourceData)
            {
                var item=paint.SourceData[i];
                if (item.Date==minuteData.Date)
                {
                    paint.SourceData[i]=minuteData;
                    var allMinuteData=this.HistoryMinuteDataToArray(paint.SourceData);
                    var sourceData=new ChartData();
                    sourceData.Data=allMinuteData;
                    yClose=minuteData.YClose;
                    break;
                }
            }
            if (sourceData==null) return;
        }
        else
        {
            if (aryMinuteData.length>0) sourceData=aryMinuteData[0];
            else sourceData=new ChartData();
            yClose=sourceData.YClose;
        }

        paint.Data=sourceData;
        paint.Title=data.name;
        paint.Symbol=data.symbol;
        paint.YClose=yClose;
        paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID;

        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.RequestOverlayHistoryMinuteData=function()
    {
        var self = this;
        var days=[];
        for(var i in this.DayData)
        {
            var item=this.DayData[i];
            days.push(item.Date);
        }
        if (days.length<=0) return;

        for(var i in this.OverlayChartPaint)
        {
            let item=this.OverlayChartPaint[i]
            var symbol=item.Symbol;
            if (!symbol) return;
            if (item.Status!=OVERLAY_STATUS_ID.STATUS_NONE_ID) continue;

            item.Status=OVERLAY_STATUS_ID.STATUS_REQUESTDATA_ID;

            if (this.NetworkFilter)
            {
                var obj=
                {
                    Name:'MinuteChartContainer::RequestOverlayHistoryMinuteData', //类名::函数名
                    Explain:'叠加股票多日分时数据',
                    Request:{ Url:self.HistoryMinuteApiUrl, Data:{days:days, symbol:symbol}, Type:'POST' }, 
                    Self:this,
                    PreventDefault:false
                };
                this.NetworkFilter(obj, function(data) 
                { 
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOverlayHistoryMinuteData(data,item);
                });

                if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
            }

            JSNetwork.HttpRequest({
                url: self.HistoryMinuteApiUrl,
                data:{ "symbol": symbol, "days": days },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    item.Status=OVERLAY_STATUS_ID.STATUS_RECVDATA_ID;
                    self.RecvOverlayHistoryMinuteData(data,item);
                }
            });
        }
    }

    this.RecvOverlayHistoryMinuteData=function(data,paint)    //叠加历史的分钟数据
    {
        var dayData=MinuteChartContainer.JsonDataToMinuteDataArray(data);
        var overlayDayData=[];
        for(var i in this.DayData)
        {
            var item=this.DayData[i];
            var bFind=false;
            for(var j in dayData)
            {
                if (item.Date==dayData[j].Date)
                {
                    overlayDayData.push(dayData[i]);
                    bFind=true;
                    break;
                }
            }
            if (!bFind) //当天不存在叠加数据, 存空
            {
                var empytData=new ChartData();
                empytData.Date=item.Date;
            }
        }

        paint.SourceData=overlayDayData;
        var allMinuteData=this.HistoryMinuteDataToArray(overlayDayData);
        var yClose=overlayDayData[0].YClose;    //取最近一个交易日前收盘最为中轴线
        
        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=allMinuteData;

        paint.Data=sourceData;
        paint.Title=data.name;
        paint.Symbol=data.symbol;
        paint.YClose=yClose;
        paint.Status=OVERLAY_STATUS_ID.STATUS_FINISHED_ID;

        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

    this.CancelAutoUpdate=function()    //关闭停止更新
    {
        if (typeof (this.AutoUpdateTimer) == 'number') 
        {
            clearTimeout(this.AutoUpdateTimer);
            this.AutoUpdateTimer = undefined;
        }
    }

    //数据自动更新
    this.AutoUpdate=function()
    {
        this.CancelAutoUpdate();
        if (!this.IsAutoUpdate) return;
        if (!this.Symbol) return;

        var self = this;
        var marketStatus=MARKET_SUFFIX_NAME.GetMarketStatus(this.Symbol);
        if (marketStatus==0 || marketStatus==3) return; //闭市,盘后

        var frequency=this.AutoUpdateFrequency;
        if (marketStatus==1) //盘前
        {
            this.AutoUpdateTimer=setTimeout(function() 
            { 
                self.AutoUpdate(); 
            },frequency);
        }
        else if (marketStatus==2)   //盘中
        {
            this.AutoUpdateTimer=setTimeout(function()
            {
                self.ResetOverlaySymbolStatus();
                self.RequestMinuteData();
            },frequency);
        }
    }

    this.BindIndexData=function(windowIndex,hisData)
    {
        if (!this.WindowIndex[windowIndex]) return;

        if (typeof(this.WindowIndex[windowIndex].RequestData)=="function")          //数据需要另外下载的.
        {
            this.WindowIndex[windowIndex].RequestData(this,windowIndex,hisData);
            return;
        }
        if (typeof(this.WindowIndex[windowIndex].ExecuteScript)=='function')
        {
            this.WindowIndex[windowIndex].ExecuteScript(this,windowIndex,hisData);
            return;
        }

        this.WindowIndex[windowIndex].BindData(this,windowIndex,hisData);
    }

    //绑定分钟数据
    this.BindMainData=function(minuteData,yClose, extendData)
    {
        //分钟数据
        var bindData=new ChartData();
        bindData.Data=minuteData.GetClose();
        this.ChartPaint[0].Data=bindData;
        this.ChartPaint[0].YClose=yClose;
        this.ChartPaint[0].NotSupportMessage=null;
        this.ChartPaint[0].SourceData=this.IsBeforeData ? minuteData:null;
        this.ChartPaint[0].IsShowLead=false;
        this.ChartPaint[0].LeadData=null;
        if (MARKET_SUFFIX_NAME.IsSHSZIndex(this.Symbol) && this.DayCount==1 && this.IsShowLead)  //指数显示领先指标
        {
            var bindLeadData=new ChartData();
            bindLeadData.Data=minuteData.GetLead();
            this.ChartPaint[0].LeadData=bindLeadData;
            this.ChartPaint[0].IsShowLead=true;
        }
       
        this.Frame.SubFrame[0].Frame.YSplitOperator.YClose=yClose;
        this.Frame.SubFrame[0].Frame.YSplitOperator.Data=bindData;
        this.Frame.Data=this.ChartPaint[0].Data;
        this.Frame.SourceData=minuteData;

        //均线
        bindData=new ChartData();
        bindData.Data=minuteData.GetMinuteAvPrice();
        this.ChartPaint[1].Data=bindData;

        var upperSymbol=this.Symbol.toUpperCase();
        if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol))    //外汇没有均线
            this.ChartPaint[1].Data=null;

        this.Frame.SubFrame[0].Frame.YSplitOperator.AverageData=bindData;
        this.Frame.SubFrame[0].Frame.YSplitOperator.SourceData=this.IsBeforeData ? minuteData:null;
        this.Frame.SubFrame[0].Frame.YSplitOperator.OverlayChartPaint=this.OverlayChartPaint;
        this.Frame.SubFrame[0].Frame.YSplitOperator.LimitPrice=this.LimitPrice;
        if (extendData)
        {
            this.Frame.SubFrame[0].Frame.YSplitOperator.High=extendData.High;
            this.Frame.SubFrame[0].Frame.YSplitOperator.Low=extendData.Low;
        }

        //成交量
        this.ChartPaint[2].Data=minuteData;
        this.ChartPaint[2].YClose=yClose;

        if(MARKET_SUFFIX_NAME.IsChinaFutures(this.Symbol))  this.BindOverlayMainData(minuteData,yClose);    //期货持仓量
        else this.ClearBindOverlayMainData();

        this.TitlePaint[0].Data=this.SourceData;                    //动态标题
        this.TitlePaint[0].Symbol=this.Symbol;
        this.TitlePaint[0].Name=this.Name;
        this.TitlePaint[0].YClose=yClose;

        if (this.ChartCorssCursor && this.ChartCorssCursor.StringFormatY)
            this.ChartCorssCursor.StringFormatY.YClose=yClose;

        if (this.ExtendChartPaint[0])
        {
            this.ExtendChartPaint[0].Symbol=this.Symbol;
            this.ExtendChartPaint[0].Name=this.Name;
        }

        var beforeDataCount=0;
        var isBeforeData=this.IsBeforeData;
        if (this.DayCount>1) isBeforeData=false;
        if (isBeforeData) beforeDataCount=15;
        for(var i in this.OverlayChartPaint)
        {
            var item=this.OverlayChartPaint[i];
            item.MainData=this.ChartPaint[0].Data;         //叠加股票
            item.MainYClose=yClose;
            item.BeforeDataCount=beforeDataCount;           //只是集合竞价数据
            item.IsBeforeData=isBeforeData;
        }
    }

    //绑定分钟叠加指标数据(持仓量)
    this.BindOverlayMainData=function(minuteData,yClose)
    {
        if (this.Frame.SubFrame.length<2) return;

        var chart=null;
        var frame=null;
        var subFrame=this.Frame.SubFrame[1];    //第2个窗口
        if (subFrame.OverlayIndex.length<=0)
        {
            var overlayFrame=new OverlayIndexItem();
            overlayFrame.Identify='Position_Line_Frame';

            frame=new OverlayMinuteFrame();
            frame.Canvas=this.Canvas;
            frame.MainFrame=subFrame.Frame;
            frame.ChartBorder=subFrame.Frame.ChartBorder;
            overlayFrame.Frame=frame;

            frame.YSplitOperator=new FrameSplitY();
            frame.YSplitOperator.LanguageID=this.LanguageID;
            frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
            frame.YSplitOperator.Frame=frame;
            frame.YSplitOperator.ChartBorder=frame.ChartBorder;
            frame.YSplitOperator.SplitCount=subFrame.Frame.YSplitOperator.SplitCount;

            var chart=new ChartLine();
            chart.Canvas=this.Canvas
            chart.Name='Position-Line';
            chart.ChartBorder=frame.ChartBorder;
            chart.ChartFrame=frame
            chart.Identify=overlayFrame.Identify;
            chart.Color=g_JSChartResource.Minute.PositionColor;
            overlayFrame.ChartPaint.push(chart);

            subFrame.OverlayIndex.push(overlayFrame);
            subFrame.Frame.RightFrame=frame;  //右边坐标绑定到主坐标上
        }
        else
        {
            chart=subFrame.OverlayIndex[0].ChartPaint[0];
            frame=subFrame.OverlayIndex[0].Frame;
        }

        var xPointCouont=this.Frame.SubFrame[0].Frame.XPointCount;
        frame.XPointCount=xPointCouont;

        var bindData=new ChartData();
        bindData.Data=minuteData.GetPosition();
        chart.Data=bindData;
    }

    this.ClearBindOverlayMainData=function()
    {
        if (this.Frame.SubFrame.length<2) return;
        var subFrame=this.Frame.SubFrame[1];    //第2个窗口
        subFrame.Frame.RightFrame=null;
        subFrame.OverlayIndex=[];
    }

    //获取子窗口的所有画法
    this.GetChartPaint=function(windowIndex)
    {
        var paint=new Array();
        for(var i in this.ChartPaint)
        {
            if (i<3) continue; //分钟 均线 成交量 3个线不能改

            var item=this.ChartPaint[i];
            if (item.ChartFrame==this.Frame.SubFrame[windowIndex].Frame)
                paint.push(item);
        }

        return paint;
    }

    //创建指定窗口指标
    this.CreateWindowIndex=function(windowIndex)
    {
        this.WindowIndex[windowIndex].Create(this,windowIndex);
    }

    //获取当前的显示的指标
    this.GetIndexInfo=function()
    {
        var aryIndex=[];
        for(var i in this.WindowIndex)
        {
            var item=this.WindowIndex[i];
            var info={Name:item.Name};
            if (item.ID) info.ID=item.ID;
            aryIndex.push(info);
        }

        return aryIndex;
    }

    this.OnTouchFinished=function()
    {
        if (this.CorssCursorTouchEnd===true)    //手势离开十字光标消失
        {
            this.DrawDynamicInfo();
            return;
        }

        /* 以后放日线的tooltip
        for(var i in this.ExtendChartPaint)
        {
            var item=this.ExtendChartPaint[i];
            if (item.ClassName==='KLineTooltipPaint')
            {
                this.DrawDynamicInfo();
            }
        }
        */
    }

    this.CreateExtendChart=function(name, option)   //创建扩展图形
    {
        var chart;
        switch(name)
        {
            case 'MinuteTooltip':
                chart=new MinuteTooltipPaint();
                chart.Canvas=this.Canvas;
                chart.ChartBorder=this.Frame.ChartBorder;
                chart.ChartFrame=this.Frame;
                chart.HQChart=this;
                option.LanguageID=this.LanguageID;
                chart.SetOption(option);
                this.ExtendChartPaint.push(chart);
                return chart;
            default:
                return null;
        }
    }

    this.SetMinuteInfo=function(aryInfo,bUpdate)
    {
        this.ChartInfo=[];  //先清空
        for(var i in aryInfo)
        {
            var infoItem=JSMinuteInfoMap.Get(aryInfo[i]);
            if (!infoItem) continue;
            var item=infoItem.Create();
            this.ChartInfo.push(item);
        }

        if (bUpdate==true) this.RequestMinuteInfoData();
    }

    this.GetChartMinuteInfo=function()
    {
        return this.ChartInfoPaint;
    }

    this.CreateMinuteInfo=function(option)  //在Create()以后 在调用
    {
        var chart=new ChartMinuteInfo();
        chart.Canvas=this.Canvas;
        chart.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        chart.ChartFrame=this.Frame.SubFrame[0].Frame;
        chart.HQChartBorder=this.Frame.ChartBorder;
        chart.ChartMinutePrice=this.ChartPaint[0];
        if (option && chart.SetOption) chart.SetOption(option);
        this.ChartInfoPaint=chart;
        return chart;
    }

    //信息地雷数据请求
    this.RequestMinuteInfoData=function()
    {
        if (this.ChartInfo.length<=0) return;

        var chart=this.GetChartMinuteInfo();
        if (!chart) chart=this.CreateMinuteInfo(null);  //不存在就创建 

        chart.SourceData=this.SourceData;

        //信息地雷信息
        for(var i in this.ChartInfo)
        {
            this.ChartInfo[i].RequestData(this);
        }
    }

    //更新信息地雷
    this.UpdataChartInfo=function()
    {
        var chart=this.GetChartMinuteInfo();
        if (!chart) return;

        var infoMap=new Map();
        for(var i in this.ChartInfo)
        {
            var infoData=this.ChartInfo[i].Data;
            for(var j in infoData)
            {
                var item=infoData[j];
                var dateTime=`${item.Date} ${item.Time}`;
                if (infoMap.has(dateTime))
                {
                    infoMap.get(dateTime).Data.push(item);
                }
                else
                {

                    infoMap.set(dateTime,{Data:new Array(item)});
                }
            }
        }
        
        chart.Data=infoMap;
    }

    
}

// 分钟走势图数据 带盘前数据
MinuteChartContainer.JsonDataToMinuteData2=function(data)
{
    var symbol=data.stock[0].symbol;
    var preClose=data.stock[0].yclose;      //前一个数据价格
    var preAvPrice=data.stock[0].yclose;    //前一个均价
    var yClose=data.stock[0].yclose;
    var aryMinuteData=[];
    var stockData=data.stock[0];
    var date=stockData.date;    //日期
    for(var i in stockData.before)
    {
        var item=new MinuteData();
        var jsData=stockData.before[i];
        var time=jsData[0];
        item.Before={};
        item.Before.Close=jsData[1];
        if (!item.Before.Close) item.Before.Close=preClose;
        else preClose=item.Before.Close;
        item.Before.Vol=jsData[2]/100;  //沪深股票原始单位股
        item.Before.Amount=jsData[3];   
        item.DateTime=date.toString()+" "+time.toString();
        item.Date=date;
        item.Time=time;
        if (yClose && item.Before.Close) 
            item.Before.Increase=(item.Before.Close-yClose)/yClose*100; //涨幅 (最新价格-昨收)/昨收*100;
        aryMinuteData.push(item);
    }

    for(var i in stockData.minute)
    {
        var jsData=stockData.minute[i];
        var item=new MinuteData();
        if (jsData.price) preClose=jsData.price;
        if (jsData.avprice) preAvPrice=jsData.avprice;
        if (jsData.time==925) continue;   //去掉9:25的数据

        item.Close=jsData.price;
        item.Open=jsData.open;
        item.High=jsData.high;
        item.Low=jsData.low;
        item.Amount=jsData.amount;
        item.Vol=jsData.vol/100;        //沪深股票原始单位股
        item.DateTime=data.stock[0].date.toString()+" "+jsData.time.toString();
        item.Date=data.stock[0].date;
        item.Time=jsData.time;

        item.Increase=jsData.increase;
        item.Risefall=jsData.risefall;
        item.AvPrice=jsData.avprice;

        //当前没有价格 使用上一个价格填充
        if (!item.Close) item.Open=item.High=item.Low=item.Close=preClose;
        if (!item.AvPrice) item.AvPrice=preAvPrice;
        if (yClose && item.Close) 
            item.Increase=(item.Close-yClose)/yClose*100; //涨幅 (最新价格-昨收)/昨收*100;

        aryMinuteData.push(item);
    }

    return aryMinuteData;
}

//API 返回数据 转化为array[]
MinuteChartContainer.JsonDataToMinuteData=function(data,isBeforeData)
{
    var symbol=data.stock[0].symbol;
    var upperSymbol=symbol.toUpperCase();
    var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol);
    var isFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol);
    var aryMinuteData=new Array();
    var preClose=data.stock[0].yclose;      //前一个数据价格
    var preAvPrice=data.stock[0].yclose;    //前一个均价
    var yClose=data.stock[0].yclose;
    if (isFutures && data.stock[0].yclearing) yClose=preClose=preAvPrice=data.stock[0].yclearing;  //期货使用昨结算价
    
    var date=data.stock[0].date;    //默认使用外部日期, 但跨天的 走势图使用内部的日期
    for(var i in data.stock[0].minute)
    {
        var jsData=data.stock[0].minute[i];
        var item=new MinuteData();

        item.Close=jsData.price;
        item.Open=jsData.open;
        item.High=jsData.high;
        item.Low=jsData.low;
        if (isSHSZ) item.Vol=jsData.vol/100; //沪深股票原始单位股
        else item.Vol=jsData.vol;
        item.Amount=jsData.amount;
        if (jsData.date>0) date=jsData.date;    //分钟数据中有日期 优先使用
        item.DateTime=date.toString()+" "+jsData.time.toString();
        item.Date=date;
        item.Time=jsData.time;
        if (isFutures) item.Position=jsData.position;

        if (i==0) 
        {
            item.IsFristData=true;
            if(isSHSZ) 
            {
                item.DateTime=data.stock[0].date.toString() + " 0925"; //沪深股票 第1个数据 写死9:25
                item.Time=925;
            }
        }
        
        item.Increase=jsData.increase;
        item.Risefall=jsData.risefall;
        item.AvPrice=jsData.avprice;
        
        if (IFrameSplitOperator.IsNumber(jsData.lead)) 
            item.Lead=jsData.lead;  //领先指标 指数才有

        if (!item.Close) //当前没有价格 使用上一个价格填充
        {
            item.Close=preClose;
            item.Open=item.High=item.Low=item.Close;
        }

        if (!item.AvPrice) item.AvPrice=preAvPrice;

        //价格是0的 都用空
        if (item.Open<=0) item.Open=null;
        if (item.Close<=0) item.Close=null;
        if (item.AvPrice<=0) item.AvPrice=null;
        if (item.High<=0) item.High=null;
        if (item.Low<=0) item.Low=null;

        //if (isFutures) item.AvPrice=null;    //期货均价暂时没有
            
        if (yClose && item.Close) 
            item.Increase=(item.Close-yClose)/yClose*100; //涨幅 (最新价格-昨收)/昨收*100;

        
        //均价太大 可能是后台算错了
        var checkValue=Math.abs(item.AvPrice-item.Close);
        //JSConsole.Chart.Log(`[MinuteChartContainer::JsonDataToMinuteData] checkValue=${checkValue}, ${item.Close*0.13} `)
        if (isSHSZ && checkValue>item.Close*0.13 ) 
            item.AvPrice=preAvPrice;

        //上次价格
        if (jsData.price>0) preClose=jsData.price;
        if (jsData.avprice>0 && item.AvPrice===jsData.avprice) preAvPrice=jsData.avprice;

        aryMinuteData[i]=item;
    }

    return aryMinuteData;
}

//多日日线数据API 转化成array[];
MinuteChartContainer.JsonDataToMinuteDataArray=function(data)
{
    var symbol=data.symbol;
    var upperSymbol=symbol.toUpperCase();
    var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol);
    var isFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol);
    var result=[];
    for(var i in data.data)
    {
        var minuteData=[];
        var dayData=data.data[i];
        var date=dayData.date;
        var yClose=dayData.yclose;  //前收盘 计算涨幅
        var preClose=yClose;        //前一个数据价格
        var preAvPrice=null;           //上一个均价
        //var preAvPrice=data.stock[0].yclose;    //前一个均价
        for(var j in dayData.minute)
        {
            var jsData=dayData.minute[j];
            if (jsData[2]) preClose=jsData[2];  //保存上一个收盘数据
            var item=new MinuteData();
            item.Close=jsData[2];
            item.Open=jsData[1];
            item.High=jsData[3];
            item.Low=jsData[4];
            if (isSHSZ) item.Vol=jsData[5]/100; //原始单位股
            else item.Vol=jsData[5];
            item.Amount=jsData[6];
            if (7<jsData.length && jsData[7]>0) item.AvPrice=jsData[7];    //均价
            item.DateTime=date.toString()+" "+jsData[0].toString();
            item.Date=date;
            item.Time=jsData[0];
            if (8<jsData.length && jsData[8]>0) item.Date=jsData[8];    //日期
            if (isFutures && 9<jsData.length) item.Position=jsData[9];  //持仓
            
            if (!item.Close)    //当前没有价格 使用上一个价格填充
            {
                item.Close=preClose;   
                item.Open=item.High=item.Low=item.Close;
            }

            if (!item.AvPrice && preAvPrice) item.AvPrice=preAvPrice;

            if (item.Close && yClose) item.Increase = (item.Close - yClose)/yClose*100;
            else item.Increase=null;
            if (j==0)      //第1个数据 写死9:25
            {
                if (isSHSZ) 
                {
                    item.DateTime=date.toString()+" 0925";
                    item.Time=925;
                }
                item.IsFristData=true;
            }

            //价格是0的 都用空
            if (item.Open<=0) item.Open=null;
            if (item.Close<=0) item.Close=null;
            if (item.AvPrice<=0) item.AvPrice=null;
            if (item.High<=0) item.High=null;
            if (item.Low<=0) item.Low=null;
            if (item.AvPrice<=0) item.AvPrice=null;

            //均价太大 可能是后台算错了
            if (item.AvPrice && isSHSZ)
            {
                var checkValue=Math.abs(item.AvPrice-item.Close);
                if (checkValue>item.Close*0.13 ) item.AvPrice=preAvPrice;
            }

            if (jsData.length>7 && jsData[7]>0 && item.AvPrice===jsData[7]) preAvPrice=jsData[7];

            minuteData[j]=item;
        }

        var newData=new ChartData();
        newData.Data=minuteData;
        newData.YClose=yClose;
        newData.Close=dayData.close;
        newData.Date=date;

        result.push(newData);
    }

    return result;
}

/*
    历史分钟走势图
*/
function HistoryMinuteChartContainer(uielement)
{
    this.newMethod=MinuteChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.HistoryMinuteApiUrl="https://opensourcecache.zealink.com/cache/minuteday/day/";
    this.ClassName='HistoryMinuteChartContainer';

    //创建主图K线画法
    this.CreateMainKLine=function()
    {
        //分钟线
        var minuteLine=new ChartMinutePriceLine();
        minuteLine.Canvas=this.Canvas;
        minuteLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        minuteLine.ChartFrame=this.Frame.SubFrame[0].Frame;
        minuteLine.Name="Minute-Line";
        minuteLine.Color=g_JSChartResource.Minute.PriceColor;

        this.ChartPaint[0]=minuteLine;

        //分钟线均线
        var averageLine=new ChartLine();
        averageLine.Canvas=this.Canvas;
        averageLine.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        averageLine.ChartFrame=this.Frame.SubFrame[0].Frame;
        averageLine.Name="Minute-Average-Line";
        averageLine.Color=g_JSChartResource.Minute.AvPriceColor;
        this.ChartPaint[1]=averageLine;

        var averageLine=new ChartMinuteVolumBar();
        averageLine.Color=g_JSChartResource.Minute.VolBarColor;
        averageLine.Canvas=this.Canvas;
        averageLine.ChartBorder=this.Frame.SubFrame[1].Frame.ChartBorder;
        averageLine.ChartFrame=this.Frame.SubFrame[1].Frame;
        averageLine.Name="Minute-Vol-Bar";
        this.ChartPaint[2]=averageLine;


        this.TitlePaint[0]=new DynamicMinuteTitlePainting();
        this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame;
        this.TitlePaint[0].Canvas=this.Canvas;
        this.TitlePaint[0].IsShowDate=true;

        /*
        //主图叠加画法
        var paint=new ChartOverlayKLine();
        paint.Canvas=this.Canvas;
        paint.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        paint.ChartFrame=this.Frame.SubFrame[0].Frame;
        paint.Name="Overlay-KLine";
        this.OverlayChartPaint[0]=paint;
        */

    }

    //设置交易日期
    this.ChangeTradeDate=function(trdateDate)
    {
        if (!trdateDate) return;

        this.TradeDate=trdateDate;
        this.RequestData(); //更新数据
    }

    this.RequestData=function()
    {
        var date=new Date();
        var nowDate=date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate();
        if (nowDate==this.TradeDate) this.RequestMinuteData();
        else this.RequestHistoryMinuteData();
    }

    //请求分钟数据
    this.RequestHistoryMinuteData=function()
    {
        var self=this;
        var url=this.HistoryMinuteApiUrl+this.TradeDate.toString()+"/"+this.Symbol+".json";

        JSNetwork.HttpRequest({
            url: url,
            type:"get",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvHistoryMinuteData(data);
            },
            error:function(reqeust)
            {
                self.ChartSplashPaint.IsEnableSplash=false;
                self.RecvHistoryMinuteError(reqeust);
            }
        });
    }

    this.RecvHistoryMinuteError=function(reqeust)
    {
        if (reqeust.status!=404) return;

        var sourceData=new ChartData();
        this.SourceData=sourceData;

        for(var i in this.ChartPaint)
        {
            this.ChartPaint[i].Data=sourceData;
            if (i==0) this.ChartPaint[i].NotSupportMessage='没有权限访问!';
        }

        this.TitlePaint[0].Data=this.SourceData;                    //动态标题
        this.TitlePaint[0].Symbol=this.Symbol;
        this.TitlePaint[0].Name=null;

        this.Draw();
    }

    this.RecvHistoryMinuteData=function(data)
    {
        var aryMinuteData=HistoryMinuteChartContainer.JsonDataToMinuteData(data);

        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=aryMinuteData;

        this.TradeDate=data.date;

        this.SourceData=sourceData;
        this.Symbol=data.symbol;
        this.Name=data.name;

        this.BindMainData(sourceData,data.day.yclose);

        if (this.Frame.SubFrame.length>2)
        {
            var bindData=new ChartData();
            bindData.Data=aryMinuteData;
            for(var i=2; i<this.Frame.SubFrame.length; ++i)
            {
                this.BindIndexData(i,bindData);
            }
        }

        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();

        //this.AutoUpdata();
    }

}

//API 返回数据 转化为array[]
HistoryMinuteChartContainer.JsonDataToMinuteData=function(data)
{
    var aryMinuteData=[];
    for(var i in data.minute.time)
    {
        var item=new MinuteData();

        if (data.minute.price[i]<=0 && i>0) //当前这一分钟价格为空,使用上一分钟的数据
        {
            item.Close=aryMinuteData[i-1].Close;
            item.Open=aryMinuteData[i-1].Close;
            item.High=item.Close;
            item.Low=item.Close;
            item.Vol=data.minute.vol[i]; //原始单位股
            item.Amount=data.minute.amount[i];
            item.DateTime=data.date.toString()+" "+data.minute.time[i].toString();
            //item.Increate=jsData.increate;
            //item.Risefall=jsData.risefall;
            item.AvPrice=aryMinuteData[i-1].AvPrice;
        }
        else
        {
            item.Close=data.minute.price[i];
            item.Open=data.minute.open[i];
            item.High=data.minute.high[i];
            item.Low=data.minute.low[i];
            item.Vol=data.minute.vol[i]; //原始单位股
            item.Amount=data.minute.amount[i];
            item.DateTime=data.date.toString()+" "+data.minute.time[i].toString();
            //item.Increate=jsData.increate;
            //item.Risefall=jsData.risefall;
            item.AvPrice=data.minute.avprice[i];
        }

        //价格是0的 都用空
        if (item.Open<=0) item.Open=null;
        if (item.Close<=0) item.Close=null;
        if (item.AvPrice<=0) item.AvPrice=null;
        if (item.High<=0) item.High=null;
        if (item.Low<=0) item.Low=null;

        aryMinuteData[i]=item;
    }

    return aryMinuteData;
}

/////////////////////////////////////////////////////////////////////////////
//  自定义指数
//
function CustomKLineChartContainer(uielement)
{
    this.newMethod=KLineChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.ClassName='CustomKLineChartContainer';
    this.CustomKLineApiUrl=g_JSChartResource.Domain+"/API/IndexCalculate";                        //自定义指数计算地址
    this.CustomStock;   //成分
    this.QueryDate={Start:20180101,End:20180627} ;     //计算时间区间

    this.RequestHistoryData=function()
    {
        var self=this;
        this.ChartSplashPaint.IsEnableSplash = true;
        this.Draw();
        JSNetwork.HttpRequest({
            url: this.CustomKLineApiUrl,
            data:
            {
                "stock": self.CustomStock,
                "Name": self.Symbol,
                "date": { "startdate":self.QueryDate.Start,"enddate":self.QueryDate.End }
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (data)
            {
                self.ChartSplashPaint.IsEnableSplash = false;
                self.RecvHistoryData(data);
            }
        });
    }

    this.RecvHistoryData=function(data)
    {
        var aryDayData=KLineChartContainer.JsonDataToHistoryData(data);

        //原始数据
        var sourceData=new ChartData();
        sourceData.Data=aryDayData;
        sourceData.DataType=0;      //0=日线数据 1=分钟数据

        //显示的数据
        var bindData=new ChartData();
        bindData.Data=aryDayData;
        bindData.Right=this.Right;
        bindData.Period=this.Period;
        bindData.DataType=0;

        if (bindData.Right>0)    //复权
        {
            var rightData=bindData.GetRightDate(bindData.Right);
            bindData.Data=rightData;
        }

        if (ChartData.IsDayPeriod(this.Period,false))   //周期数据
        {
            var periodData=sourceData.GetPeriodData(bindData.Period);
            bindData.Data=periodData;
        }

        //绑定数据
        this.SourceData=sourceData;
        this.Name=data.name;
        this.BindMainData(bindData,this.PageSize);

        for(var i=0; i<this.Frame.SubFrame.length; ++i)
        {
            this.BindIndexData(i,bindData);
        }

        //刷新画图
        this.UpdataDataoffset();           //更新数据偏移
        this.UpdatePointByCursorIndex();   //更新十字光标位子
        this.UpdateFrameMaxMin();          //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
        
    }

}

////////////////////////////////////////////////////////////////////////////////
//  K线训练
//
function KLineTrainChartContainer(uielement, bHScreen)
{
    if (bHScreen===true)
    {
        this.newMethod=KLineChartHScreenContainer;   //派生
        this.newMethod(uielement);
        delete this.newMethod;
    }
    else
    {
        this.newMethod=KLineChartContainer;   //派生
        this.newMethod(uielement);
        delete this.newMethod;
    }

    this.BuySellPaint;          //买卖点画法
    this.TrainDataCount=300;    //训练数据个数
    this.AutoRunTimer=null;     //K线自动前进定时器
    this.BuySellData=[];        //模拟买卖数据 {Buy:{Price:价格,Date:日期, Time:时间} , Sell:{Price:价格,Date:日期, Time:时间} 
    this.TrainDataIndex;        //当前训练的数据索引
    this.TrainCallback;         //训练回调 (K线每前进一次就调用一次)
    this.DragMode=0;

    this.TrainStartEnd={};

    this.OnKeyDown=function(e)
    {
        var keyID = e.keyCode ? e.keyCode :e.which;
        //只支持放大缩小
        switch(keyID)
        {
            case 37: //left
                if (this.CursorIndex>1) //十字光标只能在当前屏移动
                {
                    --this.CursorIndex;
                    this.UpdatePointByCursorIndex();
                    this.DrawDynamicInfo();
                    this.ShowTooltipByKeyDown();
                }
                break;
            case 39: //right
                var xPointcount=0;
                if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount;
                else xPointcount=this.Frame.SubFrame[0].Frame.XPointCount;
                if (this.CursorIndex+1<xPointcount) //十字光标只能在当前屏移动
                {
                    //判断是否在最后一个数据上
                    var data=null;
                    if (this.Frame.Data) data=this.Frame.Data;
                    else data=this.Frame.SubFrame[0].Frame.Data;
                    if (!data) break;
                    if (this.CursorIndex+data.DataOffset+1>=data.Data.length) break;

                    ++this.CursorIndex;
                    this.UpdatePointByCursorIndex();
                    this.DrawDynamicInfo();
                    this.ShowTooltipByKeyDown();
                }
                break;
            case 38:    //up
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomUp(cursorIndex)) break;
                this.CursorIndex=cursorIndex.Index;
                this.UpdatePointByCursorIndex();
                this.UpdataDataoffset();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
                break;
            case 40:    //down
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomDown(cursorIndex)) break;
                this.CursorIndex=cursorIndex.Index;
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
                break;
        }

        //不让滚动条滚动
        if(e.preventDefault) e.preventDefault();    
        else e.returnValue = false;
    }

    //手机拖拽
    uielement.ontouchstart=(e)=> { this.OnTouchStart(e); }
    uielement.ontouchmove=(e)=> { this.OnTouchMove(e); }
    uielement.ontouchend=(e)=> { this.OnTouchEnd(e); }

    this.OnTouchStart=function(e)
    {
        this.PhonePinch=null;
        this.IsOnTouch=true;
        this.TouchDrawCount=0;
        e.preventDefault();

        if (this.IsPhoneDragging(e))
        {
            var drag=
            {
                "Click":{},
                "LastMove":{}  //最后移动的位置
            };

            var touches=this.GetToucheData(e,false);

            drag.Click.X=touches[0].clientX;
            drag.Click.Y=touches[0].clientY;
            drag.LastMove.X=touches[0].clientX;
            drag.LastMove.Y=touches[0].clientY;

            if (drag.Click.X==drag.LastMove.X && drag.Click.Y==drag.LastMove.Y) //手指没有移动,出现十字光标
            {
                var mouseDrag=jsChart.MouseDrag;
                jsChart.MouseDrag=null;
                //移动十字光标
                var pixelTatio = GetDevicePixelRatio();
                var x = drag.Click.X-this.UIElement.getBoundingClientRect().left*pixelTatio;
                var y = drag.Click.Y-this.UIElement.getBoundingClientRect().top*pixelTatio;
                this.OnMouseMove(x,y,e);
            }

            this.SelectChartDrawPicture=null;
        }
        else if (this.IsPhonePinching(e))
        {
            var phonePinch=
            {
                "Start":{},
                "Last":{}
            };

            var touches=this.GetToucheData(e,false);

            phonePinch.Start={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};
            phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};

            this.PhonePinch=phonePinch;
            this.SelectChartDrawPicture=null;
        }
    }

    this.OnTouchMove=function(e)
    {
        e.preventDefault();

        var touches=this.GetToucheData(e,false);

        if (this.IsPhoneDragging(e))
        {
            var drag=this.MouseDrag;
            if (drag==null)
            {
                var pixelTatio = GetDevicePixelRatio();
                var x = touches[0].clientX-this.UIElement.getBoundingClientRect().left*pixelTatio;
                var y = touches[0].clientY-this.UIElement.getBoundingClientRect().top*pixelTatio;
                this.OnMouseMove(x,y,e);
            }
            else
            {
                
            }
        }
        else if (this.IsPhonePinching(e))
        {
            phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};
        }
    }

    this.CreateBuySellPaint=function()  //在主窗口建立以后 创建买卖点
    {
        var chart=new ChartBuySell();
        chart.Canvas=this.Canvas;
        chart.ChartBorder=this.Frame.SubFrame[0].Frame.ChartBorder;
        chart.ChartFrame=this.Frame.SubFrame[0].Frame;
        chart.Name="KLine-Train-BuySell";
        this.ChartPaintEx[0]=chart;
    }

    this.BindMainData=function(hisData,showCount)   //数据到达绑定主图K线数据
    {
        this.ChartPaint[0].Data=hisData;
        for(var i in this.Frame.SubFrame)
        {
            var item =this.Frame.SubFrame[i].Frame;
            item.XPointCount=showCount;
            item.Data=this.ChartPaint[0].Data;
        }

        this.TitlePaint[0].Data=this.ChartPaint[0].Data;                    //动态标题
        this.TitlePaint[0].Symbol=this.Symbol;
        this.TitlePaint[0].Name=this.Name;

        this.ChartCorssCursor.StringFormatX.Data=this.ChartPaint[0].Data;   //十字光标
        this.Frame.Data=this.ChartPaint[0].Data;

        if (!this.ChartPaintEx[0]) this.CreateBuySellPaint();
        this.ChartPaintEx[0].Data=this.ChartPaint[0].Data;

        for(var i in this.OverlayChartPaint )
        {
            var item=this.OverlayChartPaint[i];
            item.MainData=this.ChartPaint[0].Data;         //K线叠加
        }
        
        var dataOffset=hisData.Data.length-showCount-this.TrainDataCount-20;   //随机选一段数据进行训练
        if (dataOffset<0) dataOffset=0;
        this.ChartPaint[0].Data.DataOffset=dataOffset;

        this.CursorIndex=showCount;
        if (this.CursorIndex+dataOffset>=hisData.Data.length) this.CursorIndex=dataOffset;

        this.TrainDataIndex=this.CursorIndex;

        this.TrainStartEnd.Start=hisData.Data[this.TrainDataIndex+dataOffset-1];
    }

    this.Run=function()
    {
        if (this.AutoRunTimer) return;
        if (this.TrainDataCount<=0) return;

        var self=this;
        this.AutoRunTimer=setInterval(function()
        {
            if (!self.MoveNextKLineData()) clearInterval(self.AutoRunTimer);
        }, 1000);
    }

    this.MoveNextKLineData=function()
    {
        if (this.TrainDataCount<=0) return false;

        var xPointcount=0;
        if (this.Frame.XPointCount) xPointcount=this.Frame.XPointCount; //数据个数
        if (this.TrainDataIndex+1>=xPointcount)
        {
            this.CursorIndex=this.TrainDataIndex;
            if (!this.DataMoveRight()) return false;
            this.UpdataDataoffset();
            this.UpdatePointByCursorIndex();
            this.UpdateFrameMaxMin();
            this.Draw();
            ++this.TrainDataIndex;
            --this.TrainDataCount;

            if (this.TrainDataCount<=0) 
            {
                this.FinishTrainData();
                this.UpdateTrainUICallback();
                return false;
            }

            this.UpdateTrainUICallback();
            return true;
        }

        return false;
    }

    this.UpdateTrainUICallback=function()
    {
        var buySellPaint=this.ChartPaintEx[0];
        var lastData=buySellPaint.LastData.Data;
        this.TrainStartEnd.End=lastData;

        //老接口 以后会不用
        if (this.TrainCallback) this.TrainCallback(this);

        //新的监听事件
        if (!this.mapEvent.has(JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP)) return;
        var item=this.mapEvent.get(JSCHART_EVENT_ID.RECV_TRAIN_MOVE_STEP);
        if (!item.Callback) return;

        //Operator 0=买 1卖
        var data={TrainDataCount:this.TrainDataCount, BuySellData:this.BuySellData, Operator:this.GetOperator() , KLine:this.TrainStartEnd};   
        item.Callback(item,data,this);
    }

    this.FinishTrainData=function()
    {
        var buySellPaint=this.ChartPaintEx[0];
        if (buySellPaint && this.BuySellData.length)    //取最后1条数据 看是否卖了
        {
            var lastData=buySellPaint.LastData.Data;
            var item=this.BuySellData[this.BuySellData.length-1];
            if (!item.Sell)
            {
                item.Sell={Price:lastData.Close,Date:lastData.Date};
                buySellPaint.BuySellData.set(lastData.Date,{Op:1});
            }
        } 
    }

    this.GetLastBuySellData=function()   //取最后1条数据
    {
        var buySellPaint=this.ChartPaintEx[0];
        if (!buySellPaint) return null;

        if (this.BuySellData.length)   
        {
            var item=this.BuySellData[this.BuySellData.length-1];
            return item;
        } 

        return null;
    }

    this.GetOperator=function()     //获取当前是卖/买
    {
        var buySellData=this.GetLastBuySellData();
        if (buySellData && buySellData.Buy && !buySellData.Sell) return 1;

        return 0;
    }

    this.Stop=function()
    {
        if (this.AutoRunTimer!=null) clearInterval(this.AutoRunTimer);
        this.AutoRunTimer=null;
    }

    this.BuyOrSell=function()   //模拟买卖
    {
        var isMinutePeriod=ChartData.IsMinutePeriod(this.Period,true);
        var buySellPaint=this.ChartPaintEx[0];
        var lastData=buySellPaint.LastData.Data;
        var buySellData=this.GetLastBuySellData();
    
        if (buySellData && buySellData.Buy && !buySellData.Sell)
        {
            var key=lastData.Date*10000;
            buySellData.Sell={Price:lastData.Close,Date:lastData.Date};
            if (isMinutePeriod) 
            {
                buySellData.Sell.Time=lastData.Time;
                key+=lastData.Time;
            }
            buySellPaint.BuySellData.set(key,{Op:1});
            this.MoveNextKLineData();
            return;
        }
        
        var key=lastData.Date*10000;
        var newItem={ Buy:{Price:lastData.Close,Date:lastData.Date}, Sell:null };
        if (isMinutePeriod) 
        {
            newItem.Buy.Time=lastData.Time;
            key+=lastData.Time;
        }
        this.BuySellData.push(newItem);
        
        buySellPaint.BuySellData.set(key,{Op:0});
        this.MoveNextKLineData();
    }
}

////////////////////////////////////////////////////////////////////////////////
//  K线横屏显示
//
function KLineChartHScreenContainer(uielement)
{
    this.newMethod=KLineChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.ClassName='KLineChartHScreenContainer';

    this.OnMouseMove=function(x,y,e)
    {
        this.LastPoint.X=x;
        this.LastPoint.Y=y;
        this.CursorIndex=this.Frame.GetXData(y);

        this.DrawDynamicInfo();
    }

    uielement.onmousedown=function(e)   //鼠标拖拽
    {
        if(!this.JSChartContainer) return;
        if(this.JSChartContainer.DragMode==0) return;

        var pixelTatio = GetDevicePixelRatio();
        if (this.JSChartContainer.TryClickLock)
        {
            var x = (e.clientX-this.getBoundingClientRect().left)*pixelTatio;
            var y = (e.clientY-this.getBoundingClientRect().top)*pixelTatio;
            if (this.JSChartContainer.TryClickLock(x,y)) return;
        }


        var drag=
        {
            "Click":{},
            "LastMove":{}  //最后移动的位置
        };

        drag.Click.X=e.clientX;
        drag.Click.Y=e.clientY;
        drag.LastMove.X=e.clientX;
        drag.LastMove.Y=e.clientY;

        this.JSChartContainer.MouseDrag=drag;
        document.JSChartContainer=this.JSChartContainer;
        this.JSChartContainer.SelectChartDrawPicture=null;

        uielement.ondblclick=function(e)
        {
            var x = e.clientX-this.getBoundingClientRect().left;
            var y = e.clientY-this.getBoundingClientRect().top;

            if(this.JSChartContainer)
                this.JSChartContainer.OnDoubleClick(x,y,e);
        }

        document.onmousemove=function(e)
        {
            if(!this.JSChartContainer) return;
            //加载数据中,禁用鼠标事件
            if (this.JSChartContainer.ChartSplashPaint && this.JSChartContainer.ChartSplashPaint.IsEnableSplash == true) return;

            var drag=this.JSChartContainer.MouseDrag;
            if (!drag) return;

            var moveSetp=Math.abs(drag.LastMove.Y-e.clientY);

            if (this.JSChartContainer.DragMode==1)  //数据左右拖拽
            {
                if (moveSetp<5) return;

                var isLeft=true;
                if (drag.LastMove.Y<e.clientY) isLeft=false;//右移数据

                if(this.JSChartContainer.DataMove(moveSetp,isLeft))
                {
                    this.JSChartContainer.UpdataDataoffset();
                    this.JSChartContainer.UpdatePointByCursorIndex();
                    this.JSChartContainer.UpdateFrameMaxMin();
                    this.JSChartContainer.ResetFrameXYSplit();
                    this.JSChartContainer.Draw();
                }

                drag.LastMove.X=e.clientX;
                drag.LastMove.Y=e.clientY;
            }
        };

        document.onmouseup=function(e)
        {
            //清空事件
            document.onmousemove=null;
            document.onmouseup=null;

            //清空数据
            this.JSChartContainer.MouseDrag=null;
            this.JSChartContainer.CurrentChartDrawPicture=null;
            this.JSChartContainer=null;
        }
    }

    //手机拖拽
    this.OnTouchStart=function(e)
    {
        this.PhonePinch=null;
        this.IsOnTouch=true;
        this.TouchDrawCount=0;
        this.StopDragTimer();
        var isSingleTouch=this.IsSingleTouch(e);
        if (e.cancelable) e.preventDefault();

        if (this.IsPhoneDragging(e))
        {
            if (this.TryClickLock || this.TryClickIndexTitle) //指标枷锁区域, 指标标题点击
            {
                var touches = this.GetToucheData(e, this.IsForceLandscape);
                var x = touches[0].clientX;
                var y = touches[0].clientY;
                var pixelTatio = GetDevicePixelRatio();
                x -= uielement.getBoundingClientRect().left*pixelTatio;    //减去控件的偏移偏移量
                y -= uielement.getBoundingClientRect().top*pixelTatio;
                if (this.TryClickLock && this.TryClickLock(x, y)) return;
                if (this.TryClickIndexTitle && this.TryClickIndexTitle(x,y)) return;
            }

            var bStartTimer=true;
            if (this.ChartDrawOption.IsLockScreen)
            {
                bStartTimer=false;
            }
            else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
            {
                if (this.TouchStatus.CorssCursorShow==true) bStartTimer=false;
            }
            else if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID)
            {
                bStartTimer=false;
            }
            else
            {
                if (!isSingleTouch) bStartTimer=false;
            }

            var drag= {  Click:{}, LastMove:{}  }; //LastMove=最后移动的位置
           

            var touches=this.GetToucheData(e,false);

            drag.Click.X=touches[0].clientX;
            drag.Click.Y=touches[0].clientY;
            drag.LastMove.X=touches[0].clientX;
            drag.LastMove.Y=touches[0].clientY;

            this.MouseDrag=drag;
            if (this.SelectChartDrawPicture) this.SelectChartDrawPicture.IsSelected=false;
            this.SelectChartDrawPicture=null;

            if (this.CurrentChartDrawPicture)  //画图工具模式
            {
                var drawPicture=this.CurrentChartDrawPicture;
                if (drawPicture.Status==2)
                    this.SetChartDrawPictureThirdPoint(drag.Click.X,drag.Click.Y);
                else
                {
                    this.SetChartDrawPictureFirstPoint(drag.Click.X,drag.Click.Y);
                    //只有1个点 直接完成
                    if (this.FinishChartDrawPicturePoint()) this.DrawDynamicInfo({Corss:false, Tooltip:false});
                }

                if (e.cancelable) e.preventDefault();
                return;
            }
            else
            {
                var drawPictrueData={};
                var pixelTatio = GetDevicePixelRatio(); //鼠标移动坐标是原始坐标 需要乘以放大倍速
                drawPictrueData.X=(touches[0].clientX-uielement.getBoundingClientRect().left);
                drawPictrueData.Y=(touches[0].clientY-uielement.getBoundingClientRect().top);
                if (this.GetChartDrawPictureByPoint(drawPictrueData))
                {
                    drawPictrueData.ChartDrawPicture.Status=20;
                    drawPictrueData.ChartDrawPicture.ValueToPoint();
                    drawPictrueData.ChartDrawPicture.MovePointIndex=drawPictrueData.PointIndex;
                    drawPictrueData.ChartDrawPicture.IsSelected=true;
                    this.CurrentChartDrawPicture=drawPictrueData.ChartDrawPicture;
                    this.SelectChartDrawPicture=drawPictrueData.ChartDrawPicture;
                    let event=this.GetEventCallback(JSCHART_EVENT_ID.ON_CLICK_DRAWPICTURE); //选中画图工具事件
                    if (event && event.Callback)
                    {
                        let sendData={ DrawPicture: drawPictrueData.ChartDrawPicture };
                        event.Callback(event,sendData,this);
                    }

                    if (e.cancelable) e.preventDefault();
                    return;
                }
            }

            if (bStartTimer)    //长按2秒,十字光标
            {
                var self=this;
                this.DragTimer=setTimeout(function()
                {
                    if (drag.Click.X==drag.LastMove.X && drag.Click.Y==drag.LastMove.Y) //手指没有移动,出现十字光标
                    {
                        var mouseDrag=self.MouseDrag;
                        self.MouseDrag=null;
                        self.MoveCorssCursor(drag.Click,e);//移动十字光标
                    }
                }, self.PressTime);
            }

            if (this.ChartDrawOption.IsLockScreen)
            {
                this.MouseDrag=null;
            }
            else if (this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID)
            {

            }
            else if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID)
            {
                this.MouseDrag=null;
                this.MoveCorssCursor(drag.Click,e);
            }
            else if (this.IsClickShowCorssCursor)
            {
                this.MoveCorssCursor(drag.Click,e);
            }    

        }
        else if (this.IsPhonePinching(e))
        {
            var phonePinch=
            {
                "Start":{},
                "Last":{}
            };

            var touches=this.GetToucheData(e,false);

            phonePinch.Start={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};
            phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};

            this.PhonePinch=phonePinch;
            this.SelectChartDrawPicture=null;
        }
    }

    this.OnTouchMove=function(e)
    {
        if (e.cancelable) e.preventDefault();

        var touches=this.GetToucheData(e,false);

        if (this.IsPhoneDragging(e))
        {
            var drag=this.MouseDrag;
            if (drag==null)
            {
                if (!this.ChartDrawOption.IsLockScreen) this.MoveCorssCursor({X:touches[0].clientX, Y:touches[0].clientY},e);
            }
            else
            {
                var moveSetp=Math.abs(drag.LastMove.Y-touches[0].clientY);
                var moveUpDown=Math.abs(drag.LastMove.X-touches[0].clientX);
                var isMoveCorssCursor=(this.DragMode==JSCHART_DRAG_ID.CLICK_TOUCH_MODE_ID && this.TouchStatus.CorssCursorShow==true); //是否移动十字光标

                if (this.CurrentChartDrawPicture)
                {
                    var drawPicture=this.CurrentChartDrawPicture;
                    if (drawPicture.Status==1 || drawPicture.Status==2)
                    {
                        if(moveSetp<5 && moveUpDown<5) return;
                        if(this.SetChartDrawPictureSecondPoint(touches[0].clientX,touches[0].clientY))
                        {
                            this.DrawDynamicInfo();
                        }
                    }
                    else if (drawPicture.Status==3)
                    {
                        if(this.SetChartDrawPictureThirdPoint(touches[0].clientX,touches[0].clientY))
                        {
                            this.DrawDynamicInfo();
                        }
                    }
                    else if (drawPicture.Status==20)    //画图工具移动
                    {
                        if(moveSetp<5 && moveUpDown<5) return;

                        if(this.MoveChartDrawPicture(touches[0].clientX-drag.LastMove.X,touches[0].clientY-drag.LastMove.Y))
                        {
                            this.DrawDynamicInfo();
                        }
                    }

                    drag.LastMove.X=touches[0].clientX;
                    drag.LastMove.Y=touches[0].clientY;
                }
                if (isMoveCorssCursor)  //点击模式下 十字光标显示 左右移动十字光标
                {
                    var mouseDrag=this.MouseDrag;
                    this.MouseDrag=null;
                    this.MoveCorssCursor(drag.Click,e); //移动十字光标
                }
                else if (this.DragMode==1 || isMoveCorssCursor==false)  //数据左右拖拽
                {
                    if (moveSetp<5) return;

                    var isLeft=true;
                    if (drag.LastMove.Y<touches[0].clientY) isLeft=false;//右移数据

                    if(this.DataMove(moveSetp,isLeft))
                    {
                        this.UpdataDataoffset();
                        this.UpdatePointByCursorIndex();
                        this.UpdateFrameMaxMin();
                        this.ResetFrameXYSplit();
                        this.Draw();
                    }
                    else
                    {
                        if (this.DragDownloadData) this.DragDownloadData();
                    }

                    drag.LastMove.X=touches[0].clientX;
                    drag.LastMove.Y=touches[0].clientY;
                }
            }
        }
        else if (this.IsPhonePinching(e))
        {
            if (this.DragMode==JSCHART_DRAG_ID.DISABLE_DRAG_ID) return;

            var phonePinch=this.PhonePinch;
            if (!phonePinch) return;

            var yHeight=Math.abs(touches[0].pageX-touches[1].pageX);
            var yLastHeight=Math.abs(phonePinch.Last.X-phonePinch.Last.X2);
            var yStep=yHeight-yLastHeight;
            if (Math.abs(yStep)<5) return;

            if (yStep>0)    //放大
            {
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomUp(cursorIndex)) return;
                this.CursorIndex=cursorIndex.Index;
                this.UpdatePointByCursorIndex();
                this.UpdataDataoffset();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
            }
            else        //缩小
            {
                var cursorIndex={};
                cursorIndex.Index=parseInt(Math.abs(this.CursorIndex-0.5).toFixed(0));
                if (!this.Frame.ZoomDown(cursorIndex)) return;
                this.CursorIndex=cursorIndex.Index;
                this.UpdataDataoffset();
                this.UpdatePointByCursorIndex();
                this.UpdateFrameMaxMin();
                this.Draw();
                this.ShowTooltipByKeyDown();
            }

            phonePinch.Last={"X":touches[0].pageX,"Y":touches[0].pageY,"X2":touches[1].pageX,"Y2":touches[1].pageY};
        }
    }

    this.OnTouchEnd=function(e)
    {
        JSConsole.Chart.Log('[KLineChartHScreenContainer:OnTouchEnd]',e);

        var bClearDrawPicture=true;
        if (this.CurrentChartDrawPicture)
        {
            var drawPicture=this.CurrentChartDrawPicture;
            if (drawPicture.Status==2 || drawPicture.Status==1 || drawPicture.Status==3)
            {
                drawPicture.PointStatus=drawPicture.Status;
                if (this.FinishChartDrawPicturePoint())
                    this.DrawDynamicInfo();
                else
                    bClearDrawPicture=false;
            }
            else if (drawPicture.Status==20)
            {
                if (this.FinishMoveChartDrawPicture())
                    this.DrawDynamicInfo();
            }
        }

        this.IsOnTouch = false;
        this.StopDragTimer();
        this.OnTouchFinished();
        this.TouchDrawCount=0;
    }

    //创建
    //windowCount 窗口个数
    this.Create=function(windowCount)
    {
        this.UIElement.JSChartContainer=this;

        //创建十字光标
        this.ChartCorssCursor=new ChartCorssCursor();
        this.ChartCorssCursor.Canvas=this.Canvas;
        this.ChartCorssCursor.StringFormatX=new HQDateStringFormat();
        this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat();

        //创建等待提示
        this.ChartSplashPaint = new ChartSplashPaint();
        this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new HQTradeHScreenFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=30;
        this.Frame.ChartBorder.Left=5;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;
        this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架
        this.ChartSplashPaint.Frame = this.Frame;

        this.CreateChildWindow(windowCount);
        this.CreateMainKLine();

        //子窗口动态标题
        for(var i in this.Frame.SubFrame)
        {
            var titlePaint=new DynamicChartTitlePainting();
            titlePaint.Frame=this.Frame.SubFrame[i].Frame;
            titlePaint.Canvas=this.Canvas;
            titlePaint.LanguageID=this.LanguageID;
            this.TitlePaint.push(titlePaint);
        }

        this.UIElement.addEventListener("keydown", OnKeyDown, true);    //键盘消息
    }

    //创建子窗口
    this.CreateChildWindow=function(windowCount)
    {
        for(var i=0;i<windowCount;++i)
        {
            var border=new ChartBorder();
            border.UIElement=this.UIElement;

            var frame=new KLineHScreenFrame();
            frame.Canvas=this.Canvas;
            frame.ChartBorder=border;
            frame.Identify=i;                   //窗口序号

            if (this.ModifyIndexDialog) frame.ModifyIndexEvent=this.ModifyIndexDialog.DoModal;        //绑定菜单事件
            if (this.ChangeIndexDialog) frame.ChangeIndexEvent=this.ChangeIndexDialog.DoModal;

            frame.HorizontalMax=20;
            frame.HorizontalMin=10;

            if (i==0)
            {
                frame.YSplitOperator=new FrameSplitKLinePriceY();
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('price');
                //主图上下间距
                var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
                border.TopSpace=12*pixelTatio;
                border.BottomSpace=12*pixelTatio;
            }
            else
            {
                frame.YSplitOperator=new FrameSplitY();
                frame.YSplitOperator.LanguageID=this.LanguageID;
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
                //frame.IsLocked = true;
            }

            frame.YSplitOperator.Frame=frame;
            frame.YSplitOperator.ChartBorder=border;
            frame.XSplitOperator=new FrameSplitKLineX();
            frame.XSplitOperator.Frame=frame;
            frame.XSplitOperator.ChartBorder=border;

            if (i!=windowCount-1) frame.XSplitOperator.ShowText=false;

            for(var j=frame.HorizontalMin;j<=frame.HorizontalMax;j+=1)
            {
                frame.HorizontalInfo[j]= new CoordinateInfo();
                frame.HorizontalInfo[j].Value=j;
                if (i==0 && j==frame.HorizontalMin) continue;

                frame.HorizontalInfo[j].Message[1]=j.toString();
                frame.HorizontalInfo[j].Font="14px 微软雅黑";
            }

            var subFrame=new SubFrameItem();
            subFrame.Frame=frame;
            if (i==0)
                subFrame.Height=20;
            else
                subFrame.Height=10;

            this.Frame.SubFrame[i]=subFrame;
        }
    }
}


////////////////////////////////////////////////////////////////////////////////
//  走势图横屏显示
//
function MinuteChartHScreenContainer(uielement)
{
    this.newMethod=MinuteChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.ClassName='MinuteChartHScreenContainer';

    this.OnMouseMove=function(x,y,e)
    {
        this.LastPoint.X=x;
        this.LastPoint.Y=y;
        this.CursorIndex=this.Frame.GetXData(y);

        this.DrawDynamicInfo();
    }

     //创建
    //windowCount 窗口个数
    this.Create=function(windowCount)
    {
        this.UIElement.JSChartContainer=this;

        //创建十字光标
        this.ChartCorssCursor=new ChartCorssCursor();
        this.ChartCorssCursor.Canvas=this.Canvas;
        this.ChartCorssCursor.StringFormatX=new HQMinuteTimeStringFormat();
        this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat();

         //创建等待提示
         this.ChartSplashPaint = new ChartSplashPaint();
         this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new HQTradeHScreenFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=25;
        this.Frame.ChartBorder.Left=50;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;
        this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架
        this.ChartSplashPaint.Frame = this.Frame;

        this.CreateChildWindow(windowCount);
        this.CreateMainKLine();

        //子窗口动态标题
        for(var i in this.Frame.SubFrame)
        {
            var titlePaint=new DynamicChartTitlePainting();
            titlePaint.Frame=this.Frame.SubFrame[i].Frame;
            titlePaint.Canvas=this.Canvas;
            titlePaint.LanguageID=this.LanguageID;
            this.TitlePaint.push(titlePaint);
        }
        
        this.ChartCorssCursor.StringFormatX.Frame=this.Frame.SubFrame[0].Frame;

        this.UIElement.addEventListener("keydown", OnKeyDown, true);    //键盘消息
    }

    //创建子窗口
    this.CreateChildWindow=function(windowCount)
    {
        for(var i=0;i<windowCount;++i)
        {
            var border=new ChartBorder();
            border.UIElement=this.UIElement;

            var frame=new MinuteHScreenFrame();
            frame.Canvas=this.Canvas;
            frame.ChartBorder=border;
            if (i<2) frame.ChartBorder.TitleHeight=0;
            frame.XPointCount=243;

            var DEFAULT_HORIZONTAL=[9,8,7,6,5,4,3,2,1];
            frame.HorizontalMax=DEFAULT_HORIZONTAL[0];
            frame.HorizontalMin=DEFAULT_HORIZONTAL[DEFAULT_HORIZONTAL.length-1];

            if (i==0)
            {
                frame.YSplitOperator=new FrameSplitMinutePriceY();
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('price');
            }
            else
            {
                frame.YSplitOperator=new FrameSplitY();
                frame.YSplitOperator.LanguageID=this.LanguageID;
                frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
            }

            frame.YSplitOperator.Frame=frame;
            frame.YSplitOperator.ChartBorder=border;
            frame.XSplitOperator=new FrameSplitMinuteX();
            frame.XSplitOperator.Frame=frame;
            frame.XSplitOperator.ChartBorder=border;
            if (i!=windowCount-1) frame.XSplitOperator.ShowText=false;
            frame.XSplitOperator.Operator();

            for(var j in DEFAULT_HORIZONTAL)
            {
                frame.HorizontalInfo[j]= new CoordinateInfo();
                frame.HorizontalInfo[j].Value=DEFAULT_HORIZONTAL[j];
                if (i==0 && j==frame.HorizontalMin) continue;

                frame.HorizontalInfo[j].Message[1]=DEFAULT_HORIZONTAL[j].toString();
                frame.HorizontalInfo[j].Font="14px 微软雅黑";
            }

            var subFrame=new SubFrameItem();
            subFrame.Frame=frame;
            if (i==0)
                subFrame.Height=20;
            else
                subFrame.Height=10;

            this.Frame.SubFrame[i]=subFrame;
        }
    }

}


////////////////////////////////////////////////////////////////////////////////
//  简单的图形框架
//
function SimlpleChartContainer(uielement)
{
    this.newMethod=JSChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.MainDataControl;    //主数据类(对外的接口类)
    //this.SubDataControl=new Array();

    //创建
    this.Create=function()
    {
        this.UIElement.JSChartContainer=this;

        //创建十字光标
        //this.ChartCorssCursor=new ChartCorssCursor();
        //this.ChartCorssCursor.Canvas=this.Canvas;
        //this.ChartCorssCursor.StringFormatX=new HQDateStringFormat();
        //this.ChartCorssCursor.StringFormatY=new HQPriceStringFormat();

        //创建等待提示
        this.ChartSplashPaint = new ChartSplashPaint();
        this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new SimpleChartFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=30;
        this.Frame.ChartBorder.Left=5;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;
        if (this.ChartCorssCursor) this.ChartCorssCursor.Frame=this.Frame; //十字光标绑定框架
        this.ChartSplashPaint.Frame = this.Frame;

        this.CreateMainChart();

        this.UIElement.addEventListener("keydown", OnKeyDown, true);    //键盘消息
    }

    this.SetMainDataConotrl=function(dataControl)
    {
        if (!dataControl) return;

        this.MainDataControl=dataControl;
        this.ChartPaint=[]; //图形

        this.CreateMainChart();
        this.Draw();
        this.RequestData();
    }

    //创建主数据画法
    this.CreateMainChart=function()
    {
        if (!this.MainDataControl) return;

        for(let i in this.MainDataControl.DataType)
        {
           let item=this.MainDataControl.DataType[i];
           if (item.Type=="BAR")
           {
               var chartItem=new ChartBar();
               chartItem.Canvas=this.Canvas;
               chartItem.ChartBorder=this.Frame.ChartBorder;
               chartItem.ChartFrame=this.Frame;
               chartItem.Name=item.Name;
               if (item.Color) chartItem.UpBarColor=item.Color;
               if (item.Color2) chartItem.DownBarColor=item.Color2;

               this.ChartPaint.push(chartItem);
           }
           else if (item.Type=="LINE")
           {
                var chartItem=new ChartLine();
                chartItem.Canvas=this.Canvas;
                chartItem.ChartBorder=this.Frame.ChartBorder;
                chartItem.ChartFrame=this.Frame;
                chartItem.Name=item.Name;
                if (item.Color) chartItem.Color=item.Color;

                this.ChartPaint.push(chartItem);
           }
        }

        this.Frame.YSplitOperator=new FrameSplitY();
        this.Frame.YSplitOperator.FrameSplitData=this.FrameSplitData.get('double');
        this.Frame.YSplitOperator.Frame=this.Frame;
        this.Frame.YSplitOperator.ChartBorder=this.Frame.ChartBorder;

        this.Frame.XSplitOperator=new FrameSplitXData();
        this.Frame.XSplitOperator.Frame=this.Frame;
        this.Frame.XSplitOperator.ChartBorder=this.Frame.ChartBorder;


       // this.TitlePaint[0]=new DynamicKLineTitlePainting();
       // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame;
       // this.TitlePaint[0].Canvas=this.Canvas;
    }

    this.RequestData=function()
    {
        if(!this.MainDataControl) return;

        this.MainDataControl.JSChartContainer=this;
        this.MainDataControl.RequestData();
    }

    this.UpdateMainData=function(dataControl)
    {   

        let lCount=0;
        for(let i in dataControl.Data)
        {
            let itemData=new ChartData();
            itemData.Data=dataControl.Data[i];
            this.ChartPaint[i].Data=itemData;
            if (lCount<itemData.Data.length) lCount=itemData.Data.length;
        }

        this.Frame.XPointCount=lCount;
        this.Frame.Data=this.ChartPaint[0].Data;
        this.Frame.XData=dataControl.XData;

        this.UpdateFrameMaxMin();               //调整坐标最大 最小值
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

}

////////////////////////////////////////////////////////////////////////////////
//  饼图图形框架
//
function PieChartContainer(uielement)
{
    this.Radius;    //半径
    this.newMethod=JSChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.MainDataControl;    //主数据类(对外的接口类)

    //鼠标移动
    this.OnMouseMove=function(x,y,e)
    {

    }

    //创建
    this.Create=function()
    {
        this.UIElement.JSChartContainer=this;

        //创建等待提示
        this.ChartSplashPaint = new ChartSplashPaint();
        this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new NoneFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=30;
        this.Frame.ChartBorder.Left=5;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;

        this.ChartSplashPaint.Frame = this.Frame;
        this.CreateMainChart();
    }

    this.SetMainDataConotrl=function(dataControl)
    {
        if (!dataControl) return;

        this.MainDataControl=dataControl;
        this.ChartPaint=[]; //图形

        this.CreateMainChart();
        this.Draw();
        this.RequestData();
    }

    //创建主数据画法
    this.CreateMainChart=function()
    {
        if (!this.MainDataControl) return;

        for(let i in this.MainDataControl.DataType)
        {
           let item=this.MainDataControl.DataType[i];
           if (item.Type=="PIE")    //饼图
           {

               var chartItem=new ChartPie();
               chartItem.Canvas=this.Canvas;
               chartItem.ChartBorder=this.Frame.ChartBorder;
               chartItem.ChartFrame=this.Frame;
               chartItem.Name=item.Name;

               if(this.Radius) chartItem.Radius = this.Radius;

               this.ChartPaint.push(chartItem);
           }
           else if (item.Type=='RADAR') //雷达图
           {
                var chartItem=new ChartRadar();
                chartItem.Canvas=this.Canvas;
                chartItem.ChartBorder=this.Frame.ChartBorder;
                chartItem.ChartFrame=this.Frame;
                chartItem.Name=item.Name;
                if (item.StartAngle) chartItem.StartAngle=item.StartAngle;
                this.ChartPaint.push(chartItem);
           }
        }

       // this.TitlePaint[0]=new DynamicKLineTitlePainting();
       // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame;
       // this.TitlePaint[0].Canvas=this.Canvas;
    }

    this.RequestData=function()
    {
        if(!this.MainDataControl) return;

        this.MainDataControl.JSChartContainer=this;
        this.MainDataControl.RequestData();
    }

    this.UpdateMainData=function(dataControl)
    {
        for(let i in dataControl.Data)
        {
            let itemData=new ChartData();
            itemData.Data=dataControl.Data[i];
            this.ChartPaint[i].Data=itemData;
        }
        this.Frame.SetSizeChage(true);
        this.Draw();
    }

}

//地图
function MapChartContainer(uielement)
{
    this.newMethod=JSChartContainer;   //派生
    this.newMethod(uielement);
    delete this.newMethod;

    this.MainDataControl;    //主数据类(对外的接口类)

    //鼠标移动
    this.OnMouseMove=function(x,y,e)
    {

    }

    //创建
    this.Create=function()
    {
        this.UIElement.JSChartContainer=this;

        //创建等待提示
        this.ChartSplashPaint = new ChartSplashPaint();
        this.ChartSplashPaint.Canvas = this.Canvas;

        //创建框架容器
        this.Frame=new NoneFrame();
        this.Frame.ChartBorder=new ChartBorder();
        this.Frame.ChartBorder.UIElement=this.UIElement;
        this.Frame.ChartBorder.Top=30;
        this.Frame.ChartBorder.Left=5;
        this.Frame.ChartBorder.Bottom=20;
        this.Frame.Canvas=this.Canvas;

        this.ChartSplashPaint.Frame = this.Frame;
        this.CreateMainChart();
    }

    this.SetMainDataConotrl=function(dataControl)
    {
        if (!dataControl) return;

        this.MainDataControl=dataControl;
        this.ChartPaint=[]; //图形

        this.CreateMainChart();
        this.Draw();
        this.RequestData();
    }

    //创建主数据画法
    this.CreateMainChart=function()
    {
        if (!this.MainDataControl) return;

        let chartItem=new ChartChinaMap();
        chartItem.Canvas=this.Canvas;
        chartItem.ChartBorder=this.Frame.ChartBorder;
        chartItem.ChartFrame=this.Frame;
        chartItem.Name=this.MainDataControl.DataType[0].Name;

        if(this.Radius) chartItem.Radius = this.Radius;

        this.ChartPaint.push(chartItem);
           
       // this.TitlePaint[0]=new DynamicKLineTitlePainting();
       // this.TitlePaint[0].Frame=this.Frame.SubFrame[0].Frame;
       // this.TitlePaint[0].Canvas=this.Canvas;
    }

    this.RequestData=function()
    {
        if(!this.MainDataControl) return;

        this.MainDataControl.JSChartContainer=this;
        this.MainDataControl.RequestData();
    }

    this.UpdateMainData=function(dataControl)
    {
        this.ChartPaint[0].Data=dataControl.Data[0];

        this.Frame.SetSizeChage(true);
        this.Draw();
    }
}




//////////////////////////////////////////////////////////
//
//  指标信息
//
function IndexInfo(name,param)
{
    this.Name=name;                 //名字
    this.Param=param;               //参数
    this.LineColor;                 //线段颜色
    this.ReqeustData=null;          //数据请求
}

function BaseIndex(name)
{
    this.Index;         //指标阐述
    this.Name=name;     //指标名字
    this.Script=null;   //通达信脚本
    
    this.OverlayIndex=null; //叠加指标{ IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }

    //默认创建都是线段
    this.Create=function(hqChart,windowIndex)
    {
        
    }

    //指标不支持 周期/复权/股票等
    this.NotSupport=function(hqChart,windowIndex,message)
    {
        var isOverlay=this.IsOverlay();
        if (isOverlay) return;

        hqChart.DeleteIndexPaint(windowIndex);  //清空
        var frame=hqChart.Frame.SubFrame[windowIndex].Frame;

        var paint=new ChartErrorMessage();
        paint.Canvas=hqChart.Canvas;
        paint.ChartBorder=frame.ChartBorder;
        paint.ChartFrame=frame;
        paint.NotSupportMessage=message;

        hqChart.ChartPaint.push(paint);
    }

    //格式化指标名字+参数
    //格式:指标名(参数1,参数2,参数3,...)
    this.FormatIndexTitle=function()
    {
        var title=this.Name;
        var param=null;

        for(var i in this.Index)
        {
            var item = this.Index[i];
            if (item.Param==null) continue;

            if (param)
                param+=','+item.Param.toString();
            else
                param=item.Param.toString();
        }

        if (param)
        {
            title+='('+param+')';
        }

        return title;
    }

    this.IsOverlay=function()   //是否是叠加指标
    {
        if (!this.OverlayIndex || this.OverlayIndex.IsOverlay!=true) return false;

        return true;
    }

    this.CreateChart=function(id)   //创建图形
    {
        return new ChartLine();
    }

    this.CreatePaints=function(hqChart,windowIndex)
    {
        var frame=null;
        var isOverlay=this.IsOverlay();
        if (isOverlay) 
        {
            frame=this.OverlayIndex.Frame.Frame;
            this.OverlayIndex.Frame.ChartPaint=[];    //清空
        }
        else 
        {
            frame=hqChart.Frame.SubFrame[windowIndex].Frame;
            hqChart.DeleteIndexPaint(windowIndex);  //清空
        }
        
        var aryPaint=[];
        for(var i in this.Index)
        {
            var paint=this.CreateChart(i);
            paint.Color=this.Index[i].LineColor;
            paint.Canvas=hqChart.Canvas;
            paint.Name=this.Name+"-"+i.toString();
            paint.ChartBorder=frame.ChartBorder;
            paint.ChartFrame=frame

            if (isOverlay) this.OverlayIndex.Frame.ChartPaint.push(paint);
            else hqChart.ChartPaint.push(paint);

            aryPaint.push(paint);
        }

        return aryPaint;
    }

    this.SendEvent=function(hqChart,windowIndex,hisData, error)
    {
        if (!hqChart.GetIndexEvent) return;
        var event=hqChart.GetIndexEvent();  //指标计算完成回调
        if (!event) return;

        var data={ Data:this.GetOutData(), WindowIndex: windowIndex, Name: this.Name, HistoryData: hisData, 
            Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} };
        if (error) data.Error=error;
        event.Callback(event,data,self);
    }

    this.GetOutData=function()
    {
        return null;
    }
}

//市场多空
function MarketLongShortIndex()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('Long-Short');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("多头区域",null),
        new IndexInfo("空头区域",null),
        new IndexInfo("市场多空指标",null),
    );

    this.Index[0].LineColor=g_JSChartResource.UpBarColor;
    this.Index[1].LineColor=g_JSChartResource.DownBarColor;
    this.Index[2].LineColor=g_JSChartResource.Index.LineColor[0];

    this.LongShortData; //多空数据

    this.CreateChart=function(id) 
    {
        if (id==2){
          var newChartLine = new ChartLine();
          newChartLine.LineWidth = 2;
          return newChartLine;
        }

        return new ChartStraightLine();
    }

    this.GetOutData=function()
    {
        return { LongShortData:this.LongShortData };
    }

    //请求数据
    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        this.LongShortData=[];

        if (param.HQChart.Period>0)   //周期数据
        {
            this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换");
            param.HQChart.Draw();
            this.SendEvent(hqChart,windowIndex,hisData,"不支持周期切换");
            return false;
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.Index.MarketLongShortApiUrl,
            data:
            {

            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
                self.SendEvent(hqChart,windowIndex,hisData);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.data.length<=0) return;

        var aryData=new Array();
        for(var i in recvData.data)
        {
            var item=recvData.data[i];
            var indexData=new SingleData();
            indexData.Date=item[0];
            indexData.Value=item[1];
            aryData.push(indexData);
        }

        var aryFittingData=param.HistoryData.GetFittingData(aryData);

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        bindData.Period=param.HQChart.Period;   //周期
        bindData.Right=param.HQChart.Right;     //复权

        this.LongShortData=bindData.GetValue();
        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }


    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=this.CreatePaints(hqChart,windowIndex);
        var isOverlay=this.IsOverlay();
        if (paint.length!=this.Index.length) return false;

        //paint[0].Data.Data=SWLData;
        paint[2].Data.Data=this.LongShortData;
        paint[0].Data.Data[0]=8;
        paint[1].Data.Data[0]=1;

        var titleIndex=windowIndex+1;

        //指定[0,9]
        if (isOverlay) 
        {
            this.OverlayIndex.Frame.Frame.YSpecificMaxMin={Max:9,Min:0,Count:3};
            var titlePaint=hqChart.TitlePaint[titleIndex];
            var titleInfo={ Data:[], Title:'' };
            titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo);
            for(var i in paint)
            {
                var titleData=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor);
                if (i!=2) titleData.DataType="StraightLine";
                titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData;
            }
        }
        else  
        {
            hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin={Max:9,Min:0,Count:3};
            for(var i in paint)
            {
                hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor);
                if (i!=2) hqChart.TitlePaint[titleIndex].Data[i].DataType="StraightLine";
            }
        }

        return true;
    }

}

//市场择时
function MarketTimingIndex()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('Market-Timing');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("因子择时",null)
    );

    this.TimingData; //择时数据
    this.TitleColor=g_JSChartResource.FrameSplitTextColor

    this.CreateChart=function(id) 
    {
        return new ChartMACD();
    }

    this.GetOutData=function()
    {
        return { TimingData:this.TimingData };
    }

    //请求数据
    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        this.LongShortData=[];

        if (param.HQChart.Period>0)   //周期数据
        {
            this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换");
            param.HQChart.Draw();
            this.SendEvent(hqChart,windowIndex,hisData,"不支持周期切换");
            return false;
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.Index.MarketLongShortApiUrl,
            data:
            {

            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
                self.SendEvent(hqChart,windowIndex,hisData);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.data.length<=0) return;

        var aryData=new Array();
        for(var i in recvData.data)
        {
            var item=recvData.data[i];
            var indexData=new SingleData();
            indexData.Date=item[0];
            indexData.Value=item[2];
            aryData.push(indexData);
        }

        var aryFittingData=param.HistoryData.GetFittingData(aryData);

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        bindData.Period=param.HQChart.Period;   //周期
        bindData.Right=param.HQChart.Right;     //复权

        this.TimingData=bindData.GetValue();
        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }


    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=this.CreatePaints(hqChart,windowIndex);
        var isOverlay=this.IsOverlay();

        if (paint.length!=this.Index.length) return false;

        //paint[0].Data.Data=SWLData;
        paint[0].Data.Data=this.TimingData;
        paint[0].NotSupportMessage=null;

        var titleIndex=windowIndex+1;

        if (isOverlay) 
        {
            var titlePaint=hqChart.TitlePaint[titleIndex];
            var titleInfo={ Data:[], Title:'' };
            titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo);
            for(var i in paint)
            {
                var titleData=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor);
                titleData.StringFormat=STRING_FORMAT_TYPE.THOUSANDS;
                titleData.FloatPrecision=0;
                titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData;
            }
        }
        else
        {
            for(var i in paint)
            {
                hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.TitleColor);
                hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.THOUSANDS;
                hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=0;
            }
        }

        return true;
    }
}

//市场关注度
function MarketAttentionIndex()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('Market-Attention');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("市场关注度",null)
    );

    this.Data; //关注度数据
    this.TitleColor=g_JSChartResource.FrameSplitTextColor;
    this.ApiUrl=g_JSChartResource.Index.MarketAttentionApiUrl;

    this.CreateChart=function(id) 
    {
        return new ChartMACD();
    }

    this.GetOutData=function()
    {
        return { Data:this.Data };
    }

    //调整框架
    this.SetFrame=function(hqChart,windowIndex,hisData)
    {
        var isOverlay=this.IsOverlay();
        if (isOverlay)
            this.OverlayIndex.Frame.Frame.YSpecificMaxMin={Max:6,Min:0,Count:3};
        else
            hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin={Max:6,Min:0,Count:3};
    }

    //请求数据
    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        this.Data=[];

        if (param.HQChart.Period>0)   //周期数据
        {
            this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换");
            param.HQChart.Draw();
            this.SendEvent(hqChart,windowIndex,hisData,"不支持周期切换");
            return false;
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: this.ApiUrl,
            data:
            {
               "symbol":param.HQChart.Symbol,
               "startdate":20100101,
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
                self.SendEvent(hqChart,windowIndex,hisData);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.date.length<0) return;

        var aryData=new Array();
        for(var i in recvData.date)
        {
            var indexData=new SingleData();
            indexData.Date=recvData.date[i];
            indexData.Value=recvData.value[i];
            aryData.push(indexData);
        }

        var aryFittingData=param.HistoryData.GetFittingData(aryData);

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        bindData.Period=param.HQChart.Period;   //周期
        bindData.Right=param.HQChart.Right;     //复权

        this.Data=bindData.GetValue();
        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);
        this.SetFrame(param.HQChart,param.WindowIndex,param.HistoryData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }


    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=this.CreatePaints(hqChart,windowIndex);
        var isOverlay=this.IsOverlay();

        if (paint.length!=this.Index.length) return false;

        //paint[0].Data.Data=SWLData;
        paint[0].Data.Data=this.Data;
        paint[0].NotSupportMessage=null;

        var titleIndex=windowIndex+1;
        if (isOverlay) 
        {
            var titlePaint=hqChart.TitlePaint[titleIndex];
            var titleInfo={ Data:[], Title:'' };
            titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo);
            for(var i in paint)
            {
                var titleData=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor);
                titleData.StringFormat=STRING_FORMAT_TYPE.THOUSANDS;
                titleData.FloatPrecision=0;
                titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData;
            }
        }
        else
        {
            for(var i in paint)
            {
                hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.TitleColor);
                hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.THOUSANDS;
                hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=0;
            }
        }

        return true;
    }
}


/*
    行业,指数热度
*/
function MarketHeatIndex()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('Market-Heat');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("热度",5),
        new IndexInfo('MA',10),
        new IndexInfo('MA',null)
    );

    this.Data; //关注度数据

    this.ApiUrl=g_JSChartResource.Index.MarketHeatApiUrl;

    this.Index[0].LineColor=g_JSChartResource.FrameSplitTextColor;
    this.Index[1].LineColor=g_JSChartResource.Index.LineColor[0];
    this.Index[2].LineColor=g_JSChartResource.Index.LineColor[1];

    this.CreateChart=function(id) 
    {
        if (id==0) return new ChartMACD();

        return new ChartLine();
    }

    //请求数据
    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        this.Data=[];

        if (param.HQChart.Period>0)   //周期数据
        {
            this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换");
            param.HQChart.Draw();
            return false;
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: this.ApiUrl,
            data:
            {
               "symbol":param.HQChart.Symbol,
               "startdate":20100101,
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.date.length<0) return;

        var aryData=new Array();
        for(var i in recvData.date)
        {
            var indexData=new SingleData();
            indexData.Date=recvData.date[i];
            indexData.Value=recvData.value[i];
            aryData.push(indexData);
        }

        var aryFittingData=param.HistoryData.GetFittingData(aryData);

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        bindData.Period=param.HQChart.Period;   //周期
        bindData.Right=param.HQChart.Right;     //复权

        this.Data=bindData.GetValue();
        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }


    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=this.CreatePaints(hqChart,windowIndex);
        var isOverlay=this.IsOverlay();

        if (paint.length!=this.Index.length) return false;

        paint[0].Data.Data=this.Data;
        paint[0].NotSupportMessage=null;

        var MA=HQIndexFormula.MA(this.Data,this.Index[0].Param);
        paint[1].Data.Data=MA;

        var MA2=HQIndexFormula.MA(this.Data,this.Index[1].Param);
        paint[2].Data.Data=MA2;

        var titleIndex=windowIndex+1;

        if (isOverlay) 
        {
            var titlePaint=hqChart.TitlePaint[titleIndex];
            var titleInfo={ Data:[], Title:'' };
            titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo);
            for(var i in paint)
            {
                var name=''
                if(i==0) name=hqChart.Name+this.Index[i].Name;
                else name="MA"+this.Index[i-1].Param;
                var titleData=new DynamicTitleData(paint[i].Data,name,this.Index[i].LineColor);
                titleData.StringFormat=STRING_FORMAT_TYPE.DEFAULT;
                titleData.FloatPrecision=2;
                titlePaint.OverlayIndex.get(this.OverlayIndex.Identify).Data[i]=titleData;
            }
        }
        else
        {
            for(var i in paint)
            {
                var name="";    //显示的名字特殊处理
                if(i==0) name=hqChart.Name+this.Index[i].Name;
                else name="MA"+this.Index[i-1].Param;

                hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,name,this.Index[i].LineColor);
                hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.DEFAULT;
                hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=2;
            }
        }

        //hqChart.TitlePaint[titleIndex].Explain="热度说明";

        return true;
    }

}

//自定义指数热度
function CustonIndexHeatIndex()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('Market-Heat');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo('区域',3),
        new IndexInfo("热度指数",10),
        new IndexInfo('MA',5),
        new IndexInfo('MA',10)
    );

    this.Data; //关注度数据

    this.ApiUrl=g_JSChartResource.Index.CustomIndexHeatApiUrl;

    this.Index[1].LineColor=g_JSChartResource.Index.LineColor[1];
    this.Index[2].LineColor=g_JSChartResource.Index.LineColor[2];
    this.Index[3].LineColor=g_JSChartResource.Index.LineColor[3];

    this.Create=function(hqChart,windowIndex)
    {
        for(var i in this.Index)
        {
            var paint=null;
            if (i==0) 
            {
                paint = new ChartStraightArea();
            }
            else 
            {
                paint=new ChartLine();
                paint.Color=this.Index[i].LineColor;
            }

            paint.Canvas=hqChart.Canvas;
            paint.Name=this.Name+"-"+i.toString();
            paint.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
            paint.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

            hqChart.ChartPaint.push(paint);
        }
    }

    //请求数据
    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        this.Data=[];

        if (param.HQChart.Period>0)   //周期数据
        {
            this.NotSupport(param.HQChart,param.WindowIndex,"不支持周期切换");
            param.HQChart.Draw();
            return false;
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: this.ApiUrl,
            data:
            {
               "stock":param.HQChart.CustomStock,
               "date":{"startdate":param.HQChart.QueryDate.Start,"enddate":param.HQChart.QueryDate.End},
               "day":[this.Index[0].Param,this.Index[1].Param],
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.data==null || recvData.data.length<0) return;

        //JSConsole.Chart.Log(recvData.data);
        var aryData=new Array();
        for(let i in recvData.data)
        {
            let item=recvData.data[i];
            let indexData=new SingleData();
            indexData.Date=item[0];
            indexData.Value=item[1];
            aryData.push(indexData);
        }

        var aryFittingData=param.HistoryData.GetFittingData(aryData);

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        bindData.Period=param.HQChart.Period;   //周期
        bindData.Right=param.HQChart.Right;     //复权

        this.Data=bindData.GetValue();
        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }


    this.BindData=function(hqChart,windowIndex,hisData)
    {
        let paint=hqChart.GetChartPaint(windowIndex);

        if (paint.length!=this.Index.length) return false;

        paint[0].NotSupportMessage=null;
        paint[0].Data.Data=
        [
          { Value: 0, Value2: 0.2, Color: 'rgb(50,205,50)', Title: '热度1', TitleColor:'rgb(245,255 ,250)'},
          { Value: 0.2, Value2: 0.4, Color: 'rgb(255,140,0)', Title: '热度2', TitleColor:'rgb(245,255 ,250)'},
          { Value: 0.4, Value2: 0.8, Color: 'rgb(255,106,106)', Title: '热度3', TitleColor:'rgb(245,255 ,250)'},
          { Value: 0.8, Value2: 1, Color: 'rgb(208, 32 ,144)', Title: '热度4', TitleColor:'rgb(245,255 ,250)'}
        ];
        
        paint[1].Data.Data = this.Data;
        
        let MA=HQIndexFormula.MA(this.Data,this.Index[2].Param);
        paint[2].Data.Data=MA;

        let MA2=HQIndexFormula.MA(this.Data,this.Index[3].Param);
        paint[3].Data.Data=MA2;

         //指定框架最大最小[0,1]
         hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 1, Min: 0, Count: 3 };

        let titleIndex=windowIndex+1;

        for(let i=1;i<paint.length;++i)
        {
            let name=this.Index[i].Name;    //显示的名字特殊处理
            if (name=='MA') name="MA"+this.Index[i].Param;

            hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,name,this.Index[i].LineColor);
            hqChart.TitlePaint[titleIndex].Data[i].StringFormat=STRING_FORMAT_TYPE.DEFAULT;
            hqChart.TitlePaint[titleIndex].Data[i].FloatPrecision=2;
        }

        hqChart.TitlePaint[titleIndex].Title='热度'+'('+this.Index[0].Param+','+this.Index[1].Param+','+this.Index[2].Param+','+this.Index[3].Param+')';

        return true;
    }

}


/*
    本福特系数(财务粉饰)
*/
function BenfordIndex()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('财务粉饰');
    delete this.newMethod;

    this.Index = new Array(
        new IndexInfo('区域', null),
        new IndexInfo("系数", null)
      );

    this.Data; //财务数据

    this.ApiUrl=g_JSChartResource.Index.StockHistoryDayApiUrl;

    this.Index[0].LineColor=g_JSChartResource.Index.LineColor[0];
    this.Index[1].LineColor='rgb(105,105,105)';

    this.CreateChart=function(id) 
    {
        if (id==0) return new ChartStraightArea();

        return new ChartLineMultiData();
    }

    this.GetOutData=function()
    {
        return { Data:this.Data };
    }

    //请求数据
    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        this.Data=[];

        if (param.HQChart.Period!=2)   //周期数据
        {
            this.NotSupport(param.HQChart,param.WindowIndex,"只支持月线");
            param.HQChart.Draw();
            this.SendEvent(hqChart,windowIndex,hisData,"只支持月线");
            return false;
        }

        var aryField=["finance.benford","announcement2.quarter","announcement1.quarter","announcement3.quarter","announcement4.quarter"];
        var aryCondition=
            [  
                {item:["date","int32","gte","20130101"]},
                {item:[ "announcement1.year","int32","gte",0,
                        "announcement2.year","int32","gte",0,
                        "announcement3.year","int32","gte",0,
                        "announcement4.year","int32","gte",0,
                        "or"]}
            ];
        //请求数据
        JSNetwork.HttpRequest({
            url: this.ApiUrl,
            data:
            {
               "symbol":[param.HQChart.Symbol],
               "field":aryField,
               "condition":aryCondition
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
                self.SendEvent(hqChart,windowIndex,hisData);
            }
        });

        return true;
    }

    this.JsonDataToMapSingleData=function(recvData)
    {
        var stockData=recvData.stock[0].stockday;
        var mapData=new Map();
        for(var i in stockData)
        {
            var item=stockData[i];
            var indexData=new SingleData();
            indexData.Date=item.date;
            indexData.Value=new Array();
            if (item.finance1!=null && item.announcement1!=null)
            {
                let year=item.announcement1.year;
                let quarter=item.announcement1.quarter;
                let value=item.finance1.benford;
                indexData.Value.push({Year:year,Quarter:quarter,Value:value});
            }
            if (item.finance2!=null && item.announcement2!=null)
            {
                let year=item.announcement2.year;
                let quarter=item.announcement2.quarter;
                let value=item.finance2.benford;
                indexData.Value.push({Year:year,Quarter:quarter,Value:value});
            }
            if (item.finance3!=null && item.announcement3!=null)
            {
                let year=item.announcement3.year;
                let quarter=item.announcement3.quarter;
                let value=item.finance3.benford;
                indexData.Value.push({Year:year,Quarter:quarter,Value:value});
            }
            if (item.finance4!=null && item.announcement4!=null)
            {
                let year=item.announcement4.year;
                let quarter=item.announcement4.quarter;
                let value=item.finance4.benford;
                indexData.Value.push({Year:year,Quarter:quarter,Value:value});
            }

            mapData.set(indexData.Date,indexData);
        }

        var aryData=new Array();
        for( var item of mapData)
        {
            aryData.push(item[1]);
        }

        return aryData;
    }

    this.RecvData=function(recvData,param)
    {
        JSConsole.Chart.Log(recvData);
        if (recvData.stock==null || recvData.stock.length<=0) return;

        var aryData=this.JsonDataToMapSingleData(recvData);

        var aryFittingData=param.HistoryData.GetFittingMonthData(aryData);

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        bindData.Period=param.HQChart.Period;   //周期
        bindData.Right=param.HQChart.Right;     //复权

        this.Data=bindData.GetValue();
        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }


    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=this.CreatePaints(hqChart,windowIndex);

        if (paint.length!=this.Index.length) return false;

        paint[0].NotSupportMessage = null;

        paint[0].Data.Data=
        [
          { Value: 0, Value2: 0.2, Color: 'rgb(50,205,50)', Title: '安全区', TitleColor:'rgb(245,255 ,250)'},
          { Value: 0.2, Value2: 0.4, Color: 'rgb(255,140,0)', Title: '预警区', TitleColor:'rgb(245,255 ,250)'},
          { Value: 0.4, Value2: 1, Color: 'rgb(255,106,106)', Title: '警示区', TitleColor:'rgb(245,255 ,250)'}
        ];
        
        paint[1].Data.Data = this.Data;
        
        //指定框架最大最小[0,1]
        hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin = { Max: 1, Min: 0, Count: 3 };
    
        var titleIndex = windowIndex + 1;
    
        hqChart.TitlePaint[titleIndex].Data[1] = new DynamicTitleData(paint[1].Data, this.Index[1].Name, this.Index[1].LineColor);
        hqChart.TitlePaint[titleIndex].Data[1].DataType = "MultiReport";
         
        hqChart.TitlePaint[titleIndex].Title = this.FormatIndexTitle();

        return true;
    }
}



///////////////////////////////////////////////////////////////////////
//  能图指标
//

/*
    大盘趋势函数
    个股趋势函数

    上线:=SMA(C,6.5,1);
    下线:=SMA(C,13.5,1);
    STICKLINE(上线>下线 , 上线,下线 ,2, 0),COLORRED,LINETHICK2;
    STICKLINE(下线>上线,上线,下线,2,0),COLORBLUE,LINETHICK2;
*/

function LighterIndex1()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('Lighter-Trend');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("STICKLINE",null),
        new IndexInfo('STICKLINE',null)
    );

    this.Index[0].LineColor='rgb(255,0,0)';
    this.Index[1].LineColor='rgb(0,0,255)';

    this.Create=function(hqChart,windowIndex)
    {
        for(var i in this.Index)
        {
            var paint=new ChartStickLine();
            paint.Canvas=hqChart.Canvas;
            paint.Name=this.Name;
            paint.Name=this.Name+'-'+i.toString();
            paint.Color=this.Index[i].LineColor;
            paint.LineWidth=2;
            paint.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
            paint.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
            hqChart.ChartPaint.push(paint);
        }
    }

    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=hqChart.GetChartPaint(windowIndex);

        if (paint.length!=2) return false;

        var closeData=hisData.GetClose();
        var upData=HQIndexFormula.SMA(closeData,6.5,1);
        var downData=HQIndexFormula.SMA(closeData,13.5,1);

        var stickLine=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(upData,downData),upData,downData);;
        var stickLine2=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(downData,upData),upData,downData);;

        paint[0].Data.Data=stickLine;
        paint[1].Data.Data=stickLine2;

        var titleIndex=windowIndex+1;
        hqChart.TitlePaint[titleIndex].Title="大盘/个股 趋势函数";

        return true;
    }
}

/*
    位置研判函数
    N:=34;M:=3;
    28,COLORFFFFFF;
    STICKLINE(C>0,0,2,5,0),COLOR00008A;
    STICKLINE(C>0,2,5,5,0),COLOR85008A;
    STICKLINE(C>0,5,10,5,0),COLOR657600;
    STICKLINE(C>0,10,21.5,5,0),COLOR690079;
    STICKLINE(C>0,21.5,23,5,0),COLOR79B715;
    STICKLINE(C>0,23,28,5,0),COLOR00008A;
    VAR1:=EMA(100*(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N)),M)/4,COLORFFFF00,LINETHICK2;

    财貌双拳:VAR1,COLORFFFF00,LINETHICK2;
    DRAWTEXT(CURRBARSCOUNT=128,1,'底部区域'),COLOR00FFFF;
    DRAWTEXT(CURRBARSCOUNT=128,3.5,'介入区域'),COLOR00FFFF;
    DRAWTEXT(CURRBARSCOUNT=128,7.5,'稳健区域'),COLOR00FFFF;
    DRAWTEXT(CURRBARSCOUNT=128,16,'高位区域'),COLOR00FFFF;
    DRAWTEXT(CURRBARSCOUNT=128,22,'风险区域'),COLOR0000FF;
    DRAWTEXT(CURRBARSCOUNT=128,25.5,'顶部区域'),COLORFF00FF;
*/

function LighterIndex2()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('位置研判函数');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("STICKLINE",34),
        new IndexInfo('STICKLINE',3),
        new IndexInfo('STICKLINE',null),
        new IndexInfo('STICKLINE',null),
        new IndexInfo('STICKLINE',null),
        new IndexInfo('STICKLINE',null),
        new IndexInfo('LINE',null),
        new IndexInfo('TEXT',null)
    );

    this.Index[0].LineColor='rgb(0,0,138)';
    this.Index[1].LineColor='rgb(133,0,138)';
    this.Index[2].LineColor='rgb(101,118,0)';
    this.Index[3].LineColor='rgb(105,0,121)';
    this.Index[4].LineColor='rgb(121,183,21)';
    this.Index[5].LineColor='rgb(0,0,138)';
    this.Index[6].LineColor='rgb(255,0,0)';

    this.Create=function(hqChart,windowIndex)
    {
        for(var i in this.Index)
        {
            var paint=null;
            if (this.Index[i].Name=='LINE')
            {
                paint=new ChartLine();
                paint.Color=this.Index[i].LineColor;
            }
            else if (this.Index[i].Name=='TEXT')
            {
                paint=new ChartText();
            }
            else
            {
                paint=new ChartStickLine();
                paint.Color=this.Index[i].LineColor;
                paint.LineWidth=5;
            }

            paint.Canvas=hqChart.Canvas;
            paint.Name=this.Name;
            paint.Name=this.Name+'-'+i.toString();
            paint.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
            paint.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
            hqChart.ChartPaint.push(paint);
        }
    }

    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=hqChart.GetChartPaint(windowIndex);

        if (paint.length!=this.Index.length) return false;

        var closeData=hisData.GetClose();
        var highData=hisData.GetHigh();
        var lowData=hisData.GetLow();

        paint[0].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),0,2);
        paint[1].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),2,5);
        paint[2].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),5,10);
        paint[3].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),10,21.5);
        paint[4].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),21.5,23,5);
        paint[5].Data.Data=HQIndexFormula.STICKLINE(HQIndexFormula.ARRAY_GT(closeData,0),23,28,5);

        //VAR1:=EMA(100*(CLOSE-LLV(LOW,N))/(HHV(HIGH,N)-LLV(LOW,N)),M)/4
        var lineData=HQIndexFormula.ARRAY_DIVIDE(
            HQIndexFormula.EMA(
                HQIndexFormula.ARRAY_MULTIPLY(
                    HQIndexFormula.ARRAY_DIVIDE(
                        HQIndexFormula.ARRAY_SUBTRACT(closeData,HQIndexFormula.LLV(lowData,this.Index[0].Param)),
                        HQIndexFormula.ARRAY_SUBTRACT(HQIndexFormula.HHV(highData,this.Index[0].Param),HQIndexFormula.LLV(lowData,this.Index[0].Param))
                    ),
                    100),
                this.Index[1].Param),
            4
        );

        paint[6].Data.Data=lineData;

        //DRAWTEXT(CURRBARSCOUNT=128,1,'底部区域'),COLOR00FFFF;
        //DRAWTEXT(CURRBARSCOUNT=128,3.5,'介入区域'),COLOR00FFFF;
        //DRAWTEXT(CURRBARSCOUNT=128,7.5,'稳健区域'),COLOR00FFFF;
        //DRAWTEXT(CURRBARSCOUNT=128,16,'高位区域'),COLOR00FFFF;
        //DRAWTEXT(CURRBARSCOUNT=128,22,'风险区域'),COLOR0000FF;
        //DRAWTEXT(CURRBARSCOUNT=128,25.5,'顶部区域'),COLORFF00FF;

        var TextData=new Array();
        TextData[0]={Value:1,   Message:'底部区域',Color:'rgb(0,255,255)',Position:'Left'};
        TextData[1]={Value:3.5, Message:'介入区域',Color:'rgb(0,255,255)',Position:'Left'};
        TextData[2]={Value:7.5, Message:'稳健区域',Color:'rgb(0,255,255)',Position:'Left'};
        TextData[3]={Value:16,  Message:'高位区域',Color:'rgb(0,255,255)',Position:'Left'};
        TextData[4]={Value:22,  Message:'风险区域',Color:'rgb(0,0,255)',Position:'Left'};
        TextData[5]={Value:25.5,Message:'顶部区域',Color:'rgb(255,0,255)',Position:'Left'};

        paint[7].Data.Data=TextData;

        var titleIndex=windowIndex+1;
        hqChart.TitlePaint[titleIndex].Data[0]=new DynamicTitleData(paint[6].Data,"财貌双拳",paint[6].Color);

        hqChart.TitlePaint[titleIndex].Title=this.FormatIndexTitle();

        return true;
    }
}


/*
    py指标 服务器端执行
*/
function PyScriptIndex(name,script,args,option)
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod(name);
    delete this.newMethod;

    this.Script=script; //脚本
    this.Arguments=[];  //参数
    this.OutVar=[];     //输出数据
    this.ApiUrl=g_JSChartResource.PyIndexDomain+'/hq/code';
    if (args) this.Arguments=args;

    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        this.OutVar=[];
        var self = this;
        var param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData
        };

        //参数
        var strParam='';
        for(let i in this.Arguments)
        {
            if (strParam.length>0) strParam+=',';
            var item=this.Arguments[i];
            strParam+='"'+item.Name+'"'+':'+item.Value;
        }
        strParam='{'+strParam+'}';
        var indexParam=JSON.parse(strParam);

        var data=JSON.stringify(
            {
                code:this.Script,   //脚本
                symbol:param.HQChart.Symbol,    //股票代码
                period:param.HQChart.Period,    //周期 0=日线 1=周线 2=月线 3=年线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟
                right:param.HQChart.Right,      //复权 0 不复权 1 前复权 2 后复权
                params:indexParam,               //指标参数
                numcount:hqChart.MaxReqeustDataCount,
            });

        //请求数据
        JSNetwork.HttpRequest({
            url: this.ApiUrl,
            data:data,
            type:"post",
            dataType: "json",
            contentType:' application/json; charset=utf-8',
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            },
            complete:function(h)
            {
                //JSConsole.Chart.Log(h);
            },
            error: function(http,e)
            {
                self.RecvError(http,e,param);;
                
            }
        });

        return true;
    }

    this.RecvError=function(http,e,param)
    {
        JSConsole.Chart.Log("[PyScriptIndex::RecvError] error",e);
        if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback(e);
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.code!=0) 
        {
            JSConsole.Chart.Log("[PyScriptIndex::RecvData] failed.", recvData);
            if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback(recvData.msg);
            return;   //失败了
        }

        JSConsole.Chart.Log('[PyScriptIndex::RecvData] recv data.',recvData);
        var aryDate=recvData.date;
        for(var i in recvData.data)
        {
            var item=recvData.data[i];
            var indexData=[];
            for(var j=0;j<aryDate.length && j<aryDate.length;++j)
            {
                if (j>=item.data.length) continue;
                var indexItem=new SingleData(); //单列指标数据
                indexItem.Date=aryDate[j];
                indexItem.Value=item.data[j];
                indexData.push(indexItem);
            }

            var aryFittingData=param.HistoryData.GetFittingData(indexData); //数据和主图K线拟合
            var bindData=new ChartData();
            bindData.Data=aryFittingData;
            bindData.Period=param.HQChart.Period;   //周期
            bindData.Right=param.HQChart.Right;     //复权

            var indexInfo={Name:item.name,Type:item.graph,LineWidth:item.width,Data:bindData.GetValue(),Color:item.color};
            this.OutVar.push(indexInfo);
        }

        this.BindData(param.HQChart,param.WindowIndex,param.HistoryData);   //把数据绑定到图形上

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();
    }

    this.BindData=function(hqChart,windowIndex,hisData)
    {
        hqChart.DeleteIndexPaint(windowIndex); //清空指标图形

        for(let i in this.OutVar)
        {
            let item=this.OutVar[i];
            switch(item.Type)
            {
                case 'line':
                this.CreateLine(hqChart,windowIndex,item,i);
                break;
                case 'colorstick':  //上下柱子
                this.CreateColorStock(hqChart,windowIndex,item,i);
                break;
            }
        }

        var titleIndex=windowIndex+1;
        hqChart.TitlePaint[titleIndex].Title=this.Name; //这是指标名称

        let indexParam='';  //指标参数
        for(let i in this.Arguments)
        {
            let item=this.Arguments[i];
            if (indexParam.length>0) indexParam+=',';
            indexParam+=item.Value.toString();
        }
        if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')';

        return true;
    }

    this.CreateLine=function(hqChart,windowIndex,varItem,id)
    {
        let line=new ChartLine();
        line.Canvas=hqChart.Canvas;
        line.DrawType=1;
        line.Name=varItem.Name;
        line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color=varItem.Color;
        else line.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth>0) line.LineWidth=varItem.LineWidth;
        if (varItem.IsShow==false) line.IsShow=false;
        
        let titleIndex=windowIndex+1;
        line.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color);

        hqChart.ChartPaint.push(line);
    }

    this.CreateColorStock=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartMACD();
        chart.Canvas=hqChart.Canvas;

        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,this.GetDefaultColor(id));

        hqChart.ChartPaint.push(chart);
    }

    //给一个默认的颜色
    this.GetDefaultColor=function(id)
    {
        let COLOR_ARRAY=
        [
            "rgb(255,174,0)",
            "rgb(25,199,255)",
            "rgb(175,95,162)",
            "rgb(236,105,65)",
            "rgb(68,114,196)",
            "rgb(229,0,79)",
            "rgb(0,128,255)",
            "rgb(252,96,154)",
            "rgb(42,230,215)",
            "rgb(24,71,178)",
        ];

        let number=parseInt(id);
        return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)];
    }

}



/*
    json格式数据指标 用来显示本地数据
*/
function JsonDataIndex(name,script,args,option)
{
    this.newMethod=PyScriptIndex;   //派生
    this.newMethod(name);
    delete this.newMethod;

    this.JsonData;  //json格式数据
    if (option.JsonData) this.JsonData=option.JsonData;

    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        if (!this.JsonData)
        {
            console.warn("[PyScriptIndex::RequestData] JsonData is null");
            if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback('json 数据不能为空');
        }
        else
        {
            var param=
            {
                HQChart:hqChart,
                WindowIndex:windowIndex,
                HistoryData:hisData
            };

            this.JsonData.code=0;
            var recvData=this.JsonData;
            this.RecvData(recvData,param);
        }
    }

}

//给一个默认的颜色
PyScriptIndex.prototype.GetDefaultColor=function(id)
{
    let COLOR_ARRAY=
    [
        "rgb(255,174,0)",
        "rgb(25,199,255)",
        "rgb(175,95,162)",
        "rgb(236,105,65)",
        "rgb(68,114,196)",
        "rgb(229,0,79)",
        "rgb(0,128,255)",
        "rgb(252,96,154)",
        "rgb(42,230,215)",
        "rgb(24,71,178)",
    ];

    let number=parseInt(id);
    return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)];
}


/*
    点位研判函数

    HJ_1:=REF(LOW,1);
    HJ_2:=SMA(ABS(LOW-HJ_1),13,1)/SMA(MAX(LOW-HJ_1,0),13,1)*100;
    HJ_3:=EMA(IF(CLOSE*1.2,HJ_2*13,HJ_2/13),13);
    HJ_4:=LLV(LOW,34);
    HJ_5:=HHV(HJ_3,34);
    HJ_6:=IF(LLV(LOW,56),1,0);
    HJ_7:=EMA(IF(LOW<=HJ_4,(HJ_3+HJ_5*2)/2,0),3)/618*HJ_6;
    HJ_8:=HJ_7>REF(HJ_7,1);
    HJ_9:=REF(LLV(LOW,100),3);
    HJ_10:=REFDATE(HJ_9,DATE);
    HJ_11:=LOW=HJ_10;
    HJ_12:=HJ_8 AND HJ_11;
    HJ_13:=HJ_12>REF(HJ_12,1);
    启动买点:HJ_13>REF(HJ_13,1),COLORRED,LINETHICK1;
*/
function LighterIndex3()
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod('点位研判函数');
    delete this.newMethod;

    this.Index=new Array(
        new IndexInfo("启动买点",null)
    );

    this.Index[0].LineColor='rgb(255,0,0)';

    this.BindData=function(hqChart,windowIndex,hisData)
    {
        var paint=hqChart.GetChartPaint(windowIndex);

        if (paint.length!=this.Index.length) return false;

        var closeData=hisData.GetClose();
        var highData=hisData.GetHigh();
        var lowData=hisData.GetLow();

        //HJ_1:=REF(LOW,1);
        var hj_1=HQIndexFormula.REF(lowData,1);

        //HJ_2:=SMA(ABS(LOW-HJ_1),13,1)/SMA(MAX(LOW-HJ_1,0),13,1)*100;
        var hj_2=HQIndexFormula.ARRAY_MULTIPLY(
            HQIndexFormula.ARRAY_DIVIDE(
                HQIndexFormula.SMA(HQIndexFormula.ABS(HQIndexFormula.ARRAY_SUBTRACT(lowData,hj_1)),13,1),
                HQIndexFormula.SMA(HQIndexFormula.MAX(HQIndexFormula.ARRAY_SUBTRACT(lowData,hj_1),0),13,1)
            ),
            100
        );

        //HJ_3:=EMA(IF(CLOSE*1.2,HJ_2*13,HJ_2/13),13);
        var hj_3=HQIndexFormula.EMA(
            HQIndexFormula.ARRAY_IF(HQIndexFormula.ARRAY_MULTIPLY(closeData,1.2),HQIndexFormula.ARRAY_MULTIPLY(hj_2,13),HQIndexFormula.ARRAY_DIVIDE(hj_2,13)),
            13
        );
        
        //HJ_4:=LLV(LOW,34);
        var hj_4=HQIndexFormula.LLV(lowData,34);

        //HJ_5:=HHV(HJ_3,34);
        var hj_5=HQIndexFormula.HHV(hj_3,34);

        //HJ_6:=IF(LLV(LOW,56),1,0);
        var hj_6=HQIndexFormula.ARRAY_IF(HQIndexFormula.LLV(lowData,56),1,0);

        //HJ_7:=EMA(IF(LOW<=HJ_4,(HJ_3+HJ_5*2)/2,0),3)/618*HJ_6;
        //hj_7_temp=(HJ_3+HJ_5*2)/2,0)  太长了 这部分单独算下
        var hj_7_temp=HQIndexFormula.ARRAY_DIVIDE(HQIndexFormula.ARRAY_ADD(hj_3,HQIndexFormula.ARRAY_MULTIPLY(hj_5,2)),2);

        var hj_7=HQIndexFormula.ARRAY_MULTIPLY(
            HQIndexFormula.ARRAY_DIVIDE(
                HQIndexFormula.EMA(
                    HQIndexFormula.ARRAY_IF(HQIndexFormula.ARRAY_LTE(lowData,hj_4),hj_7_temp,0),
                    3
                ),
                618
            ),
            hj_6
        );

        //HJ_8:=HJ_7>REF(HJ_7,1);
        var hj_8=HQIndexFormula.ARRAY_GT(hj_7,HQIndexFormula.REF(hj_7,1));

        //HJ_9:=REF(LLV(LOW,100),3);
        var hj_9=HQIndexFormula.REF(HQIndexFormula.LLV(lowData,100),3);

        //HJ_10:=REFDATE(HJ_9,DATE); 用当日的数据 产生数组
        var hj_10=HQIndexFormula.REFDATE(hj_9,-1);

        //HJ_11:=LOW=HJ_10;
        var hj_11=HQIndexFormula.ARRAY_EQ(lowData,hj_10);

        //HJ_12:=HJ_8 AND HJ_11;
        var hj_12=HQIndexFormula.ARRAY_AND(hj_8,hj_11);
       
        var buyData=null;
        paint[0].Data.Data=hj_12;

        var titleIndex=windowIndex+1;

        for(var i in paint)
        {
            hqChart.TitlePaint[titleIndex].Data[i]=new DynamicTitleData(paint[i].Data,this.Index[i].Name,this.Index[i].LineColor);
        }

        hqChart.TitlePaint[titleIndex].Title=this.FormatIndexTitle();

        return true;
    }
}


/*
    信息地雷
*/

/*
    信息地雷列表
*/
function JSKLineInfoMap()
{
}

JSKLineInfoMap.Get=function(id)
{
    var infoMap=new Map(
        [
            ["互动易",      {Create:function(){ return new InvestorInfo()}  }],
            ["公告",        {Create:function(){ return new AnnouncementInfo()}  }],
            ["业绩预告",    {Create:function(){ return new PforecastInfo()}  }],
            ["调研",        {Create:function(){ return new ResearchInfo()}  }],
            ["大宗交易",    {Create:function(){ return new BlockTrading()}  }],
            ["龙虎榜",      {Create:function(){ return new TradeDetail()}  }]
        ]
        );

    return infoMap.get(id);
}

JSKLineInfoMap.GetClassInfo=function(id)
{
    var infoMap=new Map(
        [
            ["互动易",      { ClassName:"InvestorInfo" }],
            ["公告",        { ClassName:"AnnouncementInfo" }],
            ["业绩预告",    { ClassName:"PforecastInfo" } ],
            ["调研",        { ClassName:"ResearchInfo"  }],
            ["大宗交易",    { ClassName:"BlockTrading"  }],
            ["龙虎榜",      { ClassName:"TradeDetail"}  ]
        ]
        );

    return infoMap.get(id);
}

JSKLineInfoMap.GetIconUrl=function(type)
{
    switch(type)
    {
        case KLINE_INFO_TYPE.INVESTOR:
            return g_JSChartResource.KLine.Info.Investor.Icon;
            break;
        case KLINE_INFO_TYPE.PFORECAST:
            return g_JSChartResource.KLine.Info.Pforecast.Icon;
        case KLINE_INFO_TYPE.ANNOUNCEMENT:
            return g_JSChartResource.KLine.Info.Announcement.Icon;
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1:
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2:
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3:
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4:
            return g_JSChartResource.KLine.Info.Announcement.IconQuarter;
        case KLINE_INFO_TYPE.RESEARCH:
            return g_JSChartResource.KLine.Info.Research.Icon;
        case KLINE_INFO_TYPE.BLOCKTRADING:
            return g_JSChartResource.KLine.Info.BlockTrading.Icon;
        case KLINE_INFO_TYPE.TRADEDETAIL:
            return g_JSChartResource.KLine.Info.TradeDetail.Icon;
        default:
            return g_JSChartResource.KLine.Info.Announcement.Icon;
    }
}

JSKLineInfoMap.GetIconFont=function(type)
{
    switch(type)
    {
        case KLINE_INFO_TYPE.INVESTOR:
            return g_JSChartResource.KLine.Info.Investor.IconFont;
            break;
        case KLINE_INFO_TYPE.PFORECAST:
            return g_JSChartResource.KLine.Info.Pforecast.IconFont;
        case KLINE_INFO_TYPE.ANNOUNCEMENT:
            return g_JSChartResource.KLine.Info.Announcement.IconFont;
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1:
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2:
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3:
        case KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4:
            return g_JSChartResource.KLine.Info.Announcement.IconFont2;
        case KLINE_INFO_TYPE.RESEARCH:
            return g_JSChartResource.KLine.Info.Research.IconFont;
        case KLINE_INFO_TYPE.BLOCKTRADING:
            return g_JSChartResource.KLine.Info.BlockTrading.IconFont;
        case KLINE_INFO_TYPE.TRADEDETAIL:
            return g_JSChartResource.KLine.Info.TradeDetail.IconFont;
        default:
            return g_JSChartResource.KLine.Info.Announcement.IconFont;
    }
}


function IKLineInfo()
{
    this.MaxReqeustDataCount=1000;
    this.StartDate=20160101;
    this.Data;
    this.ClassName='IKLineInfo';

    this.GetToday=function()
    {
        var date=new Date();
        var today=date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate();
        return today;
    }
}



//互动易
function InvestorInfo()
{
    this.newMethod=IKLineInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='InvestorInfo';

    this.RequestData=function(hqChart)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
        };

        this.Data=[];

        var postData=
        {
            filed: ["question","answerdate","symbol","id"],
            symbol: [param.HQChart.Symbol],
            querydate:{"StartDate":this.StartDate,"EndDate":this.GetToday()},
            start:0,
            end:this.MaxReqeustDataCount,
        };

        if (hqChart.NetworkFilter)
        {
            var obj=
            {
                Name:'InvestorInfo::RequestData', //类名::函数
                Explain:'互动易',
                Request:{ Url:g_JSChartResource.KLine.Info.Investor.ApiUrl, Type:'post', Data: { postData } }, 
                Self:this,
                HQChart:hqChart,
                PreventDefault:false
            };

            hqChart.NetworkFilter(obj, function(data) 
            { 
                self.RecvData(data,param);
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.KLine.Info.Investor.ApiUrl,
            data:postData,
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.list.length<=0) return;

        for(var i in recvData.list)
        {
            var item=recvData.list[i];
            var infoData=new KLineInfoData();
            infoData.Date=item.answerdate;
            infoData.Title=item.question;
            infoData.InfoType=KLINE_INFO_TYPE.INVESTOR;
            this.Data.push(infoData);
        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }
}


//公告
function AnnouncementInfo()
{
    this.newMethod=IKLineInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='AnnouncementInfo';
    this.ApiType=1; //0=读API 1=读OSS缓存文件

    this.RequestData=function(hqChart)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
        };

        this.Data=[];
        if (this.ApiType==1)    //取缓存文件
        {
            var url=`${g_JSChartResource.CacheDomain}/cache/analyze/shszreportlist/${param.HQChart.Symbol}.json`;
            JSNetwork.HttpRequest({
                url: url,
                type:"get",
                dataType: "json",
                async:true,
                success: function (recvData)
                {
                    self.RecvData(recvData,param);
                },
                error: function(http,e)
                {
                    self.RecvError(http,e,param);;
                }
            });
        }
        else    //取api
        {
            //请求数据
            JSNetwork.HttpRequest({
                url: g_JSChartResource.KLine.Info.Announcement.ApiUrl,
                data:
                {
                    "filed": ["title","releasedate","symbol","id"],
                    "symbol": [param.HQChart.Symbol],
                    "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()},
                    "start":0,
                    "end":this.MaxReqeustDataCount,
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (recvData)
                {
                    self.RecvData(recvData,param);
                },
                error: function(http,e)
                {
                    self.RecvError(http,e,param);;
                }
            });

        }
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.report.length<=0) return;

        for(var i in recvData.report)
        {
            var item=recvData.report[i];
            var infoData=new KLineInfoData();
            infoData.Date=item.releasedate;
            infoData.Title=item.title;
            infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT;
            for(var j in item.type)
            {
                var typeItem=item.type[j];
                switch(typeItem)
                {
                    case "一季度报告":
                        infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_1;
                        break;
                    case "半年度报告":
                        infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_2;
                        break;
                    case "三季度报告":
                        infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_3;
                        break;
                    case "年度报告":
                        infoData.InfoType=KLINE_INFO_TYPE.ANNOUNCEMENT_QUARTER_4;
                        break;
                }
            }
            this.Data.push(infoData);
        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }

    this.RecvError=function(http,e,param)
    {
        console.warn("[AnnouncementInfo::RecvError] error, http ",e, http);
        //if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback(e);
    }
}



 //业绩预告
function PforecastInfo()
{
    this.newMethod=IKLineInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='PforecastInfo';

    this.RequestData=function(hqChart)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart,
        };

        this.Data=[];

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.KLine.Info.Pforecast.ApiUrl,
            data:
            {
                "field": ["pforecast.type","pforecast.reportdate","fweek"],
                "condition":
                [
                    {"item":["pforecast.reportdate","int32","gte",this.StartDate]}
                ],
                "symbol": [param.HQChart.Symbol],
                "start":0,
                "end":this.MaxReqeustDataCount,
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.stock.length!=1) return;
        if (recvData.stock[0].stockday.length<=0) return;

        for(var i in recvData.stock[0].stockday)
        {
            var item=recvData.stock[0].stockday[i];
            if (item.pforecast.length>0)
            {
                var dataItem=item.pforecast[0];
                var infoData=new KLineInfoData();
                infoData.Date= item.date;
                infoData.Title=dataItem.type;
                infoData.InfoType=KLINE_INFO_TYPE.PFORECAST;
                infoData.ExtendData={ Type:dataItem.type, ReportDate:dataItem.reportdate}
                if(item.fweek)  //未来周涨幅
                {
                    infoData.ExtendData.FWeek={};
                    if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1;
                    if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4;
                }
                this.Data.push(infoData);
            }
        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }
}


//投资者关系 (调研)
function ResearchInfo()
{
    this.newMethod=IKLineInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='ResearchInfo';

    this.RequestData=function(hqChart)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart
        };

        this.Data=[];

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.KLine.Info.Research.ApiUrl,
            data:
            {
                "filed": ["releasedate","researchdate","level","symbol","id",'type'],
                "querydate":{"StartDate":this.StartDate,"EndDate":this.GetToday()},
                "symbol": [param.HQChart.Symbol],
                "start":0,
                "end":this.MaxReqeustDataCount,
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.list.length<=0) return;

        for(var i in recvData.list)
        {
            var item=recvData.list[i];
            var infoData=new KLineInfoData();
            infoData.ID=item.id;
            infoData.Date= item.researchdate;
            infoData.InfoType=KLINE_INFO_TYPE.RESEARCH;
            infoData.ExtendData={ Level:item.level , Type:item.type};
            this.Data.push(infoData);

        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }
}


//大宗交易
function BlockTrading()
{
    this.newMethod=IKLineInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='BlockTrading';

    this.RequestData=function(hqChart)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart
        };

        this.Data=[];

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.KLine.Info.BlockTrading.ApiUrl,
            data:
            {
                "field": ["blocktrading.price","blocktrading.vol","blocktrading.premium","fweek","price"],
                "condition":
                [
                    {"item":["date","int32","gte",this.StartDate]},
                    {"item":["blocktrading.vol","int32","gte","0"]}
                ],
                "symbol": [param.HQChart.Symbol],
                "start":0,
                "end":this.MaxReqeustDataCount,
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.stock.length!=1) return;
        if (recvData.stock[0].stockday.length<=0) return;

        for(var i in recvData.stock[0].stockday)
        {
            var item=recvData.stock[0].stockday[i];
            var infoData=new KLineInfoData();
            infoData.Date= item.date;
            infoData.InfoType=KLINE_INFO_TYPE.BLOCKTRADING;
            infoData.ExtendData=
            {
                Price:item.blocktrading.price,          //交易价格
                Premium:item.blocktrading.premium,      //溢价 (百分比%)
                Vol:item.blocktrading.vol,              //交易金额单位(万元)
                ClosePrice:item.price,                  //收盘价
            };

            if(item.fweek)  //未来周涨幅
            {
                infoData.ExtendData.FWeek={};
                if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1;
                if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4;
            }

            this.Data.push(infoData);
        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }
}



//龙虎榜
function TradeDetail()
{
    this.newMethod=IKLineInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='TradeDetail';

    this.RequestData=function(hqChart)
    {
        var self = this;
        var param=
        {
            HQChart:hqChart
        };

        this.Data=[];

        //请求数据
        JSNetwork.HttpRequest({
            url: g_JSChartResource.KLine.Info.TradeDetail.ApiUrl,
            data:
            {
                "field": ["tradedetail.typeexplain","tradedetail.type","fweek"],
                "condition":
                [
                    {"item":["date","int32","gte",this.StartDate]},
                    {"item":["tradedetail.type","int32","gte","0"]}
                ],
                "symbol": [param.HQChart.Symbol],
                "start":0,
                "end":this.MaxReqeustDataCount,
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        if (recvData.stock.length!=1) return;
        if (recvData.stock[0].stockday.length<=0) return;

        for(var i in recvData.stock[0].stockday)
        {
            var item=recvData.stock[0].stockday[i];

            var infoData=new KLineInfoData();
            infoData.Date= item.date;
            infoData.InfoType=KLINE_INFO_TYPE.TRADEDETAIL;
            infoData.ExtendData={Detail:new Array()};

            for(var j in item.tradedetail)
            {
                var tradeItem=item.tradedetail[j]; 
                infoData.ExtendData.Detail.push({"Type":tradeItem.type,"TypeExplain":tradeItem.typeexplain});
            }

            if(item.fweek)  //未来周涨幅
            {
                infoData.ExtendData.FWeek={};
                if (item.fweek.week1!=null) infoData.ExtendData.FWeek.Week1=item.fweek.week1;
                if (item.fweek.week4!=null) infoData.ExtendData.FWeek.Week4=item.fweek.week4;
            }

            this.Data.push(infoData);
        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }
}

function JSMinuteInfoMap()
{
    
}

JSMinuteInfoMap.InfoMap=new Map(
[
    ["大盘异动",      {Create:function(){ return new MarketEventInfo()}  }],
]);

JSMinuteInfoMap.Get=function(id)
{
    return JSMinuteInfoMap.InfoMap.get(id);
}

function IMinuteInfo()
{
    this.Data;
    this.ClassName='IMinuteInfo';
}

//////////////////////////////////////////////////////////////////////
//  大盘异动
// 结构 {Date:日期 Time:时间, Title:标题, Type:0 }
////////////////////////////////////////////////////////////////////
function MarketEventInfo()
{
    this.newMethod=IMinuteInfo;   //派生
    this.newMethod();
    delete this.newMethod;

    this.ClassName='MarketEventInfo';

    this.RequestData=function(hqChart)
    {
        var self = this;
        this.Data=[];
        var param=
        {
            HQChart:hqChart
        };

        var url=g_JSChartResource.CacheDomain+'/cache/analyze/shszevent/marketevent/concept/'+hqChart.TradeDate+'.json';

        if (hqChart.NetworkFilter)
        {
            var obj=
            {
                Name:'MarketEventInfo::RequestData', //类名::
                Explain:'大盘异动',
                Request:{ Url:url, Type:'Get', Data: { Date:hqChart.TradeDate, Symbol:hqChart.Symbol } }, 
                Self:this,
                PreventDefault:false
            };
            hqChart.NetworkFilter(obj, function(data) 
            { 
                self.RecvData(data,param);
                param.HQChart.UpdataChartInfo();
                param.HQChart.Draw();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: url,
            type:"get",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvData(recvData,param);
            },
            error: function(http,e)
            {
                console.warn("[MarketEventInfo::RequestData] error, http ",e, http);
            }
        });

        return true;
    }

    this.RecvData=function(recvData,param)
    {
        for(var i in recvData.event)
        {
            var event=recvData.event[i];
            for(var j in event.data)
            {
                var item=event.data[j];
                if (Array.isArray(item))
                {
                    if (item.length<2) continue; 
                    var info={Date:event.date, Time:item[0], Title:item[1], Type:0};
                    if (item.length>=3 && item[2] && typeof(item[2])=='string') info.Color=item[2];  //[3]=字体颜色
                    if (item.length>=4 && item[3] && typeof(item[3])=='string') info.BGColor=item[3];  //[3]=背景颜色
                    this.Data.push(info);
                }
                else    //新格式
                {
                    if (!IFrameSplitOperator.IsNumber(item.Date) || !IFrameSplitOperator.IsNumber(item.Time) || !item.Title) continue;
                    var info={ Date:item.Date, Time:item.Time, Title:item.Title, Type:0 };
                    if (item.Color) info.Color=item.Color;
                    if (item.BGColor) info.BGColor=item.BGColor;
                    if (IFrameSplitOperator.IsNumber(item.Price)) info.Price=item.Price;
                    this.Data.push(info);
                }
            }
        }

        param.HQChart.UpdataChartInfo();
        param.HQChart.Draw();
    }
}


//注意!!! 这个函数已经不用了
//是否是指数代码
function IsIndexSymbol(symbol)
{
    var upperSymbol=symbol.toUpperCase();
    if (upperSymbol.indexOf('.SH')>0)
    {
        upperSymbol=upperSymbol.replace('.SH','');
        if (upperSymbol.charAt(0)=='0' && parseInt(upperSymbol)<=3000) return true;

    }
    else if (upperSymbol.indexOf('.SZ')>0)
    {
        upperSymbol=upperSymbol.replace('.SZ','');
        if (upperSymbol.charAt(0)=='3' && upperSymbol.charAt(1)=='9') return true;
    }
    else if (upperSymbol.indexOf('.CI')>0)  //自定义指数
    {
        return true;
    }

    return false;
}

//注意!!! 这个函数已经不用了
//是否是基金代码
function IsFundSymbol(symbol)
{
    if (!symbol) return false;

    var upperSymbol=symbol.toUpperCase();
    if (upperSymbol.indexOf('.SH')>0)
    {
        upperSymbol=upperSymbol.replace('.SH','');  //51XXXX.sh
        if (upperSymbol.charAt(0)=='5' && upperSymbol.charAt(1)=='1') return true;
    }
    else if (upperSymbol.indexOf('.SZ')>0)
    {
        upperSymbol=upperSymbol.replace('.SZ','');  //15XXXX.sz, 16XXXX.sz, 17XXXX.sz, 18XXXX.sz
        if (upperSymbol.charAt(0)=='1' && 
            (upperSymbol.charAt(1)=='5' || upperSymbol.charAt(1)=='6' || upperSymbol.charAt(1)=='7' || upperSymbol.charAt(1)=='8') ) return true;
    }

    return false;
}

//设置窗口基类
function IDivDialog(divElement)
{
    this.DivElement=divElement;     //父节点
    this.ID=null;                   //div id
    this.TimeOut=null;              //定时器

    //隐藏窗口
    this.Hide=function()
    {
        $("#"+this.ID).hide();
    }

    //显示窗口
    this.Show=function(left,top,width,height)
    {
        //显示位置
        $("#"+this.ID).css({'display':'block','top':top+'px', "left":left+'px', "width":width+'px', "height":height+'px' });
    }
}


//修改指标
function ModifyIndexDialog(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.Title={ ID:Guid() };      //标题
    this.ParamList={ID:Guid() };   //参数列表  class='parameter-content'
    this.ParamData=[];              //{ ID:参数ID, Value:参数值}
    this.Identify;
    this.HQChart;

    //创建
    this.Create=function()
    {
        this.ID=Guid();

        var div=document.createElement('div');
        div.className='jchart-modifyindex-box';
        div.id=this.ID;
        div.innerHTML=
        "<div class='parameter'>\
            <div class='parameter-header'>\
                <span></span>\
                <strong id='close' class='icon iconfont icon-close'></strong>\
            </div>\
            <div class='parameter-content'><input/>MA</div>\
        <div class='parameter-footer'>\
            <button class='submit' >确定</button>\
            <button class='cancel' >取消</button>\
        </div>\
        </div>";

        this.DivElement.appendChild(div);

        //确定按钮
        $("#"+this.ID+" .submit").click(
            {
                divBox:this,
            },
            function(event)
            {
                event.data.divBox.Hide();
            });

        //给一个id 后面查找方便
        var titleElement=div.getElementsByTagName('span')[0];
        titleElement.id=this.Title.ID;

        var paramListElement=div.getElementsByClassName('parameter-content')[0];
        paramListElement.id=this.ParamList.ID;
    }

    //设置标题
    this.SetTitle=function(title)
    {
        $("#"+this.Title.ID).html(title);
    }

    //清空参数
    this.ClearParamList=function()
    {
        $("#"+this.ParamList.ID).empty();
        this.ParamData=[];
    }

    this.BindParam=function(chart,identify)
    {
        var windowIndex=chart.WindowIndex[identify];
        for(var i in windowIndex.Arguments)
        {
            var item=windowIndex.Arguments[i];
            if (item.Name==null || isNaN(item.Value)) break;

            var guid=Guid();
            var param = '<input class="row-line" id="'+guid+'" value="'+item.Value+'" type="number" step="1"/>'+ item.Name +'<br>';
            $("#"+this.ParamList.ID).append(param);

            this.ParamData.push({ID:guid,Value:item.Value});
        }

        //绑定参数修改事件
        var self=this;
        for(var i in this.ParamData)
        {
            var item=this.ParamData[i];
            $("#"+item.ID).mouseup(
                {
                    Chart:chart,
                    Identify:identify,
                    ParamIndex:i   //参数序号
                },
                function(event)
                {
                    var value = parseInt($(this).val());                            //获取当前操作的input属性值,转化为整型
                    var chart=self.HQChart;
                    var identify=self.Identify;
                    var paramIndex=event.data.ParamIndex;

                    chart.WindowIndex[identify].Arguments[paramIndex].Value = value;    //为参数属性重新赋值
                    chart.UpdateWindowIndex(identify);                              //调用更新窗口指标函数,参数用来定位窗口
                }
            )

            $("#"+item.ID).keyup(
                {
                    Chart:chart,
                    Identify:identify,
                    ParamIndex:i   //参数序号
                },
                function(event)
                {
                    var value = parseInt($(this).val());                            //获取当前操作的input属性值,转化为整型
                    var chart=self.HQChart;
                    var identify=self.Identify;
                    var paramIndex=event.data.ParamIndex;

                    chart.WindowIndex[identify].Arguments[paramIndex].Value = value;    //为参数属性重新赋值
                    chart.UpdateWindowIndex(identify);                              //调用更新窗口指标函数,参数用来定位窗口
                }
            )
        }
    }

    //绑定取消事件
    this.BindCancel=function()
    {
        //取消按钮事件
        var self=this;
        $("#"+this.ID+" .cancel").click(
            function()
            {
                var chart=self.HQChart;
                var identify=self.Identify;
                self.RestoreParam(chart.WindowIndex[identify]);
                chart.UpdateWindowIndex(identify);
                self.Hide();
            }
        );

        //关闭和取消是一样的
        $("#"+this.ID+" #close").click(
            function()
            {
                var chart=self.HQChart;
                var identify=self.Identify;

                self.RestoreParam(chart.WindowIndex[identify]);
                chart.UpdateWindowIndex(identify);
                self.Hide();
            }
        );
    }

    //还原参数
    this.RestoreParam=function(windowIndex)
    {
        for(var i in this.ParamData)
        {
            var item=this.ParamData[i];
            windowIndex.Arguments[i].Value=item.Value;
        }
    }

    //显示
    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        var identify=event.data.Identify;
        var dialog=chart.ModifyIndexDialog;

        if(!dialog) return;

        if (dialog.ID==null) dialog.Create();   //第1次 需要创建div
        dialog.Identify=identify;
        dialog.HQChart=chart;
        dialog.SetTitle(chart.WindowIndex[identify].Name+" 指标参数设置");      //设置标题
        dialog.ClearParamList();            //清空参数
        dialog.BindParam(chart,identify);   //绑定参数
        dialog.BindCancel();  //绑定取消和关闭事件

        //居中显示
        var pixelTatio = GetDevicePixelRatio();
        var border=chart.Frame.ChartBorder;
        var scrollPos=GetScrollPosition();
        var left=(border.GetLeft()+border.GetWidth()/2)/pixelTatio;
        var top=(border.GetTop()+border.GetHeight()/2)/pixelTatio;
        //left = left + border.UIElement.getBoundingClientRect().left+scrollPos.Left;
        //top = top+ border.UIElement.getBoundingClientRect().top+scrollPos.Top;

        dialog.Show(left,top,200,200);      //显示

    }
}

//换指标
function ChangeIndexDialog(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.DivElement=divElement;   //父节点
    this.IndexTreeApiUrl="https://opensourcecache.zealink.com/cache/hqh5/index/commonindextree.json";               //数据下载地址
    this.OverlayIndexTreeApiUrl="https://opensourcecache.zealink.com/cache/hqh5/index/commonindextree.json";        //叠加指标列表数据下载地址
    this.IsOverlayIndex=false;

    this.Create=function()
    {
        var div=document.createElement('div');
        div.className='jchart-changeindex-box';
        div.id=this.ID=Guid();
        div.innerHTML=
        '<div class="target-panel">\n' +
            '            <div class="target-header">\n' +
            '                <span>换指标</span>\n' +
            '                <strong class="close-tar icon iconfont icon-close"></strong>\n' +
            '            </div>\n' +
            '            <div class="target-content">\n' +
            '                <div class="target-left">\n' +
            '                    <input type="text">\n' +
            '                    <ul></ul>\n' +
            '                </div>\n' +
            '                <div class="target-right">\n' +
            '                    <ul></ul>\n' +
            '                </div>\n' +
            '            </div>\n' +
            '        </div>';

        this.DivElement.appendChild(div);
    }

    //下载数据 如果上次下载过可以 可以不用下载
    this.ReqeustData=function()
    {
        if($(".target-left ul li").length>0){
            return false;
        }
        var url = this.IndexTreeApiUrl;
        if (this.IsOverlayIndex==true) url=this.OverlayIndexTreeApiUrl;
        JSNetwork.HttpRequest({
            url: url,
            type: 'get',
            success: function (res) {
                var item = res.list;
                changeIndexLeftList(item);   //处理左侧list列表
                changeIndexRightList(item);  //处理右侧内容列表
            }
        });

        //处理左侧list列表
        function changeIndexLeftList(item) {
            $.each(item,function(i,result){
                var htmlList;
                htmlList = '<li>' + result.node + '</li>';
                $(".target-left ul").append(htmlList);
            });
            //默认选中第一项
            $(".target-left ul li:first-child").addClass("active-list");
        }
        //处理右侧内容列表
        function changeIndexRightList(listNum) {
            var contentHtml;
            var conData = [];
            $.each(listNum,function(index,result){
                conData.push(result.list);
            })
            //页面初始化时显示第一个列表分类下的内容
            $.each(conData[0],function (i, res) {
                contentHtml = '<li id='+res.id+'>'+ res.name +'</li>';
                $(".target-right ul").append(contentHtml);
            })
            //切换list
            $(".target-left ul").delegate("li","click",function () {
                $(this).addClass("active-list").siblings().removeClass("active-list");
                var item = $(this).index();
                $(".target-right ul").html("");
                $.each(conData[item],function (i, res) {
                    contentHtml = '<li id='+res.id+'>'+ res.name +'</li>';
                    $(".target-right ul").append(contentHtml);
                })
            })
        }
    }

    this.BindClose=function(chart)
    {
        //关闭按钮
        $("#"+this.ID+" .close-tar").click(
            {
                Chart:chart,
            },
            function(event)
            {
                var chart=event.data.Chart;
                chart.ChangeIndexDialog.Hide();
            }
        );
    }

    //搜索事件
    this.BindSearch=function(chart)
    {
        $(".target-left input").on('input',
            {
                Chart:chart
            },
            function(event)
            {
                let scriptData = new JSIndexScript();
                let result=scriptData.Search(event.target.value);

                $(".target-right ul").html("");
                for(var i in result)
                {
                    var name=result[i];
                    var contentHtml = '<li id='+name+'>'+ name +'</li>';
                    $(".target-right ul").append(contentHtml);
                }
                
            }
        );
    }

    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        var identify=event.data.Identify;
        var dialog=chart.ChangeIndexDialog;
        var isOverlay=event.data.IsOverlay; //是否叠加

        if(!dialog) return;

        if (dialog.ID==null) dialog.Create();   //第1次 需要创建div
        dialog.IsOverlayIndex=isOverlay;
        dialog.ReqeustData();   //下载数据

        //切换窗口指标类型  每次委托事件执行之前,先用undelegate()解除之前的所有绑定
        changeIndeWindow();
        function changeIndeWindow() 
        {
            $(".target-right ul").undelegate().delegate("li","click",function () {
                var idv = $(this).attr("id");
                if (isOverlay)
                    chart.AddOverlayIndex({WindowIndex:identify,IndexName:idv});
                else
                    chart.ChangeIndex(identify,idv);
                $(this).addClass("active-list").siblings().removeClass("active-list");
            });
        }

        dialog.BindSearch(chart);
        //关闭弹窗
        dialog.BindClose(chart);

        //居中显示
        var pixelTatio = GetDevicePixelRatio();
        var border=chart.Frame.ChartBorder;
        var scrollPos=GetScrollPosition();
        var left=(border.GetLeft()+border.GetWidth()/2)/pixelTatio;
        var top=(border.GetTop()+border.GetHeight()/2)/pixelTatio;
        //left = left + border.UIElement.getBoundingClientRect().left+scrollPos.Left;
        //top = top+ border.UIElement.getBoundingClientRect().top+scrollPos.Top;
        dialog.Show(left,top,200,200);
    }
}

//信息地理tooltip
function KLineInfoTooltip(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.UpColor=g_JSChartResource.UpTextColor;
    this.DownColor=g_JSChartResource.DownTextColor;
    this.UnchagneColor=g_JSChartResource.UnchagneTextColor;

    this.Create=function()
    {
        this.ID=Guid();

        var div=document.createElement('div');
        div.className='jchart-klineinfo-tooltip';
        div.id=this.ID;
        div.innerHTML="<div class='title-length'></div>";
        this.DivElement.appendChild(div);
    }


    this.BindInfoList=function(infoType,infoList)
    {
        var strBox="<div class='total-list'>共"+infoList.length+"条</div>";
        var strText=[];
        for(var i in infoList)
        {
            var item=infoList[i];
            var strOld=item.Date;
            if(infoType==KLINE_INFO_TYPE.PFORECAST)
            {
                var reportDate=item.ExtendData.ReportDate;
                var year=parseInt(reportDate/10000);  //年份
                var day=reportDate%10000;   //比较 这个去掉年份的日期
                var reportType;
                if(day == 1231){
                    reportType = "年报"
                }else if(day == 331){
                    reportType = "一季度报"
                }else if(day == 630){
                    reportType = "半年度报"
                }else if(day == 930){
                    reportType = "三季度报"
                }

                var weekData="";
                if (item.ExtendData.FWeek)
                {
                    if (item.ExtendData.FWeek.Week1!=null) weekData+="一周后涨幅:<i class='increase' style='color:"+this.GetColor(item.ExtendData.FWeek.Week1.toFixed(2))+"'>"+ item.ExtendData.FWeek.Week1.toFixed(2)+"%</i>";
                    if (item.ExtendData.FWeek.Week4!=null) weekData+="&nbsp;四周后涨幅:<i class='increase' style='color:"+this.GetColor(item.ExtendData.FWeek.Week4.toFixed(2))+"'>"+ item.ExtendData.FWeek.Week4.toFixed(2)+"%</i>";
                    if (weekData.length>0) weekData="<br/>&nbsp;&nbsp;<i class='prorecast-week'>"+weekData+"</i>";
                }
                var strDate=item.Date.toString();
                var strNew=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8);  //转换时间格式
                strText+="<span>"+strNew+"&nbsp;&nbsp;"+year+reportType+item.Title+"&nbsp;"+weekData+"</span>";

            }
            else if (infoType==KLINE_INFO_TYPE.RESEARCH)    //调研单独处理
            {
                var levels=item.ExtendData.Level;
                var recPerson=[];
                if(levels.length==0){
                    recPerson = "<i>无</i>"
                }else{
                    for(var j in levels)
                    {
                        if(levels[j]==0){
                            recPerson+="<i style='color:#00a0e9'>证券代表&nbsp;&nbsp;&nbsp;</i>";
                        }else if(levels[j]==1){
                            recPerson+="<i>董秘&nbsp;&nbsp;&nbsp;</i>";
                        }else if(levels[j]==2){
                            recPerson+="<i style='color:#00a0e9'>总经理&nbsp;&nbsp;&nbsp;</i>";
                        }else if(levels[j]==3){
                            recPerson+="<i style='color:#00a0e9'>董事长&nbsp;&nbsp;&nbsp;</i>";
                        }
                    }
                }
                var strDate=item.Date.toString();
                var strNew=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8);  //转换时间格式
                strText+="<span>"+strNew+"&nbsp;&nbsp;&nbsp;接待:&nbsp;&nbsp;&nbsp;"+recPerson+"</span>";
            }else if(infoType==KLINE_INFO_TYPE.BLOCKTRADING)
            {
                var showPriceInfo = item.ExtendData;
                var strDate=item.Date.toString();
                var strNew=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8);  //转换时间格式
                strText+="<span><i class='date-tipbox'>"+strNew+"</i>&nbsp;&nbsp;<i class='tipBoxTitle'>成交价:&nbsp;"+showPriceInfo.Price.toFixed(2)+"</i><i class='tipBoxTitle'>收盘价:&nbsp;"+showPriceInfo.ClosePrice.toFixed(2)+
                    "</i><br/><i class='rate-discount tipBoxTitle'>溢折价率:&nbsp;<strong style='color:"+ this.GetColor(showPriceInfo.Premium.toFixed(2))+"'>"+
                    showPriceInfo.Premium.toFixed(2)+"%</strong></i><i class='tipBoxTitle'>成交量(万股):&nbsp;"+showPriceInfo.Vol.toFixed(2)+"</i></span>";
            }
            else if (infoType==KLINE_INFO_TYPE.TRADEDETAIL) //龙虎榜
            {
                /*var detail=[
                    "日价格涨幅偏离值达到9.89%",
                    "日价格涨幅偏离值达格涨幅偏离值达格涨幅偏离值达到9.89%"
                ]*/
                var detail=item.ExtendData.Detail;
                //格式:日期 上榜原因: detail[0].TypeExplain
                //                    detail[1].TypeExplain
                //      一周后涨幅: xx 四周后涨幅: xx
                var str=strOld.toString();
                var strNew=str.substring(0,4)+"-"+str.substring(4,6)+"-"+str.substring(6,8);  //转换时间格式
                var reasons = [];
                for(var i in detail){
                    reasons += "<i>"+detail[i].TypeExplain+"</i><br/>"
                    // reasons += detail[i] + "<br/>"
                }

                strText = "<span><i class='trade-time'>"+strNew+"&nbsp;&nbsp;&nbsp;上榜原因:&nbsp;&nbsp;</i><i class='reason-list'>"+reasons+"</i><br/><i class='trade-detall'>一周后涨幅:&nbsp;<strong style='color:"+
                    this.GetColor(item.ExtendData.FWeek.Week1.toFixed(2))+"'>"+ item.ExtendData.FWeek.Week1.toFixed(2)+
                    "%</strong>&nbsp;&nbsp;&nbsp;四周后涨幅:&nbsp;<strong style='color:"+this.GetColor(item.ExtendData.FWeek.Week4.toFixed(2))+";'>"+
                    item.ExtendData.FWeek.Week4.toFixed(2)+"%</strong></i></span>";
            }
            else
            {
                var str=strOld.toString();
                var strNew=str.substring(0,4)+"-"+str.substring(4,6)+"-"+str.substring(6,8);  //转换时间格式
                strText+="<span>"+strNew+"&nbsp;&nbsp;&nbsp;"+item.Title+"</span>";
            }
        }
        var titleInnerBox = $(".title-length").html(strText);

        $("#"+this.ID).html(titleInnerBox);

        //当信息超过8条时,添加“共XX条”统计总数
        if(infoList.length > 8){
            $("#"+this.ID).append(strBox);
        }
    }


    this.GetColor=function(price)
    {
        if(price>0) return this.UpColor;
        else if (price<0) return this.DownColor;
        else return this.UnchagneColor;
    }

    //显示窗口,改函数仅为KLineInfoTooltip使用
    this.Show=function(left,top,width,height,tooltip,times)
    {
        //显示位置
        $("#"+this.ID).css({'display':'block','top':top+'px', "left":left+'px', "width":width+'px', "height":height+'px' });

        function toolHide() {
            tooltip.Hide();
        }

        if (this.TimeOut!=null)
            clearTimeout(this.TimeOut); //清空上一次的定时器,防止定时器不停的被调用

        //设置窗口定时隐藏
        this.TimeOut=setTimeout(toolHide,times);

    }


    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        var infoType=event.data.InfoType;   //信息地雷类型
        var infoList=event.data.InfoList;    //信息数据列表
        var tooltip=chart.KLineInfoTooltip;

        if(!tooltip) return;
        if (tooltip.ID==null) tooltip.Create();   //第1次 需要创建div

        tooltip.BindInfoList(infoType,infoList);

        var left=event.pageX;
        var top=event.pageY+10;
        var widthTool=380;
        var heightTool=$("#"+tooltip.ID).height();

        if((left + widthTool) > chart.UIElement.getBoundingClientRect().width){
            left = left - widthTool;
        }
        /*if(top+heightTool>chart.UIElement.getBoundingClientRect().height){
            top=top-heightTool-45;
        }*/

        tooltip.Show(left,top,widthTool,"auto",tooltip,8000);
    }

    //鼠标离开
    this.Leave=function(event)
    {
        var chart=event.data.Chart;
        var tooltip=chart.KLineInfoTooltip;

        if(!tooltip || tooltip.ID==null) return;

        tooltip.Hide();
    }
}

//历史K线上双击 弹出分钟走势图框
function MinuteDialog(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;


    this.JSChart=null;
    this.Height=500;
    this.Width=600;
    this.Symbol;
    this.TradeDate;
    this.HistoryData;


    this.Create=function()
    {
        this.ID=Guid();
        var div=document.createElement('div');
        div.className='jchart-kline-minute-box';
        div.id=this.ID;
        var hqchartID=Guid();
        div.innerHTML=`<div><div class='minute-dialog-title'><span></span><strong class='close-munite icon iconfont icon-close'></strong></div><div class='minute-hqchart' id='${hqchartID}' ></div></div>`;
        div.style.width=this.Height+'px';
        div.style.height=this.Width+'px';

        this.DivElement.appendChild(div);
        this.JSChart=JSChart.Init(document.getElementById(hqchartID));

        var option=
        {
            Type:'历史分钟走势图',
            Symbol:this.Symbol,     //股票代码
            IsAutoUpdate:false,       //是自动更新数据

            IsShowRightMenu:false,   //右键菜单
            HistoryMinute: { TradeDate:this.TradeDate, IsShowName:false, IsShowDate:false }   //显示的交易日期
        };

        this.JSChart.SetOption(option);
    }

    this.BindClose=function(chart)
    {
        //关闭按钮
        $("#"+this.ID+" .close-munite").click(
            {
                Chart:chart
            },
            function(event)
            {
                var chart=event.data.Chart;
                chart.MinuteDialog.Hide();
            }
        );
    }

    this.DoModal=function(event)
    {
        this.UpColor=g_JSChartResource.UpTextColor;
        this.DownColor=g_JSChartResource.DownTextColor;
        this.UnchagneColor=g_JSChartResource.UnchagneTextColor;

        var chart=event.data.Chart;
        var tooltip=event.data.Tooltip;
        var dialog=chart.MinuteDialog;

        dialog.Symbol=chart.Symbol;
        dialog.TradeDate=tooltip.Data.Date;

        if(!dialog) return;
        if (dialog.ID==null)
        {
            dialog.Create();   //第1次 需要创建div
        }
        else
        {
            dialog.JSChart.JSChartContainer.TradeDate=dialog.TradeDate;
            dialog.JSChart.ChangeSymbol(this.Symbol);
        }

        var left=event.clientX;
        var top=event.clientY+10;

        dialog.Show(500,100,600,500);
        dialog.JSChart.OnSize();

        this.BindClose(chart);

        this.GetColor=function(price,yclse)
        {
            if(price>yclse) return this.UpColor;
            else if (price<yclse) return this.DownColor;
            else return this.UnchagneColor;
        }

        var strName = event.data.Chart.Name;
        var strData=event.data.Tooltip.Data;
        var date=new Date(parseInt(strData.Date/10000),(strData.Date/100%100-1),strData.Date%100);
        var strDate = strData.Date.toString();
        var strNewDate=strDate.substring(0,4)+"-"+strDate.substring(4,6)+"-"+strDate.substring(6,8);  //转换时间格式
        var str = "<span>"+strName+"</span>"+"<span>"+strNewDate+"</span>&nbsp;"+
            "<span style='color:"+this.GetColor(strData.Open,strData.YClose)+";'>开:"+strData.Open.toFixed(2)+"</span>"+
            "<span style='color:"+this.GetColor(strData.High,strData.YClose)+";'>高:"+strData.High.toFixed(2)+"</span>"+
            "<span style='color:"+this.GetColor(strData.Low,strData.YClose)+";'>低:"+strData.Low.toFixed(2)+"</span>"+
            "<span style='color:"+this.GetColor(strData.Close,strData.YClose)+";'>收:"+strData.Close.toFixed(2)+"</span>"+
            "<span style='color:"+this.VolColor+";'>量:"+IFrameSplitOperator.FormatValueString(strData.Vol,2)+"</span>"+
            "<span style='color:"+this.AmountColor+";'>额:"+IFrameSplitOperator.FormatValueString(strData.Amount,2)+"</span>";
        $(".minute-dialog-title span").html(str);
    }
}

//区间统计
function KLineSelectRectDialog(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.SelectData;
    this.Dialog;
    this.HQChart;

    //隐藏窗口
    this.Close=function()
    {
        if (this.Dialog) 
        {
            this.DivElement.removeChild(this.Dialog);
            this.Dialog=null;
        }

        this.HQChart.HideSelectRect();
    }

    //创建
    this.Create=function()
    {
        this.ID=Guid();
        var div=document.createElement('div');
        div.className='jchart-select-statistics-box';
        div.id=this.ID;
        div.innerHTML=
        "<div class='parameter jchart-select-section'>\
            <div class='parameter-header'>\
                <span>区间统计</span>\
                <strong id='close' class='icon iconfont icon-close'></strong>\
            </div>\
            <div class='parameter-content'>统计数据</div>\
        <div class='parameter-footer'>\
            <button id='close' class='submit' >确定</button>\
            <button id='match' class='submit' >形态匹配</button>\
        </div>\
        </div>";

        this.DivElement.appendChild(div);
        this.Dialog=div;

        //关闭按钮
        $("#"+this.ID+" #close").click(
            {
                divBox:this,
            },
            function(event)
            {
                event.data.divBox.Close();
            });
        
        //形态匹配
        $("#"+this.ID+" #match").click(
            {
                divBox:this,
            },
            function(event)
            {
                event.data.divBox.KLineMatch();
            });   
    }

    this.BindData=function()
    {
        var hqData=this.SelectData.Data;
        var start=this.SelectData.Start;
        var end=this.SelectData.End;

        var showData=
        { 
            Open:0,Close:0,High:0,Low:0, YClose:0,
            Vol:0, Amount:0, Date:{Start:0,End:0}, Count:0,
            KLine:{ Up:0,Down:0,Unchanged:0 }    //阳线|阴线|平线
        }

        for(var i=start; i<=end && i<hqData.Data.length; ++i)
        {
            var item=hqData.Data[i];
            if (i==start) 
            {
                showData.Date.Start=item.Date;
                showData.Open=item.Open;
                showData.High=item.High;
                showData.Low=item.Low;
                showData.YClose=item.YClose;
            }

            showData.Date.End=item.Date;
            showData.Close=item.Close;
            showData.Vol+=item.Vol;
            showData.Amount+=item.Amount;
            ++showData.Count;
            if (showData.High<item.High) showData.High=item.High;
            if(showData.Low>item.Low) showData.Low=item.Low;
            if (item.Close>item.Open) ++showData.KLine.Up;
            else if (item.Close<item.Open) ++showData.KLine.Down;
            else ++showData.KLine.Unchanged;
        }

        if (showData.Vol>0) showData.AvPrice=showData.Amount/showData.Vol;  //均价
        if (item.YClose>0)
        {
            showData.Increase = (showData.Close - showData.YClose) / showData.YClose *100;   //涨幅
            showData.Amplitude = (showData.High - showData.Low) / showData.YClose * 100;     //振幅
        }

        // JSConsole.Chart.Log('[KLineSelectRectDialog::BindData]', showData);
        var defaultfloatPrecision=GetfloatPrecision(this.SelectData.Symbol);
        var startDate = showData.Date.Start.toString().substring(0,4) + "/" + showData.Date.Start.toString().substring(4,6) + "/" +  showData.Date.Start.toString().substring(6,8);
        var endDate = showData.Date.End.toString().substring(0,4) +  "/" + showData.Date.End.toString().substring(4,6) +  "/" + showData.Date.End.toString().substring(6,8);

        var startLeftClass="",startRightClass="",endLeftClass="",endRightClass="";
        if(start<=0) startLeftClass = "BtnBackground";
        if(start >= end) {
            startRightClass = "BtnBackground";
            endLeftClass = "BtnBackground";
        }
        if(end >= hqData.Data.length - 1) endRightClass = "BtnBackground";

        var div=document.createElement('div');
        div.className='jchart-select-table-right';
        div.innerHTML=
            '<div class="jchart-select-date">\n' +
            '            <span>开始: '+ startDate +'<i class="start-date-left '+ startLeftClass +'"><</i><i class="start-date-right '+ startRightClass +'">></i></span>\n' +
            '            <span>结束: '+ endDate +'<i class="end-date-left '+ endLeftClass +'"><</i><i class="end-date-right '+ endRightClass +'">></i></span>\n' +
            '            <span>总个数: '+ showData.Count +'</span>\n' +
            '        </div>\n' +
            '        <table>\n' +
            '            <tr><td><strong>起始价: </strong><span>'+ showData.YClose.toFixed(defaultfloatPrecision) +'</span></td>' +
            '               <td><strong>最终价: </strong><span>'+ showData.Close.toFixed(defaultfloatPrecision) +'</span></td>' +
            '               <td><strong>均价: </strong><span>'+ showData.AvPrice.toFixed(defaultfloatPrecision) +'</span></td></tr>\n' +
            '            <tr><td><strong>最低价: </strong><span>'+ showData.Low.toFixed(defaultfloatPrecision) +'</span></td>' +
            '               <td><strong>最高价: </strong><span>'+ showData.High.toFixed(defaultfloatPrecision) +'</span></td>' +
            '               <td><strong>涨跌幅: </strong><span class="'+ IFrameSplitOperator.FormatValueColor(showData.Increase) +'">'+ showData.Increase.toFixed(2) +'%</span></td></tr>\n' +
            '            <tr><td><strong>振幅: </strong><span>'+ showData.Amplitude.toFixed(2) +'%</span></td>' +
            '               <td><strong>成交量: </strong><span>'+ IFrameSplitOperator.FormatValueString(showData.Vol,2) +'股</span></td>' +
            '               <td><strong>金额: </strong><span>'+ IFrameSplitOperator.FormatValueString(showData.Amount,2) +'元</span></td></tr>\n' +
            '            <tr><td><strong>阴线: </strong><span>'+ showData.KLine.Up +'</span></td>' +
            '               <td><strong>阳线: </strong><span>'+ showData.KLine.Down +'</span></td>' +
            '               <td><strong>平线: </strong><span>'+ showData.KLine.Unchanged +'</span></td></tr>\n' +
            '        </table>';

        $(".parameter-content").html(div);
        this.BindEvent();
    }

    this.BindEvent = function () {
        var _self = this;
        if(_self.SelectData.Start > 0){
            $(".jchart-select-date .start-date-left").click(function () {
                _self.SelectData.Start--;
                _self.BindData();
                _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End);
            })
        }
        if(_self.SelectData.Start < _self.SelectData.End){
            $(".jchart-select-date .start-date-right").click(function () {
                _self.SelectData.Start++;
                _self.BindData();
                _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End);
            })
            $(".jchart-select-date .end-date-left").click(function () {
                _self.SelectData.End--;
                _self.BindData();
                _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End);
            })
        }
        if(_self.SelectData.End < _self.SelectData.Data.Data.length - 1){
            $(".jchart-select-date .end-date-right").click(function () {
                _self.SelectData.End++;
                _self.BindData();
                _self.HQChart.UpdateSelectRect(_self.SelectData.Start,_self.SelectData.End);
            })
        }
    }

    //显示
    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        if (this.ID==null) this.Create();   //第1次 需要创建div
        this.SelectData=event.data.SelectData;
        this.HQChart=chart;

        this.BindData();

        //居中显示
        var border=chart.Frame.ChartBorder;
        var scrollPos=GetScrollPosition();
        var left=border.GetWidth()/2;
        var top=border.GetHeight()/2;
        //left = left + border.UIElement.getBoundingClientRect().left+scrollPos.Left;
        //top = top+ border.UIElement.getBoundingClientRect().top+scrollPos.Top;

        this.Show(left,top,200,200);      //显示
    }

    //形态匹配
    this.KLineMatch=function(data)
    {
        var waitDialog=new WaitDialog(this.DivElement);
        waitDialog.DoModal(
            {
                data:
                { 
                    Title:'计算中......', 
                    Chart:this.HQChart
                } 
            });

        this.Close();   //关闭窗口

        var hqChart=this.HQChart;
        var param= { Scope: { Plate:["CNA.ci"],Minsimilar:0.90 }, WaitDialog:waitDialog }  //沪深A股, 相似度>=90%
        hqChart.RequestKLineMatch(this.SelectData, param);
    }
}

//形态选股
function KLineMatchDialog(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.MatchData;     //匹配的股票数据
    this.Sample;        //样本数据
    this.Dialog;
    this.HQChart;

    this.PageData; //分页数据

    //隐藏窗口
    this.Close=function()
    {
        this.DivElement.removeChild(this.Dialog);
    }

    //创建
    this.Create=function()
    {
        this.ID=Guid();
        var div=document.createElement('div');
        div.className='jchart-kline-match-box';
        div.id=this.ID;
        div.innerHTML=
        `<div class='parameter jchart-kline-match-box'>
            <div class='parameter-header'>
                <span>形态匹配</span>
                <strong id='close' class='icon iconfont icon-close'></strong>
            </div>
            <div class='parameter-content'>
                <p class='dataCount'></p>
                <table class='matchTable'>
                    <thead>
                        <tr>
                            <td>股票名称</td>
                            <td>匹配度</td>
                            <td>时间段</td>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
                <div class='pagination' data-current='1'></div>
            </div>
            <div class='parameter-footer'>
                <button id='close' class='submit' >确定</button>
            </div>
        </div>`.trim();

        this.DivElement.appendChild(div);
        this.Dialog=div;

        //关闭按钮
        $("#"+this.ID+" #close").click(
            {
                divBox:this,
            },
            function(event)
            {
                event.data.divBox.Close();
            });
    }

    this.BindData=function()
    {
        JSConsole.Chart.Log(`[KLineMatchDialog::BindData] 形态源: ${this.Sample.Stock.Name} 区间:${this.Sample.Date.Start} - ${this.Sample.Date.End}`);
        var count = this.MatchData.length + 1;
        var pageData = {NewData:{},MetaData:[],PageCount:0,Count:count};
        var pageCount = 0;
        var paginationHtml = '';

        $('#'+this.ID+' .dataCount').html('个数:'+count);
        
        for(let i = 0; i < count ; i++){
            var dataObj = {};
            if(i == 0){
                dataObj = {
                    Symbol:this.Sample.Stock.Symbol,
                    Name:this.Sample.Stock.Name,
                    Rate:'形态源',
                    Color:'red',
                    Date:`${this.Sample.Date.Start}-${this.Sample.Date.End}`
                };
            }else{
                let dataItem = this.MatchData[i - 1];
                dataObj = {
                    Symbol:dataItem.Symbol,
                    Name:dataItem.Name,
                    Rate:Number(dataItem.Similar * 100).toFixed(2),
                    Color:'',
                    Date:`${dataItem.Start}-${dataItem.End}`
                };
            }
            pageData.MetaData.push(dataObj);
        }

        if(pageData.Count % 10 == 0){
            pageCount = pageData.Count / 10;
        }else{
            pageCount = Math.floor(pageData.Count / 10) + 1;
        }
        pageData.PageCount = pageCount;

        this.PaginationMetaData(pageData);
        this.PageData = pageData;
        JSConsole.Chart.Log('[KLineMatchDialog::DoModal pageData]',pageData);

        this.RenderDom(1);

        this.PaginationInit('#'+this.ID,pageData.PageCount,this.paginationCallback);
        // $('#' + this.ID + ' .pagination').html(paginationHtml);

        
    }
    this.RenderDom = function(page){
        let currentPageData = this.PageData.NewData[page];
        JSConsole.Chart.Log('[KLineMatchDialog::RenderDom currentPageData]',currentPageData);
        let bodyHtml = '';
        for(let i = 0; i < currentPageData.length; i++){
            bodyHtml += `<tr>
                        <td class=${currentPageData[i].Color}>${currentPageData[i].Name}</td>
                        <td class=${currentPageData[i].Color}>${currentPageData[i].Rate}</td>
                        <td class=${currentPageData[i].Color}>${currentPageData[i].Date}</td>
                    </tr>`.trim();
        }
        
        $('#'+this.ID + ' .matchTable tbody').html(bodyHtml)
    }
    var _this = this;
    this.paginationCallback = function(page) {
        _this.RenderDom(page);
        _this.PaginationInit('#'+_this.ID,_this.PageData.PageCount,_this.paginationCallback); //更新UI
    }
    this.PaginationInit = function(id, maxPageNum, callback) {  //初始化分页
        var spanStr = "";
        var currentPageNum = $(id + " .pagination").data("current");
        var lastPageNum = 0;
        var showCountPage = 5; //只显示5个数字项
    
        if (currentPageNum < showCountPage) {  //当前页小于预显示页数
            if (maxPageNum >= showCountPage) {
                for (var j = 0; j < showCountPage; j++) {  //上 1 2 3 4 5 下
                    spanStr += (j + 1) != currentPageNum ? "<span>" + (j + 1) + "</span>" : "<span class='active'>" + (j + 1) + "</span>";
                }
            } else {
                for (var j = 0; j < maxPageNum; j++) {  //上 1 2 3 4 5 下
                    spanStr += (j + 1) != currentPageNum ? "<span>" + (j + 1) + "</span>" : "<span class='active'>" + (j + 1) + "</span>";
                }
            }
        } else { //大于5时,最终页数是当前页数加1
            lastPageNum = (currentPageNum + 1) > maxPageNum ? currentPageNum : (currentPageNum + 1);

            for (var i = currentPageNum - 3; i <= lastPageNum; i++) { //含最终项之前的五项
                spanStr += i != currentPageNum ? "<span>" + i + "</span>" : "<span class='active'>" + i + "</span>";
            }
        }
    
        spanStr = "<span class='beforePage'>上一页</span>" + spanStr + "<span class='nextPage'>下一页</span>";
        $(id + " .pagination").html(spanStr);
        $(id + " .pagination span").bind('click', { "maxpage": maxPageNum, "Callback": callback }, this.PaginationCurrentIndex);
        // return spanStr;
    }
    
    this.PaginationCurrentIndex = function(event) {  //分页切换
        var text = $(this).text();
        JSConsole.Chart.Log('[::PaginationCurrentIndex text]',text);
        var currentPageNum = Number($(this).parent().data("current"));
        var maxPageNum = event.data.maxpage;
        var callback = event.data.Callback;
        var flag = 1;
        if (text === "上一页") {
            flag = currentPageNum === 1 ? currentPageNum : currentPageNum - 1;
        } else if (text === "下一页") {
            flag = currentPageNum === maxPageNum ? currentPageNum : currentPageNum + 1;
        } else {
            flag = Number(text);
        }
        $(this).parent().data("current", flag);  //将当前页存到dom上
        callback(flag);
    }
    
    this.PaginationMetaData = function(data){ //假分页数据,每页10条数据
        // data = {NewData:{},MetaData:[],PageCount:0,Callback:null};
        var newData = {};
        var metaData = data.MetaData;
        var pageCount = data.PageCount;
        
        for(let i = 0; i < pageCount; i++){
            var itemArr = [];
            for(let j = 0; j < 10; j++){
                var itemIndex = 10*i + j;
                if(itemIndex <= metaData.length - 1){
                    var item = metaData[itemIndex];
                    itemArr.push(item);
                }else {
                    break;
                }
            }
            newData[i+1] = itemArr;
        }
        data.NewData = newData;
    }

    //显示
    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        if (this.ID==null) this.Create();   //第1次 需要创建div
        this.MatchData=event.data.MatchData;
        this.Sample=event.data.Sample;
        this.HQChart=chart;

        this.BindData();
        

        //居中显示
        var border=chart.Frame.ChartBorder;
        var scrollPos=GetScrollPosition();
        var left=border.GetWidth()/2;
        var top=border.GetHeight()/2;

        this.Show(left,top,200,200);      //显示
    }
}

//等待动画窗口
function WaitDialog(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.Title='加载中......';
    this.Dialog;

    //隐藏窗口
    this.Close=function()
    {
        if (this.Dialog) 
        {
            this.DivElement.removeChild(this.Dialog);
            this.Dialog=null;
        }
    }

    this.SetTitle=function(title)
    {
        this.Title=title;
        if (!this.Dialog) return;
        //TODO: 更新标题数据
    }

    this.Create=function()
    {
        this.ID=Guid();
        var div=document.createElement('div');
        div.className='jchart-wait-box';
        div.id=this.ID;
        div.innerHTML=
        `<div class='parameter jchart-kline-match-box'>
            <div class='parameter-header'>
                <span>${this.Title}</span>
            </div>
        </div>`.trim();

        this.DivElement.appendChild(div);
        this.Dialog=div;
    }

    //显示
    this.DoModal=function(event)
    {
        this.Title=event.data.Title;
        var chart=event.data.Chart;
        if (this.ID==null) this.Create();   //第1次 需要创建div
        
        //居中显示
        var border=chart.Frame.ChartBorder;
        var scrollPos=GetScrollPosition();
        var left=border.GetWidth()/2;
        var top=border.GetHeight()/2;

        this.Show(left,top,200,40);      //显示
    }
}

//K线右键菜单类
//id:"kline"
function KLineRightMenu(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.option={};

    this.Create = function () {
        var _self = this;

        this.ID=Guid();

        _self.BindData();
        _self.BindEvent();
    }
    this.BindData=function(){
        var _self = this;

        var id=this.DivElement.id;
        var $body = $("#" + id);

        $body.find("div[id^='topMenu_']").remove();
        $body.find("div[id^='childMenu_']").remove();

        var $topMenu = $("<div />");
        $topMenu.attr("id", "topMenu_"+_self.ID).addClass("context-menu-wrapper topmenu").hide();
        $body.append($topMenu);

        var $topTable = $("<table />");
        $topTable.attr({ id: "topTable_" + _self.ID, cellspacing: "0", cellpadding: "0" }).addClass("context-menu");
        $topMenu.append($topTable);

        $topTable.append(_self.childrenList(_self.option.data));

        for (var i = 0; i < _self.option.data.length; i++) {
            var isHasChildren = typeof _self.option.data[i].children != "undefined";

            if (isHasChildren) {

                var $childMenu = $("<div />");
                $childMenu.attr({ id: "childMenu_"+_self.ID + i, "data-index": i }).addClass("context-menu-wrapper").hide();
                $body.append($childMenu);

                var $childTable = $("<table />");
                $childTable.attr({ id: "childTable_" + _self.ID + i, cellspacing: "0", cellpadding: "0" }).addClass("context-menu");
                $childMenu.append($childTable);

                $childTable.append(_self.childrenList(_self.option.data[i].children));
            }
        }
    }

    this.Update=function()
    {
        var _self = this;
        //var id=this.DivElement.id;
        //var $body=$("#"+id);
        //
        //var $topTable = $("#topTable_" + _self.ID);
        //$topTable.empty();
        //$topTable.append(_self.childrenList(_self.option.data));
        //
        //for (var i = 0; i < _self.option.data.length; i++) {
        //    var isHasChildren = typeof _self.option.data[i].children != "undefined";
        //
        //    if (isHasChildren) {
        //        var $childTable = $("#childTable_" + _self.ID + i);
        //        $childTable.empty();
        //        $childTable.append(_self.childrenList(_self.option.data[i].children));
        //    }
        //}

        _self.BindData();
        _self.BindEvent();
    }

    this.childrenList = function(list) {
        var result = [];

        for (var i = 0; i < list.length; i++) {
            var isBorder = typeof list[i].isBorder != "undefined" && list[i].isBorder;

            var $tr = $("<tr />");
            $tr.addClass("font_Arial context-menu");
            if (isBorder)
                $tr.addClass("border");

            var $td1 = $("<td />");
            if(list[i].selected){
                $td1.addClass("spacer context-menu").html("√");
            }else{
                $td1.addClass("spacer context-menu");
            }

            var $td2 = $("<td />");
            $td2.addClass("text").html(list[i].text);

            var $td3 = $("<td />");
            $td3.addClass("right shortcut");

            var $td4 = $("<td />");
            $td4.addClass(typeof list[i].children != "undefined" ? "submenu-arrow" : "context-menu spacer");

            $tr.append($td1).append($td2).append($td3).append($td4);

            result.push($tr);
        }
        return result;
    }

    this.Show=function (obj) {
        var _self = this;
        $.extend(_self.option, obj);

        if (!_self.ID) _self.Create();  //判断是否重复创建
        else _self.Update();            //更新菜单状态

        var $topMenu = $("#topMenu_"+_self.ID),
            topWidth = $topMenu.outerWidth(),
            topHeight = $topMenu.outerHeight();

        var x = _self.option.x,
            y = _self.option.y;

        if (topWidth > _self.option.position.X + _self.option.position.W- x) //超过了右边距
            x = x - topWidth;

        if (topHeight > _self.option.position.Y +_self.option.position.H - y)//超过了下边距
            y = y - topHeight;

        $topMenu.hide();
        $topMenu.css({ position:"absolute",left: x + "px", top: y + "px" }).show();

        this.isInit = true;
    }

    this.Hide=function () {
        var _self = this;

        $("#topMenu_" + _self.ID).hide();
        $("[id^='childMenu_" + _self.ID + "']").hide();
    }

    this.BindEvent=function () {
        var _self = this;
        var $childMenu = $("[id^='childMenu_" + _self.ID + "']");

        $("#topTable_" + _self.ID).find("tr").mouseenter(function () {
            var $this = $(this),
                index = $this.index(),
                $topMenu = $("#topMenu_" + _self.ID),
                $child = $("#childMenu_" + _self.ID + index),
                trWidth = $this.outerWidth(),
                trHeight = $this.outerHeight();

            var left = $topMenu.position().left + trWidth + 1;
            var top = $topMenu.position().top + (trHeight * index);

            if (trWidth > _self.option.position.X + _self.option.position.W - left) //超过了右边距
                left = left - trWidth - $topMenu.outerWidth() - 2;

            if ($child.outerHeight() > _self.option.position.Y +_self.option.position.H - top)//超过了下边距
                top = $topMenu.position().top + $topMenu.outerHeight() - $child.outerHeight();

            $childMenu.hide();
            $child.css({ left: left + "px", top: top + "px" }).show();
        }).mouseleave(function () {
            var index = $(this).index();
            setTimeout(function () {
                if ($("#childMenu_" + _self.ID + index).attr("data-isShow") != 1) {
                    $("#childMenu_" + _self.ID + index).hide();
                }
            }, 10)

        }).click(function () {
            var $this = $(this);
            var index = $this.index();

            if ($.type(_self.option.data[index].click) == "function") {
                _self.option.data[index].click(_self.option.returnData);
                $this.hide();
            }
        });


        $childMenu.mouseenter(function () {
            $(this).attr("data-isShow", "1");
        }).mouseleave(function () {
            $(this).attr("data-isShow", "0");
        });

        $childMenu.find("tr").click(function () {
            var $this = $(this);
            var divIndex = parseInt($this.closest("div").attr("data-index"));
            var trIndex = $this.index();

            if ($.type(_self.option.data[divIndex].children[trIndex].click) == "function") {
                _self.option.data[divIndex].children[trIndex].click(_self.option.windowIndex || 1);
                $childMenu.hide();
            }
        });
    }

    this.GetPeriod=function (chart) 
    {
        var data=
        [
            {
                text: "日线", Value:0,
                click: function () { chart.ChangePeriod(0); }
            }, 
            {
                text: "周线",Value:1,
                click: function () { chart.ChangePeriod(1); }
            }, 
            {
                text: "双周线",Value:21,
                click: function () { chart.ChangePeriod(21); }
            },
            {
                text: "月线",Value:2,
                click: function () { chart.ChangePeriod(2); }
            }, 
            {
                text: "季线",Value:9,
                click: function () { chart.ChangePeriod(9); }
            },
            {
                text: "年线",Value:3,
                click: function () { chart.ChangePeriod(3); }
            },
            {
                text: "1分",Value:4,
                click: function () { chart.ChangePeriod(4); }
            },
            {
                text: "5分",Value:5,
                click: function () { chart.ChangePeriod(5); }
            },
            {
                text: "15分",Value:6,
                click: function () { chart.ChangePeriod(6); }
            },
            {
                text: "30分",Value:7,
                click: function () { chart.ChangePeriod(7); }
            },
            {
                text: "60分",Value:8,
                click: function () { chart.ChangePeriod(8); }
            },
            {
                text: "2小时",Value:11,
                click: function () { chart.ChangePeriod(11); }
            },
            {
                text: "4小时",Value:12,
                click: function () { chart.ChangePeriod(12); }
            },
            {
                text: "分笔",Value:10,
                click: function () { chart.ChangePeriod(10); }
            },
            {
                text: "自定义周期:3分钟",Value:20003,
                click: function () { chart.ChangePeriod(20003); }
            },
            {
                text: "自定义周期:35分钟",Value:20035,
                click: function () { chart.ChangePeriod(20035); }
            },
            {
                text: "自定义周期:8日",Value:40008,
                click: function () { chart.ChangePeriod(40008); }
            }
        ];

        for(var i in data)
        {
            var item=data[i];
            if (item.Value==chart.Period)
            {
                item.selected=true;
                break;
            }
        }

        return data; 
    }

    this.GetRight=function(chart)
    {
        var data=
        [
            {
                text: "不复权",
                click: function () { chart.ChangeRight(0); }
            }, 
            {
                text: "前复权",
                click: function () { chart.ChangeRight(1); }
            }, 
            {
                text: "后复权",
                click: function () { chart.ChangeRight(2); }
            }
        ];

        if (chart.Right>=0 && chart.Right<data.length) data[chart.Right].selected=true;

        return data;
    }

    //指标
    this.GetIndex=function (chart) 
    {
        return [{
            text: "均线",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, '均线')
            }
        }, {
            text: "BOLL",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'BOLL')
            },
            isBorder:true
        }, {
            text: "MACD",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'MACD')
            }
        }, {
            text: "KDJ",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'KDJ')
            }
        }, {
            text: "VOL",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'VOL')
            }
        }, {
            text: "RSI",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'RSI')
            }
        }, {
            text: "BRAR",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'BRAR')
            }
        }, {
            text: "WR",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'WR')
            }
        }, {
            text: "BIAS",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'BIAS')
            }
        }, {
            text: "OBV",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'OBV')
            }
        }, {
            text: "DMI",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'DMI')
            }
        }, {
            text: "CR",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'CR')
            }
        }, {
            text: "PSY",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'PSY')
            }
        }, {
            text: "CCI",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'CCI')
            }
        }, {
            text: "DMA",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'DMA')
            }
        }, {
            text: "TRIX",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'TRIX')
            }
        }, {
            text: "VR",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'VR')
            }
        }, {
            text: "EMV",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'EMV')
            }
        }, {
            text: "ROC",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'ROC')
            }
        }, {
            text: "MIM",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'MIM')
            }
        }, {
            text: "FSL",
            click: function (windowIndex) {
                chart.ChangeIndex(windowIndex, 'FSL')
            }
        } ]
    }

    //五彩K线
    this.GetColorIndex=function (chart) 
    {
        var data=
        [
            {
                text: "十字星",
                click: function (windowIndex) { chart.ChangeInstructionIndex('五彩K线-十字星') }
            }, 
            {
                text: "早晨之星",
                click: function (windowIndex) { chart.ChangeInstructionIndex('五彩K线-早晨之星') },
            }, 
            {
                text: "垂死十字",
                click: function (windowIndex) { chart.ChangeInstructionIndex('五彩K线-垂死十字') },
            }, 
            {
                text: "三只乌鸦",
                click: function (windowIndex) { chart.ChangeInstructionIndex('五彩K线-三只乌鸦') }
            }, 
            {
                text: "光脚阴线",
                click: function (windowIndex) { chart.ChangeInstructionIndex('五彩K线-光脚阴线') }
            }, 
            {
                text: "黄昏之星",
                click: function (windowIndex) { chart.ChangeInstructionIndex('五彩K线-黄昏之星') }
            }
        ];

        if (chart.ColorIndex)
        {
            data[data.length-1].isBorder=true;
            data.push(
                {
                    text: "删除五彩K线",
                    click: function (windowIndex) { chart.CancelInstructionIndex() }
                });
        }

        return data;
    }

    //专家系统
    this.GetTradeIndex=function(chart)
    {
        var data=
        [
            {
                text: "BIAS",
                click: function (windowIndex) { chart.ChangeInstructionIndex('交易系统-BIAS') }
            }, 
            {
                text: "CCI",
                click: function (windowIndex) { chart.ChangeInstructionIndex('交易系统-CCI') }
            }, 
            {
                text: "DMI",
                click: function (windowIndex) { chart.ChangeInstructionIndex('交易系统-DMI') }
            }, 
            {
                text: "KD",
                click: function (windowIndex) { chart.ChangeInstructionIndex('交易系统-KD') }
            }, 
            {
                text: "BOLL",
                click: function (windowIndex) { chart.ChangeInstructionIndex('交易系统-BOLL') }
            }, 
            {
                text: "KDJ",
                click: function (windowIndex) { chart.ChangeInstructionIndex('交易系统-KDJ') }
            }
        ];

        if (chart.TradeIndex)
        {
            data[data.length-1].isBorder=true;
            data.push(
                {
                    text: "删除专家系统",
                    click: function (windowIndex) { chart.CancelInstructionIndex()}
                });
        }
        return data;
    }

    //叠加
    this.GetOverlay=function (chart)  
    {
        var data=
        [
            {
                text: "上证指数",
                click: function () { chart.OverlaySymbol('000001.sh'); }
            },
            {
                text: "深证成指",
                click: function () { chart.OverlaySymbol('399001.sz'); }
            }, 
            {
                text: "中小板指",
                click: function () { chart.OverlaySymbol('399005.sz'); }
            }, 
            {
                text: "创业板指",
                click: function () { chart.OverlaySymbol('399006.sz'); }
            }, 
            {
                text: "沪深300",
                click: function () { chart.OverlaySymbol('000300.sh'); },
            }
        ];

        for(var i in chart.OverlayChartPaint)
        {
            var item=chart.OverlayChartPaint[i];
            var symbol=item.Symbol;
            const mapSymbol=new Map([['000001.sh',0],['399001.sz',1],['399005.sz',2],['399006.sz',3],['000300.sh',4]]);
            if (mapSymbol.has(symbol)) 
            {
                var menuItem=data[mapSymbol.get(symbol)];
                let delSymbol=symbol;
                menuItem.selected=true;
                menuItem.click=function () { chart.DeleteOverlaySymbol(delSymbol); };
            }
        }

        if (chart.OverlayChartPaint && chart.OverlayChartPaint.length>0)
        {
            data[data.length-1].isBorder=true;
            data.push(
                {
                    text: "取消叠加",
                    click: function () { chart.ClearOverlaySymbol();}
                }
            );
        }

        return data;
    }

    //K线类型设置
    this.GetKLineType=function(chart)
    {
        var data=
        [
            {
                text: "K线(空心阳线)",
                click: function () { chart.ChangeKLineDrawType(3);}
            },
            {
                text: "K线(实心阳线)",
                click: function () { chart.ChangeKLineDrawType(0); }
            }, 
            {
                text: "美国线",
                click: function () { chart.ChangeKLineDrawType(2); }
            }, 
            {
                text: "收盘线",
                click: function () { chart.ChangeKLineDrawType(1); }
            },
            {
                text: "收盘面积",
                click: function () { chart.ChangeKLineDrawType(4); }
            }
        ];

        switch(chart.KLineDrawType)
        {
            case 0:
                data[1].selected=true;
                break;
            case 1:
                data[3].selected=true;
                break;
            case 2:
                data[2].selected=true;
                break;
            case 3:
                data[0].selected=true;
                break;
            case 4:
                data[4].selected=true;
                break;
        }
        return data;
    }

    //指标窗口个数
    this.GetIndexWindowCount=function(chart)
    {
        var data=
        [
            {
                text: "1个窗口",
                click: function () { chart.ChangeIndexWindowCount(1); }
            },
            {
                text: "2个窗口",
                click: function () { chart.ChangeIndexWindowCount(2); }
            }, 
            {
                text: "3个窗口",
                click: function () { chart.ChangeIndexWindowCount(3); }
            }, 
            {
                text: "4个窗口",
                click: function () { chart.ChangeIndexWindowCount(4); }
            },
            {
                text: "5个窗口",
                click: function () { chart.ChangeIndexWindowCount(5); }
            }
        ];

        var count=chart.Frame.SubFrame.length;
        if ((count-1)>=0 && (count-1)<data.length) data[count-1].selected=true;  //选中

        return data;
    }

    //坐标类型
    this.GetCoordinateType=function(chart)
    {
        var data= 
        [
            {
                text: "普通坐标",
                click: function () { chart.ChangeCoordinateType(0); }
            },
            {
                text: "百分比坐标",
                click: function () { chart.ChangeCoordinateType(1); }
            },
            {
                text: "反转坐标",
                click: function () { chart.ChangeCoordinateType(2); }
            }
        ];

        if (chart.Frame && chart.Frame.SubFrame && chart.Frame.SubFrame.length>0) 
        {
            if (chart.Frame.SubFrame[0].Frame.CoordinateType==1) data[2].selected=true;
            else data[0].selected=true;

            if (chart.Frame.SubFrame[0].Frame.YSplitOperator.CoordinateType==1) data[1].selected=true;
        }

        return data;
    }

    //拖拽模式
    this.GetDragModeType=function(chart)
    {
        var data=
        [
            {
                text: "禁止拖拽",
                click: function () { chart.DragMode=0; }
            },
            {
                text: "启动拖拽",
                click: function () { chart.DragMode=1; }
            },
            {
                text: "区间选择",
                click: function () { chart.DragMode=2; }
            }
        ];

        if (chart.DragMode>=0 && chart.DragMode<data.length) data[chart.DragMode].selected=true;  //选中

        return data;
    }

    //工具
    this.GetTools=function(chart)
    {
        var data=[];
        var drawTools=chart.GetExtendChartByClassName('DrawToolsButton');
        if (drawTools)
        {
            data.push(
                {
                    text: "关闭画图工具",
                    click: function () 
                    { 
                        var toolsWidth=drawTools.Chart.Width;
                        var toolsIndex=parseInt(drawTools.Index);
                        for(var i=toolsIndex+1; i<chart.ExtendChartPaint.length; ++i)   //在画图工具后面创建的需要减去工具的宽度
                        {
                            var item=chart.ExtendChartPaint[i];
                            if (item.ClassName=='StockChip')
                            {
                                item.Left-=toolsWidth;
                            }
                        }
                        chart.DeleteExtendChart(drawTools); 
                        chart.Frame.ChartBorder.Right-=toolsWidth;
                        chart.SetSizeChage(true);
                        chart.Draw();
                    }
                }
            );
        }
        else
        {
            data.push(
                {
                    text: "画图工具",
                    click: function () {
                        var option={Name:'画图工具', Top:chart.Frame.ChartBorder.Top };
                        var extendChart=chart.CreateExtendChart(option.Name, option);   //创建扩展图形
                        chart.SetSizeChage(true);
                        chart.Draw();
                    }
                }
            );
        }

        var StockChip=chart.GetExtendChartByClassName('StockChip');
        if (StockChip)
        {
            data.push(
                {
                    text: "关闭移动筹码",
                    click: function () 
                    { 
                        var chipWidth=StockChip.Chart.Width;
                        var chipIndex=parseInt(StockChip.Index);
                        for(var i=chipIndex+1; i<chart.ExtendChartPaint.length; ++i)   //在筹码后面创建的需要筹码的宽度
                        {
                            var item=chart.ExtendChartPaint[i];
                            if (item.ClassName=='DrawToolsButton')
                            {
                                item.Left-=chipWidth;
                            }
                        }
                        chart.DeleteExtendChart(StockChip); 
                        chart.Frame.ChartBorder.Right-=chipWidth;
                        chart.SetSizeChage(true);
                        chart.Draw();
                    }
                    
                }
            );
        }
        else
        {
            data.push(
                {
                    text: "移动筹码",
                    click: function () {  
                        var option={Name:'筹码分布', ShowType:1, Width:230 };
                        var extendChart=chart.CreateExtendChart(option.Name, option);   //创建扩展图形
                        chart.SetSizeChage(true);
                        chart.Draw();
                    }
                }
            );
        }

        return data;
    }

    this.GetKLineInfo=function(chart)
    {
        var setInfo=new Set();
        for(var i in chart.ChartInfo)
        {
            var item=chart.ChartInfo[i];
            setInfo.add(item.ClassName);
        }

        var aryKLineInfo=["公告","业绩预告","调研","大宗交易","龙虎榜","互动易"]

        var data=[];
        for(var i in aryKLineInfo)
        {
            var infoName=aryKLineInfo[i];
            var classInfo=JSKLineInfoMap.GetClassInfo(infoName);
            if (!classInfo) continue;
            
            var item=this.CreateKlineInfoItem(infoName, setInfo.has(classInfo.ClassName), chart);
            data.push(item);
        }

        if (chart.ChartInfo.length>0)
        {
            data[data.length-1].isBorder=true;
            var item={ text:'删除所有', click:function() { chart.ClearKLineInfo()} };
            data.push(item);
           
        }

        return data;
    }

    this.CreateKlineInfoItem=function(infoName,bExist,chart)
    {
        var item= 
        {
            text:infoName, 
            selected:bExist
        }

        if (bExist) item.click=function() { chart.DeleteKLineInfo(infoName) };
        else  item.click=function() { chart.AddKLineInfo(infoName,true) }

        return item;
    }

    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        var rightMenu=chart.RightMenu;
        var x = event.offsetX;
        var y = event.offsetY;

        var dataList=[{
            text: "分析周期",
            children: this.GetPeriod(chart)
        }, 
        {
            text: "复权处理",
            children: this.GetRight(chart)
        }, 
        {
            text: "指标切换",
            children: this.GetIndex(chart)
        }, 
        {
            text:"五彩K线",
            children: this.GetColorIndex(chart)
        },
        {
            text:'专家系统',
            children: this.GetTradeIndex(chart)
        },
        {
            text:'信息地雷',
            children: this.GetKLineInfo(chart)
        },
        {
            text: "叠加品种",
            children: this.GetOverlay(chart)
        },
        {
            text:'主图线型',
            children: this.GetKLineType(chart)
        },
        {
            text:"坐标类型",
            children:this.GetCoordinateType(chart)
        },
        {
            text:'指标窗口个数',
            children: this.GetIndexWindowCount(chart)
        },
        {
            text:'鼠标拖拽',
            children: this.GetDragModeType(chart)
        },
        {
            text:"工具",
            children:this.GetTools(chart)
        }
        ];

        var upperSymbol=chart.Symbol.toUpperCase();
        if(MARKET_SUFFIX_NAME.IsSHSZIndex(chart.Symbol) || MARKET_SUFFIX_NAME.IsBIT(upperSymbol))
        {
            dataList.splice(1,1);
        }

        var identify=event.data.FrameID;
        var overlayIndex=this.GetOverlayIndex(chart,identify);
        if (overlayIndex && overlayIndex.length>0)
        {
            var delOverlayIndexMenu={ text:'删除叠加指标', children:this.GetDeleteOverlayIndex(chart,overlayIndex) }
            dataList.splice(3,0,delOverlayIndexMenu);
        }

        JSConsole.Chart.Log('[KLineRightMenu::DoModal]',identify);
        rightMenu.Show({
            windowIndex :identify,
            x:x+chart.UIElement.offsetLeft,
            y:y+chart.UIElement.offsetTop,
            position:chart.Frame.Position,
            data:dataList
        })

        $(document).click(function () {
            rightMenu.Hide();
        });
    }

    this.GetOverlayIndex=function(chart, windowsIndex)
    {
        if (windowsIndex>=chart.Frame.SubFrame.length) return [];

        var result=[];
        var item=chart.Frame.SubFrame[windowsIndex];
        for(var i in item.OverlayIndex)
        {
            var overlayItem=item.OverlayIndex[i];
            result.push({Name:overlayItem.Script.Name, Identify:overlayItem.Identify});
        }
        
        return result;
    }

    this.GetDeleteOverlayIndex=function(chart,overlayIndex)
    {
        var data=[];
        for(var i in overlayIndex)
        {
            let identify=overlayIndex[i].Identify;
            data.push({text:overlayIndex[i].Name, click:function()
                { 
                    chart.DeleteOverlayWindowsIndex(identify)
                }});
        }

        return data;
    }
}

//K线区间选择右键菜单
function KLineSelectRightMenu(divElement)
{
    this.newMethod=KLineRightMenu;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        var rightMenu=this;
        var x = event.data.X;
        var y = event.data.Y;

        var dataList=
        [
            {
                text: "区间统计",
                click: function () 
                {
                    JSConsole.Chart.Log('[KLineSelectRightMenu::click] 区间统计');
                    var dialog=new KLineSelectRectDialog(divElement);
                    dialog.DoModal(event);
                }
            } 
        ];

        rightMenu.Show({
            x:x,
            y:y,
            position:chart.Frame.Position,
            data:dataList
        });
    }

    this.Show=function (obj) 
    {
        var _self = this;
        $.extend(_self.option, obj);

        //判断是否重复创建
        if (!_self.ID) _self.Create();

        var $topMenu = $("#topMenu_"+_self.ID),
            topWidth = $topMenu.outerWidth(),
            topHeight = $topMenu.outerHeight();

        var x = _self.option.x,
            y = _self.option.y;

        if (topWidth > _self.option.position.X + _self.option.position.W- x) //超过了右边距
            x = x - topWidth;

        if (topHeight > _self.option.position.Y +_self.option.position.H - y)//超过了下边距
            y = y - topHeight;

        $topMenu.hide();
        $topMenu.css({ position:"absolute",left: x + "px", top: y + "px" }).show();

        $("#topMenu_" + _self.ID).find("tr").show();    //把菜单列表显示

        this.isInit = true;
    }
}

//分钟数据右键菜单
function MinuteRightMenu(divElement)
{
    this.newMethod=KLineRightMenu;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.DoModal=function(event)
    {
        var chart=event.data.Chart;
        var rightMenu=chart.RightMenu;
        var x = event.offsetX;
        var y = event.offsetY;

        var dataList=
        [
            {
                text: "叠加品种",
                children: this.GetOverlay(chart)
            },
            {
                text: "多日分时图",
                children: this.GetDayCount(chart)
            },
            {
                text:'指标窗口个数',
                children: this.GetIndexWindowCount(chart)
            },
            {
                text: "副图指标切换",
                children: this.GetIndex(chart)
            }
        ];

        var symbol=chart.Symbol;
        if (MARKET_SUFFIX_NAME.IsSHSZStockA(symbol))
        {
            dataList.push({text:'集合竞价',children: this.GetShowBeforeData(chart)});
        }

        var identify=event.data.FrameID;
        JSConsole.Chart.Log('[MinuteRightMenu::DoModal]',identify);
        rightMenu.Show({
            windowIndex :identify,
            x:x+chart.UIElement.offsetLeft,
            y:y+chart.UIElement.offsetTop,
            position:chart.Frame.Position,
            data:dataList
        })

        $(document).click(function () {
            rightMenu.Hide();
        });
    }

    this.GetDayCount=function(chart)
    {
        var data=
        [
            {
                text: "当日分时图",
                click: function () { chart.ChangeDayCount(1); },
                isBorder:true
            }, 
            {
                text: "最近2日",
                click: function () { chart.ChangeDayCount(2); }
            }, 
            {
                text: "最近3日",
                click: function () { chart.ChangeDayCount(3); }
            },
            {
                text: "最近4日",
                click: function () { chart.ChangeDayCount(4); }
            },
            {
                text: "最近5日",
                click: function () { chart.ChangeDayCount(5); }
            },
            {
                text: "最近6日",
                click: function () { chart.ChangeDayCount(6); }
            }
        ];
        
        if ((chart.DayCount-1)>=0 && (chart.DayCount-1)<data.length) data[chart.DayCount-1].selected=true;

        return data;
    }

    this.GetIndex=function (chart) 
    {
        var data=
        [
            {
                text: "MACD",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'MACD') }
            }, 
            {
                text: "DMI",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'DMI') }
            },
            {
                text: "DMA",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'DMA') }
            }, 
            {
                text: "BRAR",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'BRAR') }
            }, 
            {
                text: "KDJ",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'KDJ') }
            }, 
            {
                text: "RSI",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'RSI') }
            }, 
            {
                text: "WR",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'WR') }
            },  
            {
                text: "CCI",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'CCI') }
            },
            {
                text: "TRIX",
                click: function (windowIndex) { chart.ChangeIndex(windowIndex, 'TRIX') }
            }
        ];

        return data;
    }

    this.GetShowBeforeData=function(chart)  //是否显示集合竞价
    {
        if (chart.IsShowBeforeData)
        {
            var data=
            [
                {
                    text: "隐藏",
                    click: function () { chart.ShowBeforeData(false); },
                }
            ];
            
            return data;
        }
        else
        {
            var data=
            [
                {
                    text: "显示",
                    click: function () { chart.ShowBeforeData(true); },
                }
            ];

            return data;
        }
    }

    //指标窗口个数
    this.GetIndexWindowCount=function(chart)
    {
        var data=
        [
            {
                text: "1个窗口",
                click: function () { chart.ChangeIndexWindowCount(2); }
            },
            {
                text: "2个窗口",
                click: function () { chart.ChangeIndexWindowCount(3); }
            }, 
            {
                text: "3个窗口",
                click: function () { chart.ChangeIndexWindowCount(4); }
            }
        ];

        var count=chart.Frame.SubFrame.length-1;
        if ((count-1)>=0 && (count-1)<data.length) data[count-1].selected=true;  //选中

        return data;
    }
}

//画图工具 单个图形设置
function ChartPictureSettingMenu(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.HQChart;
    this.ChartPicture;
    this.SubToolsDiv;
    this.SettingMenu;

    this.DoModal=function(event)
    {
        var $body;
        if (!this.SubToolsDiv)
        {
            this.ID=Guid();
            var div=document.createElement("div");
            div.className='subTolls';
            div.id=this.ID;
            this.DivElement.appendChild(div);
            //$body = $("."+event.data.HQChart.ClassName).context.body;
            //$body.append(div);
            this.SubToolsDiv=div;
        }
        this.HQChart=event.data.HQChart;
        this.ChartPicture=event.data.ChartPicture;

        var frame=this.HQChart.Frame.SubFrame[0].Frame;
        // var top=frame.ChartBorder.GetTopTitle();
        var top=frame.ChartBorder.Top + 40;
        // var right=frame.ChartBorder.GetRight();
        var right=frame.ChartBorder.Right;
        var left=frame.ChartBorder.GetLeft();
        var className = this.ChartPicture.ClassName; //='ChartDrawPictureText'时加“设置”
        var toolsDiv = "";
        if(className === 'ChartDrawPictureText'){
            toolsDiv = '<span class="changes-color" title="改变图形颜色">'+
                           '<i class="iconfont icon-bianji"></i>'+
                           '<input type="color" name="color" id="color" class="change-color" value="'+ this.ChartPicture.LineColor +'">'+
                        '</span>\n' +
                        '<span class="subtool-set" title="设置"><i class="iconfont icon-shezhi"></i></span>'+
                        '<span class="subtool-del"><i class="iconfont icon-recycle_bin"></i></span>';
        }else{
            toolsDiv =
            '<p class="changes-color" title="改变图形颜色"><i class="iconfont icon-bianji"></i>' +
            '<input type="color" name="color" id="color" class="change-color" value="'+ this.ChartPicture.LineColor +'"></p>\n' +
            '        <p class="subtool-del"><i class="iconfont icon-recycle_bin"></i></p>';
        }

        this.SubToolsDiv.style.right = right + "px";
        this.SubToolsDiv.style.top = top + "px";
        this.SubToolsDiv.innerHTML = toolsDiv;
        this.SubToolsDiv.style.position = "absolute";
        this.SubToolsDiv.style.display = "block";

        var hqChart = this.HQChart;
        var picture = this.ChartPicture;
        var subToolDiv = this.SubToolsDiv;
        $(".subtool-del").click(function(){
            hqChart.ClearChartDrawPicture(picture);
            // subToolDiv.innerHTML = "";
            $(".subTolls").css("display","none");
        });
        var self = this;
        $(".subtool-set").click(function(){
            $(self.SubToolsDiv).hide();
            //创建div设置窗口
            if (!this.SettingMenu) self.SettingMenu=new ChartPictureTextSettingMenu(frame.ChartBorder.UIElement.parentNode);

            self.SettingMenu.ChartPicture=picture;
            self.SettingMenu.HQChart=hqChart;
            self.SettingMenu.Position={Left:right + 80,Top:top + 20};
            self.SettingMenu.DoModal();
        });
        $(".changes-color").click(function () {
            document.getElementById('color').click();
            $(".change-color").change(function () {
                var color = $(".change-color").val();
                picture.LineColor = color;
                picture.PointColor = color;
                if (hqChart.ChartDrawStorage) hqChart.ChartDrawStorage.SaveDrawData(picture);   //保存下
            });
        })


        JSConsole.Chart.Log("[ChartPictureSettingMenu::DoModal]", {Top:top,Left:left, Right:right});
    }
}

//画图工具 文本设置窗口
function ChartPictureTextSettingMenu(divElement)
{
    this.newMethod=IDivDialog;   //派生
    this.newMethod(divElement);
    delete this.newMethod;

    this.ChartPicture;
    this.SettingDiv;
    this.Position;

    this.BackupData;    //画图工具备份数据

    this.Close=function()   
    {
        if (this.SettingDiv) this.DivElement.removeChild(this.SettingDiv);  //直接删除
    }

    this.DoModal=function()
    {
        var text=this.ChartPicture.Text;    //显示的文本
        var fontOption=this.ChartPicture.FontOption;    //字体设置
        var lineColor=this.ChartPicture.LineColor;
        //数据备份, 点取消的时候把备份数据设置回去
        this.BackupData=
        {
            Text:text, 
            LineColor:lineColor,
            FontOption:{Family: fontOption.Family, Size: fontOption.Size, Weight: fontOption.Weight, Style: fontOption.Style }
        };
        JSConsole.Chart.Log('[ChartPictureTextSettingMenu::DoModal] picture info',this.BackupData);

        var self=this;
        var div=this.DivElement.getElementsByClassName('chartpicture-text-setting')[0];
        if (!div)
        {
            div=document.createElement("div");
            div.className='chartpicture-text-setting';
            this.DivElement.appendChild(div);
            this.SettingDiv=div;
        }
        else 
        {
            this.SettingDiv=div;
        }
        
        var titleContainerStr = '<div class="titleWrap">'+
                                    '<span class="titleName">样式设置</span>'+
                                    '<i class="closeBtn iconfont icon-close"></i>'+
                                '</div>';
                            
        var fontSizeArray = [10,11,12,14,16,20,24,28,32,40];
        var fontArray = ['微软雅黑','宋体','Arial','仿宋'];
        var sizeListStr = "";
        var fontListStr = "";
        fontArray.forEach(function(item,index){
            fontListStr += index !== 0 ? '<p>'+item+'</P>' : '<p class="active">'+item+'</P>';
        });
        fontSizeArray.forEach(function(item,index){
            sizeListStr += index !== 5 ? '<p>'+item+'</P>' : '<p class="active">'+item+'</P>';
        });
        var contentContainerStr = '<div class="contentWrap">'+
                                    '<div class="styleOptions">'+
                                        '<span class="colorPicker"><input type="color" id="fontColor" value="#1e90ff"></span>'+
                                        '<div class="likeSelect fontSelect"><span class="choicedText">微软雅黑</span><div class="selectList">'+fontListStr+'</div><i class="iconfont icon-xia"></i></div>'+
                                        '<div class="likeSelect fontSizeSelect"><span class="choicedText">20</span><div class="selectList">'+sizeListStr+'</div><i class="iconfont icon-xia"></i></div>'+
                                        '<span class="strongFont likeBtn"><i class="iconfont icon-jiacu"></i></span>'+
                                        '<span class="italicsFont likeBtn"><i class="iconfont icon-qingxieL"></i></span>'+
                                    '</div>'+
                                    '<textarea class="tArea" id="tArea" placeholder="Text"></textarea>'+
                                  '</div>';
        var btnContainer = '<div class="btnsContainer">'+
                                '<span class="okBtn btn">确认</span>'+
                                '<span class="cancelBtn btn">取消</span>'+
                            '</div>';
        var DoModalStr = titleContainerStr+contentContainerStr+btnContainer;
        this.SettingDiv.style.left = this.Position.Left + "px";
        this.SettingDiv.style.top = this.Position.Top + "px";
        this.SettingDiv.innerHTML=DoModalStr;
        this.SettingDiv.style.position = "absolute";
        this.SettingDiv.style.display = "block";
        $(".chartpicture-text-setting .colorPicker").css({ //初始设置
            "borderColor":self.ChartPicture.LineColor,
            "background-color":self.ChartPicture.LineColor
        });

        var family = this.ChartPicture.FontOption.Family;
        $('.chartpicture-text-setting .fontSelect .choicedText').html(family);
        fontArray.forEach(function(item,index){
            if(item == family){
                $('.chartpicture-text-setting .fontSelect p').removeClass('active');
                $('.chartpicture-text-setting .fontSelect p').eq(index).addClass('active');
            }
        });

        var size = this.ChartPicture.FontOption.Size;
        $('.chartpicture-text-setting .fontSizeSelect .choicedText').html(size);
        fontSizeArray.forEach(function(item,index){
            if(item == size){
                $('.chartpicture-text-setting .fontSizeSelect p').removeClass('active');
                $('.chartpicture-text-setting .fontSizeSelect p').eq(index).addClass('active');
            }
        });

        var weight = this.ChartPicture.FontOption.Weight;
        if( weight != null && weight == 'bold'){
            $('.chartpicture-text-setting .strongFont').addClass('hot');
        }

        var style = this.ChartPicture.FontOption.Style;
        if( style != null && style == 'italic'){
            $('.chartpicture-text-setting .italicsFont').addClass('hot');
        }

        var text = this.ChartPicture.Text;
        $('.chartpicture-text-setting .tArea').val(text);  //结束初始设置

        var defaultTextOption = { Family:'微软雅黑', Size:20, Weight:null, Style:null };
        $(".chartpicture-text-setting #fontColor").change(
            {
                Picture:this.ChartPicture
            },
            function(event)
            {  //颜色选择
                var value = $(this).val();
                $(this).parent().css({
                    "borderColor":value,
                    "background-color":value
                });
                var chart=event.data.Picture;
                chart.LineColor = value;
                if (chart.Update) chart.Update();   //更新界面
            }
        );
        $(".chartpicture-text-setting .fontSelect,.chartpicture-text-setting .fontSizeSelect").click(function(){
            $(this).find('.selectList').toggle();
            $(this).toggleClass('hot');
        });
        $(".chartpicture-text-setting .fontSelect p").click(
            {
                Picture:this.ChartPicture
            },
            function(event){ //字体选择
                var choicedText = $(this).closest(".fontSelect").find('.choicedText').html();
                var currentSelect = event.currentTarget.innerHTML;
                if(choicedText !== currentSelect){
                    $(this).closest(".fontSelect").find('.choicedText').html(currentSelect);
                    $(this).siblings().removeClass('active');
                    $(this).addClass('active');
                    var chart = event.data.Picture;
                    chart.FontOption.Family = currentSelect;
                    if (chart.Update) chart.Update();   //更新界面
                }
        });
        $(".chartpicture-text-setting .fontSizeSelect p").click(
            {
                Picture:this.ChartPicture
            },
            function(event){  //字号选择
            var choicedText = $(this).closest(".fontSizeSelect").find('.choicedText').html();
            var currentSelect = event.currentTarget.innerHTML;
            if(choicedText !== currentSelect){
                $(this).closest(".fontSizeSelect").find('.choicedText').html(currentSelect);
                $(this).siblings().removeClass('active');
                $(this).addClass('active');
                var chart = event.data.Picture;
                chart.FontOption.Size = Number(currentSelect);
                if (chart.Update) chart.Update();   //更新界面
            }
        });
        $(".chartpicture-text-setting .strongFont").click(
            {
                Picture:this.ChartPicture
            },
            function(event){
            $(this).toggleClass('hot');
            var classnames = $(this).attr('class');
            if(classnames.indexOf('hot') > 0){
                var chart = event.data.Picture;
                chart.FontOption.Weight = 'bold';
                if (chart.Update) chart.Update();   //更新界面
            }
        });
        $(".chartpicture-text-setting .italicsFont").click(
            {
                Picture:this.ChartPicture
            },
            function(event){
            $(this).toggleClass('hot')
            var classnames = $(this).attr('class');
            if(classnames.indexOf('hot') > 0){
                var chart = event.data.Picture;
                chart.FontOption.Style = 'italic';
                if (chart.Update) chart.Update();   //更新界面
            }
        });
        $(".chartpicture-text-setting .titleWrap .closeBtn,.chartpicture-text-setting .btnsContainer .cancelBtn").click(  //取消
            {
                Picture:this.ChartPicture
            },
            function(event){
                var picture = event.data.Picture;
                picture.Text = self.BackupData.Text;
                picture.LineColor = self.BackupData.LineColor;
                picture.FontOption = self.BackupData.FontOption;
                if (picture.Update) picture.Update();
                self.Close();
        });
        $(".chartpicture-text-setting .tArea").keyup( //文本内容
            {
                Picture:this.ChartPicture
            },
            function(event){
                JSConsole.Chart.Log('[ChartPictureTextSettingMenu::DoModal] $(".chartpicture-text-setting .tArea").keyup()');
                var content = $(this).val();
                var chart = event.data.Picture;
                chart.Text = content;
                if (chart.Update) chart.Update();   //更新界面
        });

        //确定按钮
        $(".chartpicture-text-setting .btnsContainer .okBtn").click(
            function()
            {  
                self.Close(); 
                if (self.HQChart && self.HQChart.ChartDrawStorage) self.HQChart.ChartDrawStorage.SaveDrawData(self.ChartPicture);   //保存下
            }
        );
    }
}



///////////////////////////////////////////////////////////////////////////////////////
//
//  各个品种分钟走势图坐标信息
//
//////////////////////////////////////////////////////////////////////////////////////
var MARKET_SUFFIX_NAME=
{
    SH:'.SH',
    SZ:'.SZ',
    HK:'.HK',            //港股
    FHK:'.FHK',          //港股期货            
    SHFE: '.SHF',        //上期所 (Shanghai Futures Exchange)
    SHFE2:'.SHFE',       //上期所 (Shanghai Futures Exchange)
    CFFEX: '.CFE',       //中期所 (China Financial Futures Exchange)
    DCE: '.DCE',         //大连商品交易所(Dalian Commodity Exchange)
    CZCE: '.CZC',        //郑州期货交易所
    USA:'.USA',          //美股
    FTSE:'.FTSE',        //富时中国

    BIT:'.BIT',          //数字货币 如比特币
    BIZ:'.BIZ',          //数字货币

    FOREX:'.FOREX',      //外汇 (Foreign Exchange)

    ET:'.ET',            //其他未知的品种

    IsET:function(upperSymbol)
    {
        if (!upperSymbol) return false;
        return upperSymbol.indexOf(this.ET) > 0;
    },

    IsETShowAvPrice:function(upperSymbol)   //是否显示均价
    {
        return false;
    },

    IsForeignExchange(upperSymbol)
    {
        if (!upperSymbol) return false;
        return upperSymbol.indexOf(this.FOREX) > 0;
    },

    IsFTSE:function(upperSymbol)
    {
        if (!upperSymbol) return false;
        return upperSymbol.indexOf(this.FTSE) > 0;
    },

    IsFHK:function(upperSymbol)
    {
        if (!upperSymbol) return false;
        return upperSymbol.indexOf(this.FHK) > 0;
    },

    IsBIT:function(upperSymbol)
    {
        if (!upperSymbol) return false;
        if (upperSymbol.indexOf(this.BIT) > 0) return true;
        if (upperSymbol.indexOf(this.BIZ) > 0) return true;
        return false;
    },

    IsUSA:function(upperSymbol) //是否是美股
    {
        if (!upperSymbol) return false;
        return upperSymbol.indexOf(this.USA) > 0;
    },

    IsSH: function (upperSymbol)
    {
        //需要精确匹配最后3位
        var pos = upperSymbol.length-this.SH.length;
        var find = upperSymbol.indexOf(this.SH);
        return find == pos;
    },

    IsSZ: function (upperSymbol)
    {
        var pos = upperSymbol.length - this.SZ.length;
        var find = upperSymbol.indexOf(this.SZ);
        return find == pos;
    },

    IsHK: function (upperSymbol)
    {
        var pos = upperSymbol.length - this.HK.length;
        var find = upperSymbol.indexOf(this.HK);
        return find == pos;
    },

    IsSHFE: function (upperSymbol)
    {
        if (!upperSymbol) return false;
        if (upperSymbol.indexOf(this.SHFE) > 0) return true;
        if (upperSymbol.indexOf(this.SHFE2) > 0) return true;
        return false;
    },
        
    IsCFFEX: function (upperSymbol) 
    {
        return upperSymbol.indexOf(this.CFFEX) > 0;
    },

    IsDCE: function (upperSymbol) 
    {
        return upperSymbol.indexOf(this.DCE) > 0;
    },

    IsCZCE: function (upperSymbol) 
    {
        return upperSymbol.indexOf(this.CZCE) > 0;
    },

    IsChinaFutures:function(upperSymbol)   //是否是国内期货
    {
        return this.IsCFFEX(upperSymbol) || this.IsCZCE(upperSymbol) || this.IsDCE(upperSymbol) || this.IsSHFE(upperSymbol);
    },

    IsSHSZ:function(upperSymbol)            //是否是沪深的股票
    {
        return this.IsSZ(upperSymbol)|| this.IsSH(upperSymbol);
    },

    IsSHSZFund:function(upperSymbol)        //是否是交易所基金
    {
        if (!upperSymbol) return false;

        if (this.IsSH(upperSymbol)) //51XXXX.SH
        {
            if (upperSymbol.charAt(0)=='5' && upperSymbol.charAt(1)=='1') return true;
        }
        else if (this.IsSZ(upperSymbol)) //15XXXX.sz, 16XXXX.sz, 17XXXX.sz, 18XXXX.sz
        {
            if (upperSymbol.charAt(0)=='1' && 
                (upperSymbol.charAt(1)=='5' || upperSymbol.charAt(1)=='6' || upperSymbol.charAt(1)=='7' || upperSymbol.charAt(1)=='8') ) return true;
        }

        return false;
    },

    IsSHSZIndex:function(symbol)     //是否是沪深指数代码
    {
        if (!symbol) return false;
        var upperSymbol=symbol.toUpperCase();
        if (this.IsSH(upperSymbol))
        {
            var temp=upperSymbol.replace('.SH','');
            if (upperSymbol.charAt(0)=='0' && parseInt(temp)<=3000) return true;

        }
        else if (this.IsSZ(upperSymbol))
        {
            if (upperSymbol.charAt(0)=='3' && upperSymbol.charAt(1)=='9') return true;
        }
        else if (upperSymbol.indexOf('.CI')>0)  //自定义指数
        {
            return true;
        }

        return false;
    },

    IsSHSZStockA:function(symbol) //是否是沪深A股
    {
        if (!symbol) return false;
        var upperSymbol=symbol.toUpperCase();
        if (this.IsSH(upperSymbol))
        {
            var temp=upperSymbol.replace('.SH','');
            if (upperSymbol.charAt(0)=='6') return true;

        }
        else if (this.IsSZ(upperSymbol))
        {
            if (upperSymbol.charAt(0)=='0')
            {
                if (upperSymbol.charAt(1)=='0' && upperSymbol.charAt(1)=='2') return true;  //002 中小板
                if (upperSymbol.charAt(1)!='7' && upperSymbol.charAt(1)!='8') return true;
            } 
        }

        return false;
    },

    IsSHStockSTAR:function(symbol)   // 是否是科创板 Sci-Tech innovAtion boaRd (STAR Market)
    {
        if (!symbol) return false;
        var upperSymbol=symbol.toUpperCase();
        if (!this.IsSH(upperSymbol)) return false;
        if (upperSymbol.charAt(0)=='6' && upperSymbol.charAt(1)=='8' && upperSymbol.charAt(2)=='8')
            return true;
        
        return false;
    },

    GetMarketStatus:function(symbol)    //获取市场状态 0=闭市 1=盘前 2=盘中 3=盘后
    {
        if (!symbol) return 0;
        var upperSymbol=symbol.toUpperCase();
        var nowDate= new Date();
        var day = nowDate.getDay();
        var time = nowDate.getHours() * 100 + nowDate.getMinutes();
        if (this.IsUSA(upperSymbol))
        {
            var usaDate=GetLocalTime(-4);
            var day = usaDate.getDay();
            var time = usaDate.getHours() * 100 + usaDate.getMinutes();
            if(day == 6 || day== 0) return 0;   //周末

            //9:30 - 16:00 考虑夏令时间时间增加1小时 9:30 - 17:00
            if (time>1730) return 3;
            if (time<930) return 1;

            return 2;
        }
        else if (this.IsBIT(upperSymbol))   //数字货币24小时
        {
            return 2;
        }
        else if (this.IsForeignExchange(upperSymbol))   //外汇24小时
        {
            return 2;
        }
        else if (this.IsFTSE(upperSymbol))  //富时中国 9:00-16:30 17:00-04:45
        {
            if(day == 6 || day== 0) return 0;   //周末
            if (time>=830 && time<=2359) return 2;
            if (time>=0 && time<=500) return 2;
            return 0;
        }
        else if (this.IsFHK(upperSymbol))   //港股指数期货 9:15-12:00 13:00-16:30 17:15-01:00
        {
            if(day == 6 || day== 0) return 0;   //周末
            if (time>=900 && time<=2359) return 2;
            if (time>=0 && time<=320) return 2;
            return 0;
        }
        else if (this.IsET(upperSymbol))
        {
            return this.GetETMarketStatus(symbol);
        }
        else    //9:30 - 15:40
        {
            if(day == 6 || day== 0) return 0;   //周末
            if(time>1540) return 3;
            if(time<925) return 1;
            return 2;   
        }

    },

    GetLimitPriceRange:function(symbol, name) //涨停范围
    {
        if (!this.IsSHSZStockA(symbol)) return null;
        if (this.IsSHStockSTAR(symbol)) return {Max:0.2 , Min:-0.2};    //科创板 [20%- -20%]
        
        if (!name) return null;
        if (name.indexOf('ST')>=0) return { Max:0.05, Min:-0.05 }; //ST 股票 [5% - -5%]
        
        return {Max:0.1 , Min:-0.1}; //[10% - -10%]
    },

    GetFHKDecimal:function(symbol)  //港股指数期货 小数位数
    {
        return 0;
    },

    GetFTSEDecimal:function(symbol) //富时中国A50期货 小数位数
    {
        return 0;
    },

    GetBITDecimal:function(symbol)
    {
        return 2;
    },

    GetETDecimal:function(symbol)
    {
        return 2;
    },

    GetForeignExchangeDecimal:function(symbol)
    {
        return 4;
    },

    GetETMarketStatus:function(symbol)  
    {
        // 0=闭市 1=盘前 2=盘中 3=盘后
        return 2;
    }
}


//走势图分钟数据对应的时间
function MinuteTimeStringData() 
{
    this.SHSZ = null;       //上海深证交易所时间
    this.HK = null;         //香港交易所时间
    this.Futures=new Map(); //期货交易时间 key=时间名称 Value=数据
    this.USA = null;        //美股交易时间
    this.FTSE=null;         //富时中国
    this.FHK=null;          //港股指数期货
    this.ForeEx=null;       //外汇

    this.Initialize = function ()  //初始化 默认只初始化沪深的 其他市场动态生成
    {
        //this.SHSZ = this.CreateSHSZData();
        //this.HK = this.CreateHKData();
    }

    this.GetET=function(upperSymbol)   //当天所有的分钟
    {
        throw {Name:'MinuteTimeStringData::GetET', Error:'not implement'};
    }

    this.GetSHSZ=function() //动态创建
    {
        if (!this.SHSZ) this.SHSZ=this.CreateSHSZData();
        return this.SHSZ;
    }

    this.GetHK=function()
    {
        if (!this.HK) this.HK = this.CreateHKData();
        return this.HK;
    }

    this.GetFutures=function(splitData)
    {
        if (!this.Futures.has(splitData.Name)) 
        {
            var data = this.CreateTimeData(splitData.Data);
            this.Futures.set(splitData.Name,data);
        }
        
        return this.Futures.get(splitData.Name);
    }

    this.GetUSA=function()
    {
        if (!this.USA) this.USA=this.CreateUSAData(0);
        return this.USA;
    }

    this.GetFTSE=function()
    {
        if (!this.FTSE) this.FTSE=this.CreateFTSEData();
        return this.FTSE;
    }

    this.GetFHK=function()
    {
        if (!this.FHK) this.FHK=this.CreateFHKData();
        return this.FHK;
    }

    this.GetForeignExchange=function()
    {
        if (!this.ForeEx) this.ForeEx=this.CreateForeignExchangeData();
        return this.ForeEx;
    }

    this.CreateSHSZData = function () 
    {
        const TIME_SPLIT =
            [
                { Start: 925, End: 925 },
                { Start: 930, End: 1130 },
                { Start: 1300, End: 1500 }
            ];

        return this.CreateTimeData(TIME_SPLIT);
    }

    this.CreateHKData = function () 
    {
        const TIME_SPLIT =
            [
                { Start: 930, End: 1200 },
                { Start: 1300, End: 1600 }
            ];

        return this.CreateTimeData(TIME_SPLIT);
    }

    this.CreateFTSEData=function()
    {
        const TIME_SPLIT=
        [
            { Start:1700, End:2359 },
            { Start:0,  End:445 },
            { Start:900, End:1630 }
        ];

        return this.CreateTimeData(TIME_SPLIT);
    }

    this.CreateFHKData=function()
    {
         //港股指数期货 9:15-12:00 13:00-16:30 17:15-03:00
         const TIME_SPLIT=           
         [
             { Start:1715, End:2359 },
             { Start:0,  End:300 },
             { Start:915, End:1200 },
             { Start:1300, End:1630 },
         ];

        return this.CreateTimeData(TIME_SPLIT);
    }

    this.CreateUSAData=function(type)
    {
        if (type==1)    //美国夏令时
        {
            const TIME_SPLIT =
            [
                { Start: 2130, End: 2359 },
                { Start: 0, End: 400 }
            ];

            return this.CreateTimeData(TIME_SPLIT); 
        }
        else if (type==2)    //非夏令时
        {
            const TIME_SPLIT =
            [
                { Start: 2230, End: 2359 },
                { Start: 0, End: 500 }
            ];
            
            return this.CreateTimeData(TIME_SPLIT); 
        }
        else    //使用美国本地时间
        {
            const TIME_SPLIT =
            [
                { Start: 930, End: 1600 }   //美国东部时间9:30到16:00
            ];
            
            return this.CreateTimeData(TIME_SPLIT); 
        }
    }

    this.CreateForeignExchangeData=function()
    {
         //外汇 7:00 - 6:59
         const TIME_SPLIT=           
         [
             { Start:600, End:2359 },
             { Start:0,  End:559 },
         ];

        return this.CreateTimeData(TIME_SPLIT);
    }

    this.CreateTimeData = function (timeSplit) 
    {
        var data = [];
        for (var i in timeSplit) 
        {
            var item = timeSplit[i];
            for (var j = item.Start; j <= item.End; ++j) 
            {
                if (j % 100 >= 60) continue;    //大于60分钟的数据去掉
                data.push(j);
            }
        }
        return data;
    }

    this.GetTimeData = function (symbol) 
    {
        if (!symbol) return this.SHSZ;

        var upperSymbol = symbol.toLocaleUpperCase(); //转成大写
        if (MARKET_SUFFIX_NAME.IsSH(upperSymbol) || MARKET_SUFFIX_NAME.IsSZ(upperSymbol)) return this.GetSHSZ();
        if (MARKET_SUFFIX_NAME.IsHK(upperSymbol)) return this.GetHK();
        if (MARKET_SUFFIX_NAME.IsUSA(upperSymbol)) return this.GetUSA(true);
        if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol) || MARKET_SUFFIX_NAME.IsCZCE(upperSymbol) || MARKET_SUFFIX_NAME.IsDCE(upperSymbol) || MARKET_SUFFIX_NAME.IsSHFE(upperSymbol))
        {
            var splitData = g_FuturesTimeData.GetSplitData(upperSymbol);
            if (!splitData) return null;
            return this.GetFutures(splitData);
        }
        if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) return this.GetFTSE();
        if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) return this.GetFHK();
        if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) return this.GetForeignExchange();
        if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) return this.GetET(upperSymbol);
    }
}

//走势图刻度分钟线
function MinuteCoordinateData() 
{
    //沪深走势图时间刻度
    const SHZE_MINUTE_X_COORDINATE =
        {
            Full:   //完整模式
            [
                [0, 0, "rgb(200,200,200)", "09:30"],
                [31, 0, "RGB(200,200,200)", "10:00"],
                [61, 0, "RGB(200,200,200)", "10:30"],
                [91, 0, "RGB(200,200,200)", "11:00"],
                [122, 1, "RGB(200,200,200)", "13:00"],
                [152, 0, "RGB(200,200,200)", "13:30"],
                [182, 0, "RGB(200,200,200)", "14:00"],
                [212, 0, "RGB(200,200,200)", "14:30"],
                [242, 1, "RGB(200,200,200)", "15:00"], // 15:00
            ],
            Simple: //简洁模式
            [
                [0, 0, "rgb(200,200,200)", "09:30"],
                [61, 0, "RGB(200,200,200)", "10:30"],
                [122, 1, "RGB(200,200,200)", "13:00"],
                [182, 0, "RGB(200,200,200)", "14:00"],
                [242, 1, "RGB(200,200,200)", "15:00"]
            ],
            Min:   //最小模式     
            [
                [0, 0, "rgb(200,200,200)", "09:30"],
                [122, 1, "RGB(200,200,200)", "13:00"],
                [242, 1, "RGB(200,200,200)", "15:00"]
            ],

            Count: 243,
            MiddleCount: 122,

            GetData: function (width) 
            {
                if (width < 200) return this.Min;
                else if (width < 400) return this.Simple;

                return this.Full;
            }
        };

    //港股走势图时间刻度
    const HK_MINUTE_X_COORDINATE =
        {
            Full:   //完整模式
            [
                [0, 1, "RGB(200,200,200)", "09:30"],
                [30, 0, "RGB(200,200,200)", "10:00"],
                [60, 1, "RGB(200,200,200)", "10:30"],
                [90, 0, "RGB(200,200,200)", "11:00"],
                [120, 1, "RGB(200,200,200)", "11:30"],
                [151, 0, "RGB(200,200,200)", "13:00"],
                [181, 1, "RGB(200,200,200)", "13:30"],
                [211, 0, "RGB(200,200,200)", "14:00"],
                [241, 1, "RGB(200,200,200)", "14:30"],
                [271, 0, "RGB(200,200,200)", "15:00"],
                [301, 1, "RGB(200,200,200)", "15:30"],
                [331, 1, "RGB(200,200,200)", "16:00"]
            ],
            Simple: //简洁模式
            [
                [0, 1, "RGB(200,200,200)", "09:30"],
                [60, 1, "RGB(200,200,200)", "10:30"],
                [120, 1, "RGB(200,200,200)", "11:30"],
                [211, 0, "RGB(200,200,200)", "14:00"],
                [271, 0, "RGB(200,200,200)", "15:00"],
                [331, 1, "RGB(200,200,200)", "16:00"]
            ],
            Min:   //最小模式     
            [
                [0, 1, "RGB(200,200,200)", "09:30"],
                [151, 0, "RGB(200,200,200)", "13:00"],
                [331, 1, "RGB(200,200,200)", "16:00"]
            ],

            Count: 332,
            MiddleCount: 151,

            GetData: function (width) 
            {
                if (width < 200) return this.Min;
                else if (width < 450) return this.Simple;

                return this.Full;
            }
        };

    //美股走势图时间刻度
    const USA_MINUTE_X_COORDINATE =
    {
        /*
        Full:   //完整模式
        [
            [0, 0, "rgb(200,200,200)", "21:30"],
            [60, 0, "RGB(200,200,200)", "22:30"],
            [120, 1, "RGB(200,200,200)", "23:30"],
            [210, 0, "RGB(200,200,200)", "01:00"],
            [270, 0, "RGB(200,200,200)", "02:00"],
            [330, 0, "RGB(200,200,200)", "03:00"],
            [390, 0, "RGB(200,200,200)", "04:00"],
        ],
        Simple: //简洁模式
        [
            [0, 0, "rgb(200,200,200)", "21:30"],
            [160, 1, "RGB(200,200,200)", "00:00"],
            [270, 0, "RGB(200,200,200)", "02:00"],
            [390, 0, "RGB(200,200,200)", "04:00"],
        ],
        Min:   //最小模式     
        [
            [0, 0, "rgb(200,200,200)", "21:30"],
            [160, 1, "RGB(200,200,200)", "00:00"],
            [390, 0, "RGB(200,200,200)", "04:00"],
        ],
        */

        //美国本地时间
        Full:   //完整模式
        [
            [0, 0, "rgb(200,200,200)", "9:30"],
            [30, 0, "RGB(200,200,200)", "10:00"],
            [90, 1, "RGB(200,200,200)", "11:00"],
            [150, 0, "RGB(200,200,200)", "12:00"],
            [210, 0, "RGB(200,200,200)", "13:00"],
            [270, 0, "RGB(200,200,200)", "14:00"],
            [330, 0, "RGB(200,200,200)", "15:00"],
            [390, 0, "RGB(200,200,200)", "16:00"],
        ],
        Simple: //简洁模式
        [
            [30, 0, "rgb(200,200,200)", "10:00"],
            [150, 1, "RGB(200,200,200)", "12:00"],
            [270, 0, "RGB(200,200,200)", "14:00"],
            [390, 0, "RGB(200,200,200)", "16:00"],
        ],
        Min:   //最小模式     
        [
            [30, 0, "rgb(200,200,200)", "10:00"],
            [210, 1, "RGB(200,200,200)", "13:00"],
            [390, 0, "RGB(200,200,200)", "16:00"],
        ],

        Count: 391,
        MiddleCount: 211,

        GetData: function (width) 
        {
            if (width < 200) return this.Min;
            else if (width < 400) return this.Simple;

            return this.Full;
        }
    };

    //富时中国
    const FTSE_MINUTE_X_COORDINATE=
    {
        Full:   //完整模式
        [
            [0, 1, "RGB(200,200,200)", "17:00"],
            //[60, 0, "RGB(200,200,200)", "18:00"],
            [120, 1, "RGB(200,200,200)", "19:00"],
            //[180, 0, "RGB(200,200,200)", "20:00"],
            [240, 1, "RGB(200,200,200)", "21:00"],
            //[300, 0, "RGB(200,200,200)", "22:00"],
            [360, 1, "RGB(200,200,200)", "23:00"],
            //[420, 0, "RGB(200,200,200)", "00:00"],
            [480, 1, "RGB(200,200,200)", "01:00"],
            //[540, 0, "RGB(200,200,200)", "02:00"],
            [600, 1, "RGB(200,200,200)", "03:00"],
            //[660, 1, "RGB(200,200,200)", "04:00"],
            [706, 1, "RGB(200,200,200)", "09:00"],
            //[766, 1, "RGB(200,200,200)", "10:00"],
            [826, 1, "RGB(200,200,200)", "11:00"],
            //[886, 1, "RGB(200,200,200)", "12:00"],
            [946, 1, "RGB(200,200,200)", "13:00"],
            //[1006, 1, "RGB(200,200,200)", "14:00"],
            [1066, 1, "RGB(200,200,200)", "15:00"],
            [1156, 1, "RGB(200,200,200)", "16:30"],
        ],
        Simple: //简洁模式
        [
            [0, 1, "RGB(200,200,200)", "17:00"],
            //[60, 0, "RGB(200,200,200)", "18:00"],
            //[120, 1, "RGB(200,200,200)", "19:00"],
            //[180, 0, "RGB(200,200,200)", "20:00"],
            [240, 1, "RGB(200,200,200)", "21:00"],
            //[300, 0, "RGB(200,200,200)", "22:00"],
            //[360, 1, "RGB(200,200,200)", "23:30"],
            //[420, 0, "RGB(200,200,200)", "00:00"],
            [480, 1, "RGB(200,200,200)", "01:00"],
            //[540, 0, "RGB(200,200,200)", "02:00"],
            //[600, 1, "RGB(200,200,200)", "03:00"],
            //[660, 1, "RGB(200,200,200)", "04:00"],
            [706, 1, "RGB(200,200,200)", "09:00"],
            //[766, 1, "RGB(200,200,200)", "10:00"],
            //[826, 1, "RGB(200,200,200)", "11:00"],
            //[886, 1, "RGB(200,200,200)", "12:00"],
            [946, 1, "RGB(200,200,200)", "13:00"],
            //[1006, 1, "RGB(200,200,200)", "14:00"],
            //[1066, 1, "RGB(200,200,200)", "15:00"],
            [1156, 1, "RGB(200,200,200)", "16:30"],
        ],
        Min:   //最小模式     
        [
            [0, 1, "RGB(200,200,200)", "17:00"],
            [706, 1, "RGB(200,200,200)", "09:00"],
            [1156, 1, "RGB(200,200,200)", "16:30"],
        ],

        Count: 1157,
        MiddleCount: 707,

        GetData: function (width) 
        {
            if (width < 200) return this.Min;
            else if (width < 450) return this.Simple;

            return this.Full;
        }
    }

    //港股指数期货
    const FHK_MINUTE_X_COORDINATE=
    {
        Full:   //完整模式
        [
            [0, 1, "RGB(200,200,200)", "17:15"],
            [105, 1, "RGB(200,200,200)", "19:00"],
            [225, 1, "RGB(200,200,200)", "21:00"],
            [345, 1, "RGB(200,200,200)", "23:00"],
            [586, 0, "RGB(200,200,200)", "09:15"],
            [691, 1, "RGB(200,200,200)", "11:00"],
            [812, 1, "RGB(200,200,200)", "14:00"],
            [963, 1, "RGB(200,200,200)", "16:30"],
        ],
        Simple: //简洁模式
        [
            [0, 1, "RGB(200,200,200)", "17:15"],
            [225, 1, "RGB(200,200,200)", "21:00"],
            [586, 0, "RGB(200,200,200)", "09:15"],
            [752, 1, "RGB(200,200,200)", "13:00"],
            [963, 1, "RGB(200,200,200)", "16:30"],
        ],
        Min:   //最小模式     
        [
            [0, 1, "RGB(200,200,200)", "17:15"],
            [586, 0, "RGB(200,200,200)", "09:15"],
            [963, 1, "RGB(200,200,200)", "16:30"],
        ],

        Count: 963,
        MiddleCount: 526,

        GetData: function (width) 
        {
            if (width < 200) return this.Min;
            else if (width < 450) return this.Simple;

            return this.Full;
        }
    }

    //外汇
    const FOREX_MINUTE_X_COORDINATE=
    {
        Full:   //完整模式
        [
            [0, 1, "RGB(200,200,200)", "06:00"],
            [120, 1, "RGB(200,200,200)", "08:00"],
            [240, 1, "RGB(200,200,200)", "10:00"],
            [360, 1, "RGB(200,200,200)", "12:00"],
            [480, 0, "RGB(200,200,200)", "14:00"],
            [600, 1, "RGB(200,200,200)", "16:00"],
            [720, 1, "RGB(200,200,200)", "18:00"],
            [840, 1, "RGB(200,200,200)", "20:00"],
            [960, 1, "RGB(200,200,200)", "22:00"],
            [1080, 1, "RGB(200,200,200)", "0:00"],
            [1200, 1, "RGB(200,200,200)", "02:00"],
            [1320, 1, "RGB(200,200,200)", "04:00"],
        ],
        Simple: //简洁模式
        [
            [0, 1, "RGB(200,200,200)", "06:00"],
            [240, 1, "RGB(200,200,200)", "10:00"],
            [480, 0, "RGB(200,200,200)", "14:00"],
            [720, 1, "RGB(200,200,200)", "18:00"],
            [960, 1, "RGB(200,200,200)", "22:00"],
            [1200, 1, "RGB(200,200,200)", "02:00"],
        ],
        Min:   //最小模式     
        [
            [0, 1, "RGB(200,200,200)", "06:00"],
            [480, 0, "RGB(200,200,200)", "14:00"],
            [960, 1, "RGB(200,200,200)", "22:00"],
        ],

        Count: 1440,
        MiddleCount: 600,

        GetData: function (width) 
        {
            if (width < 200) return this.Min;
            else if (width < 450) return this.Simple;

            return this.Full;
        }
    }

    

    this.GetCoordinateData = function (symbol, width) 
    {
        var data = null;
        if (!symbol) 
        {
            data = SHZE_MINUTE_X_COORDINATE;    //默认沪深股票
        }
        else 
        {
            var upperSymbol = symbol.toLocaleUpperCase(); //转成大写
            if (MARKET_SUFFIX_NAME.IsSH(upperSymbol) || MARKET_SUFFIX_NAME.IsSZ(upperSymbol) || MARKET_SUFFIX_NAME.IsSHSZIndex(upperSymbol))
                data = this.GetSHSZData(upperSymbol,width);
            else if (MARKET_SUFFIX_NAME.IsHK(upperSymbol))
                data = HK_MINUTE_X_COORDINATE;
            else if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol) || MARKET_SUFFIX_NAME.IsCZCE(upperSymbol) || MARKET_SUFFIX_NAME.IsDCE(upperSymbol) || MARKET_SUFFIX_NAME.IsSHFE(upperSymbol))
                return this.GetFuturesData(upperSymbol,width);
            else if (MARKET_SUFFIX_NAME.IsUSA(upperSymbol))
                data = this.GetUSAData(upperSymbol,width);
            else if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol))
                data=this.GetFTSEData(upperSymbol,width);
            else if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol))
                data=this.GetFHKData(upperSymbol,width);
            else if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol))
                data=this.GetForeignExchangeData(upperSymbol,width);
            else if (MARKET_SUFFIX_NAME.IsET(upperSymbol))
                data=this.GetETData(upperSymbol,width);
        }

        //JSConsole.Chart.Log('[MiuteCoordinateData]', width);
        var result = { Count: data.Count, MiddleCount: data.MiddleCount, Data: data.GetData(width) };
        return result;
    }

    this.GetSHSZData=function(upperSymbol,width)
    {
        var result=SHZE_MINUTE_X_COORDINATE;
        return result;
    }

    this.GetUSAData=function(upperSymbol,width)
    {
        var result=USA_MINUTE_X_COORDINATE;

        return result;
    }

    this.GetFuturesData = function (upperSymbol,width)
    {
        var splitData = g_FuturesTimeData.GetSplitData(upperSymbol);
        if (!splitData) return null;
        var stringData = g_MinuteTimeStringData.GetFutures(splitData);
        if (!stringData) return null;
        var result = { Count: stringData.length };
        var coordinate=null;
        var minWidth=200, simpleWidth=480;
        /*
        if (splitData.Name =='21:00-1:00,9:00-10:15,10:30-11:30,13:30-15:00')
        {
            minWidth=250;
            simpleWidth=500;
        }
        */
        
        if (width < minWidth) coordinate = splitData.Coordinate.Min;
        else if (width < simpleWidth) coordinate = splitData.Coordinate.Simple;
        else coordinate = splitData.Coordinate.Full;
        
        var data=[];
        for(var i=0;i<stringData.length;++i)
        {
            var value = stringData[i];
            for(var j=0;j<coordinate.length;++j)
            {
                var coordinateItem = coordinate[j];
                if (value == coordinateItem.Value)
                {
                    var item = [i, 0, 'RGB(200,200,200)', coordinateItem.Text];
                    data.push(item);
                    break;
                }
            }
        }

        result.Data = data;
        return result;
    }

    this.GetFTSEData=function(upperSymbol,width)
    {
        var result=FTSE_MINUTE_X_COORDINATE;
        return result;
    }

    this.GetFHKData=function(upperSymbol,width)
    {
        var result=FHK_MINUTE_X_COORDINATE;
        return result
    }

    this.GetForeignExchangeData=function(upperSymbol,width)
    {
        var result=FOREX_MINUTE_X_COORDINATE;
        return result;
    }

    this.GetETData=function(upperSymbol,width)
    {
        throw {Name:'MinuteCoordinateData::GetETData', Error:'not implement'};
    }
}

//期货不同品种 交易时间数据 
function FuturesTimeData()
{
    const TIME_SPLIT=
    [
        {
            Name:'9:00-10:15,10:30-11:30,13:30-15:00',
            Data:
            [
                //9:00-10:15,10:30-11:30,13:30-15:00
                { Start: 900, End: 1015 },
                { Start: 1031, End: 1130 },
                { Start: 1331, End: 1500 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 900, Text: '9:00' },
                    { Value: 930, Text: '9:30' },
                    { Value: 1000, Text: '10:00' },
                    { Value: 1030, Text: '10:30' },
                    { Value: 1100, Text: '11:00' },
                    { Value: 1330, Text: '13:30' },
                    { Value: 1400, Text: '14:00' },
                    { Value: 1430, Text: '14:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Simple: //简洁模式
                [
                    { Value: 900, Text: '9:00' },
                    { Value: 1000, Text: '10:00' },
                    { Value: 1330, Text: '13:30' },
                    { Value: 1430, Text: '14:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Min:   //最小模式  
                [
                    { Value: 900, Text: '9:00' },
                    { Value: 1330, Text: '13:30' },
                    { Value: 1500, Text: '15:00' },
                ]
            }
        },
        {

            Name:'9:15-11:30,13:00-15:15',
            Data:
            [
                { Start: 915, End: 1130 },
                { Start: 1301, End: 1515 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 930, Text: '9:30' },
                    { Value: 1000, Text: '10:00' },
                    { Value: 1030, Text: '10:30' },
                    { Value: 1100, Text: '11:00' },
                    { Value: 1300, Text: '13:00' },
                    { Value: 1330, Text: '13:30' },
                    { Value: 1400, Text: '14:00' },
                    { Value: 1430, Text: '14:30' },
                    { Value: 1515, Text: '15:15' },
                ],
                Simple: //简洁模式
                [
                    { Value: 930, Text: '9:30' },
                    { Value: 1030, Text: '10:30' },
                    { Value: 1300, Text: '13:00' },
                    { Value: 1400, Text: '14:00' },
                    { Value: 1515, Text: '15:15' },
                ],
                Min:   //最小模式  
                [
                    { Value: 930, Text: '9:30' },
                    { Value: 1300, Text: '13:00' },
                    { Value: 1515, Text: '15:15' },
                ]
            }
        },
        {
            Name:'9:30-11:30,13:00-15:00',
            Data:
            [
                { Start: 930, End: 1130 },
                { Start: 1301, End: 1500 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 930, Text: '9:30' },
                    { Value: 1000, Text: '10:00' },
                    { Value: 1030, Text: '10:30' },
                    { Value: 1100, Text: '11:00' },
                    { Value: 1300, Text: '13:00' },
                    { Value: 1330, Text: '13:30' },
                    { Value: 1400, Text: '14:00' },
                    { Value: 1430, Text: '14:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Simple: //简洁模式
                [
                    { Value: 930, Text: '9:30' },
                    { Value: 1030, Text: '10:30' },
                    { Value: 1300, Text: '13:00' },
                    { Value: 1400, Text: '14:00' },
                    { Value: 1500, Text: '15:00' },
                ],
                Min:   //最小模式  
                [
                    { Value: 930, Text: '9:30' },
                    { Value: 1300, Text: '13:00' },
                    { Value: 1500, Text: '15:00' },
                ]
            }
        },
        {
            Name:'21:00-23:30,9:00-10:15,10:30-11:30,13:30-15:00',
            Data:
            [
                { Start: 2100, End: 2330 },
                { Start: 901, End: 1015 },
                { Start: 1031, End: 1130 },
                { Start: 1331, End: 1500 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2200, Text: '22:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1031, Text: '10:30' },
                    { Value: 1331, Text: '13:30' },
                    { Value: 1430, Text: '14:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Simple: //简洁模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1331, Text: '13:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Min:   //最小模式  
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1500, Text: '15:00' },
                ]
            }
        },
        {
            Name:'21:00-1:00,9:00-10:15,10:30-11:30,13:30-15:00',
            Data:
            [   
                { Start: 2100, End: 2359 },
                { Start: 0, End: 100 },
                { Start: 901, End: 1015 },
                { Start: 1031, End: 1130 },
                { Start: 1331, End: 1500 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2200, Text: '22:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1031, Text: '10:30' },
                    { Value: 1331, Text: '13:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Simple: //简洁模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1031, Text: '10:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Min:   //最小模式  
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1500, Text: '15:00' },
                ]
            }
        },
        {
            Name:'21:00-2:30,9:00-10:15,10:30-11:30,13:30-15:00',
            Data:
            [
                { Start: 2100, End: 2359 },
                { Start: 0, End: 230 },
                { Start: 901, End: 1015 },
                { Start: 1031, End: 1130 },
                { Start: 1331, End: 1500 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 100, Text: '1:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1031, Text: '10:30' },
                    { Value: 1331, Text: '13:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Simple: //简洁模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1100, Text: '11:00' },
                    { Value: 1500, Text: '15:00' },
                ],
                Min:   //最小模式  
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 901, Text: '9:00' },
                    { Value: 1500, Text: '15:00' },
                ]
            }
        },
        {
            Name:'21:00-23:00,9:00-10:15,10:30-11:30,13:30-15:00',
            Data:
            [
                { Start: 2100, End: 2300 },
                { Start: 901, End: 1015 },
                { Start: 1031, End: 1130 },
                { Start: 1331, End: 1500 }
            ],
            Coordinate:
            {
                Full://完整模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2200, Text: '22:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 1030, Text: '10:30' },
                    { Value: 1331, Text: '13:30' },
                    { Value: 1430, Text: '14:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Simple: //简洁模式
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 1331, Text: '13:30' },
                    { Value: 1500, Text: '15:00' },
                ],
                Min:   //最小模式  
                [
                    { Value: 2100, Text: '21:00' },
                    { Value: 2300, Text: '23:00' },
                    { Value: 1500, Text: '15:00' },
                ]
            }
        }
    ];

    const MAP_TWOWORDS=new Map([
        //大连商品交易所
        [MARKET_SUFFIX_NAME.DCE + '-JD', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-FB', {Time:0,Decimal:2}],
        [MARKET_SUFFIX_NAME.DCE + '-BB', {Time:0,Decimal:2}],
        [MARKET_SUFFIX_NAME.DCE + '-PP', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-JM', {Time:3,Decimal:1}],
        //上期所
        [MARKET_SUFFIX_NAME.SHFE + '-CU', {Time:4,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-AL', {Time:4,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-NI', {Time:4,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-SN', {Time:4,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-ZN', {Time:4,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-PB', {Time:4,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-RU', {Time:6,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-FU', {Time:6,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-RB', {Time:6,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-BU', {Time:6,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-HC', {Time:6,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-WR', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-AG', {Time:5,Decimal:0}],
        [MARKET_SUFFIX_NAME.SHFE + '-AU', {Time:5,Decimal:2}],
        [MARKET_SUFFIX_NAME.SHFE + '-NR', {Time:5,Decimal:1}],
        [MARKET_SUFFIX_NAME.SHFE + '-SC', {Time:5,Decimal:1}],
        //郑州期货交易所
        [MARKET_SUFFIX_NAME.CZCE + '-CF', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-SR', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-MA', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-ZC', {Time:3,Decimal:1}],
        [MARKET_SUFFIX_NAME.CZCE + '-TA', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-RM', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-OI', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-ME', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-FG', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-WS', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-WT', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-GN', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-RO', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-RS', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-ER', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-RI', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-WH', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-AP', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-PM', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-QM', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-TC', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-JR', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-LR', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-SF', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.CZCE + '-SM', {Time:0,Decimal:0}],
        //中期所 
        [MARKET_SUFFIX_NAME.CFFEX + '-TF', {Time:1,Decimal:3}],
        [MARKET_SUFFIX_NAME.CFFEX + '-TS', {Time:1,Decimal:3}],
        [MARKET_SUFFIX_NAME.CFFEX + '-IH', {Time:2,Decimal:1}],
        [MARKET_SUFFIX_NAME.CFFEX + '-IC', {Time:2,Decimal:1}],
        [MARKET_SUFFIX_NAME.CFFEX + '-IF', {Time:2,Decimal:1}],
    ]);

    const MAP_ONEWORD=new Map([
        //大连商品交易所
        [MARKET_SUFFIX_NAME.DCE + '-C', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-L', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-V', {Time:0,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-A', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-B', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-M', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-Y', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-P', {Time:3,Decimal:0}],
        [MARKET_SUFFIX_NAME.DCE + '-J', {Time:3,Decimal:1}],
        [MARKET_SUFFIX_NAME.DCE + '-I', {Time:3,Decimal:1}],
        //中期所 
        [MARKET_SUFFIX_NAME.CFFEX + '-T', {Time:1,Decimal:3}],
    ]);

    this.GetData=function(upperSymbol)
    {
        var oneWord = upperSymbol.charAt(0);
        var twoWords = upperSymbol.substr(0,2);
        var oneWordName = null, twoWordsName=null;

        if (MARKET_SUFFIX_NAME.IsDCE(upperSymbol))  //大连商品交易所
        {
            oneWordName = MARKET_SUFFIX_NAME.DCE+'-'+oneWord;
            twoWordsName = MARKET_SUFFIX_NAME.DCE + '-' + twoWords;
        }
        else if (MARKET_SUFFIX_NAME.IsSHFE(upperSymbol))  //上期所
        {
            oneWordName = MARKET_SUFFIX_NAME.SHFE + '-' + oneWord;
            twoWordsName = MARKET_SUFFIX_NAME.SHFE + '-' + twoWords;
        }
        else if (MARKET_SUFFIX_NAME.IsCFFEX(upperSymbol))  //中期所 
        {
            oneWordName = MARKET_SUFFIX_NAME.CFFEX + '-' + oneWord;
            twoWordsName = MARKET_SUFFIX_NAME.CFFEX + '-' + twoWords;
        }
        else if (MARKET_SUFFIX_NAME.IsCZCE(upperSymbol))  //郑州期货交易所
        {
            oneWordName = MARKET_SUFFIX_NAME.CZCE + '-' + oneWord;
            twoWordsName = MARKET_SUFFIX_NAME.CZCE + '-' + twoWords;
        }

        if (MAP_TWOWORDS.has(twoWordsName))
        {
            return MAP_TWOWORDS.get(twoWordsName);
        }

        if (MAP_ONEWORD.has(oneWordName))
        {
            return MAP_ONEWORD.get(oneWordName);
        }

        return null;
    }

    this.GetSplitData = function (upperSymbol)
    {
        var data=this.GetData(upperSymbol);
        if (!data) return null;

        return TIME_SPLIT[data.Time];
    }

    this.GetDecimal=function(upperSymbol)
    {
        var data=this.GetData(upperSymbol);
        if (!data) return 2;

        return data.Decimal;
    }
}

var g_MinuteTimeStringData = new MinuteTimeStringData();
var g_MinuteCoordinateData = new MinuteCoordinateData();
var g_FuturesTimeData = new FuturesTimeData();


function GetfloatPrecision(symbol)  //获取小数位数
{
    var defaultfloatPrecision=2;    //默认2位
    if (!symbol) return defaultfloatPrecision;
    var upperSymbol=symbol.toUpperCase();

    if (MARKET_SUFFIX_NAME.IsSHSZFund(upperSymbol)) defaultfloatPrecision=3;    //基金3位小数
    else if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol)) defaultfloatPrecision=g_FuturesTimeData.GetDecimal(upperSymbol);  //期货小数位数读配置
    else if (MARKET_SUFFIX_NAME.IsFHK(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetFHKDecimal(upperSymbol);
    else if (MARKET_SUFFIX_NAME.IsFTSE(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetFTSEDecimal(upperSymbol);
    else if (MARKET_SUFFIX_NAME.IsBIT(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetBITDecimal(upperSymbol);
    else if (MARKET_SUFFIX_NAME.IsET(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetETDecimal(upperSymbol); 
    else if (MARKET_SUFFIX_NAME.IsForeignExchange(upperSymbol)) defaultfloatPrecision=MARKET_SUFFIX_NAME.GetForeignExchangeDecimal(upperSymbol);

    return defaultfloatPrecision;
}

function GetLocalTime(i)    //得到标准时区的时间的函数
{
    if (typeof i !== 'number') return;
    var d = new Date();
    //得到1970年一月一日到现在的秒数
    var len = d.getTime();
    //本地时间与GMT时间的时间偏移差
    var offset = d.getTimezoneOffset() * 60000;
    //得到现在的格林尼治时间
    var utcTime = len + offset;
    return new Date(utcTime + 3600000 * i);
}





/*
   Copyright (c) 2018 jones
 
    http://www.apache.org/licenses/LICENSE-2.0

   开源项目 https://github.com/jones2000/HQChart
 
   jones_2000@163.com

   分析家语法编译执行器 (H5版本)
*/

//日志输出类
if (!JSConsole)
{
    var JSConsole=
    { 
        Chart:{ Log:console.log },      //图形日志
        Complier:{ Log:console.log }    //编译器日志
    };
}

//API默认地址
var g_JSComplierResource=
{
    Domain : "https://opensource.zealink.com",               //API域名
    CacheDomain : "https://opensourcecache.zealink.com",      //缓存域名

    DrawIcon:
    {  
        Family:'iconfont', 
        Data:new Map([
            [1, { Text:'\ue625', Color:'rgb(255,106,106)'}],    //向上箭头
            [2, { Text:'\ue68b', Color:'rgb(46,139,87)'}],      //向下箭头
            [11,{ Text:'\ue624', Color:'rgb(245,159,40)'}],     //点赞
            [12,{ Text:'\ue600', Color:'rgb(245,159,40)'}],
            [13,{Text:'\ue70f',Color:'rgb(209,37,35)'}, ],      //B
            [14,{Text:'\ue64c',Color:'rgb(127,209,59)'} ],      //S
            [9, {Text:'\ue626',Color:'rgb(245,159,40)'} ],      //$
            [36,{Text:'\ue68c',Color:'rgb(255,106,106)'} ],     //关闭 红色
            [37,{Text:'\ue68c',Color:'rgb(46,139,87)'} ],       //关闭 绿色
            [38,{Text:'\ue68d',Color:'rgb(238,44,44)'} ],       //▲
            [39,{Text:'\ue68e',Color:'rgb(0,139,69)'} ],        //▼
            [46,{Text:'\ue64d',Color:'rgb(51,51,51)'} ],        //message
        ])
    },

    CustomDrawIcon:
    {
        Data:new Map()  //自定义图标 key=id value={ID:, Text:, Color, Family: }
    },

    CustomFunction: //定制函数
    {
        Data:new Map()  //自定义函数 key=函数名, Value:{ID:函数名, Callback: }
    },

    CustomVariant:  //自定义变量
    {
        Data:new Map()  //自定义函数 key=变量名, Value:{ID:变量名, Callback: }
    },

    GetDrawIcon:function(id)
    {
        var icon;
        if (g_JSComplierResource.CustomDrawIcon.Data.has(id))
        {
            const iconfont=g_JSComplierResource.CustomDrawIcon.Data.get(id);
            icon={ Symbol:iconfont.Text, Color:iconfont.Color, Family:iconfont.Family, IconFont:true, ID:id };
            return icon;
        }

        if (g_JSComplierResource.DrawIcon.Data.has(id))
        {
            const iconfont=g_JSComplierResource.DrawIcon.Data.get(id);
            icon={ Symbol:iconfont.Text, Color:iconfont.Color, Family:g_JSComplierResource.DrawIcon.Family, IconFont:true, ID:id };
            return icon;
        }

        return null;
    },

    IsCustomFunction:function(name)
    {
        if (g_JSComplierResource.CustomFunction.Data.has(name)) return true;
        return false;
    },

    IsCustomVariant:function(name)
    {
        if (g_JSComplierResource.CustomVariant.Data.has(name)) return true;
        return false;
    }
}

var Messages = {
    BadGetterArity: 'Getter must not have any formal parameters',
    BadSetterArity: 'Setter must have exactly one formal parameter',
    BadSetterRestParameter: 'Setter function argument must not be a rest parameter',
    ConstructorIsAsync: 'Class constructor may not be an async method',
    ConstructorSpecialMethod: 'Class constructor may not be an accessor',
    DeclarationMissingInitializer: 'Missing initializer in %0 declaration',
    DefaultRestParameter: 'Unexpected token =',
    DuplicateBinding: 'Duplicate binding %0',
    DuplicateConstructor: 'A class may only have one constructor',
    DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals',
    ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer',
    GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts',
    IllegalBreak: 'Illegal break statement',
    IllegalContinue: 'Illegal continue statement',
    IllegalExportDeclaration: 'Unexpected token',
    IllegalImportDeclaration: 'Unexpected token',
    IllegalLanguageModeDirective: 'Illegal \'use strict\' directive in function with non-simple parameter list',
    IllegalReturn: 'Illegal return statement',
    InvalidEscapedReservedWord: 'Keyword must not contain escaped characters',
    InvalidHexEscapeSequence: 'Invalid hexadecimal escape sequence',
    InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
    InvalidLHSInForIn: 'Invalid left-hand side in for-in',
    InvalidLHSInForLoop: 'Invalid left-hand side in for-loop',
    InvalidModuleSpecifier: 'Unexpected token',
    InvalidRegExp: 'Invalid regular expression',
    LetInLexicalBinding: 'let is disallowed as a lexically bound name',
    MissingFromClause: 'Unexpected token',
    MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
    NewlineAfterThrow: 'Illegal newline after throw',
    NoAsAfterImportNamespace: 'Unexpected token',
    NoCatchOrFinally: 'Missing catch or finally after try',
    ParameterAfterRestParameter: 'Rest parameter must be last formal parameter',
    Redeclaration: '%0 \'%1\' has already been declared',
    StaticPrototype: 'Classes may not have static property named prototype',
    StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
    StrictDelete: 'Delete of an unqualified identifier in strict mode.',
    StrictFunction: 'In strict mode code, functions can only be declared at top level or inside a block',
    StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
    StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
    StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
    StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
    StrictModeWith: 'Strict mode code may not include a with statement',
    StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
    StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
    StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
    StrictReservedWord: 'Use of future reserved word in strict mode',
    StrictVarName: 'Variable name may not be eval or arguments in strict mode',
    TemplateOctalLiteral: 'Octal literals are not allowed in template strings.',
    UnexpectedEOS: 'Unexpected end of input',
    UnexpectedIdentifier: 'Unexpected identifier',
    UnexpectedNumber: 'Unexpected number',
    UnexpectedReserved: 'Unexpected reserved word',
    UnexpectedString: 'Unexpected string',
    UnexpectedTemplate: 'Unexpected quasi %0',
    UnexpectedToken: 'Unexpected token %0',
    UnexpectedTokenIllegal: 'Unexpected token ILLEGAL',
    UnknownLabel: 'Undefined label \'%0\'',
    UnterminatedRegExp: 'Invalid regular expression: missing /'
};

var Regex = {
    // Unicode v8.0.0 NonAsciiIdentifierStart:
    NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/,
    // Unicode v8.0.0 NonAsciiIdentifierPart:
    NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/
}

var Character =
{
    FromCodePoint: function (cp) {
        return (cp < 0x10000) ? String.fromCharCode(cp) :
            String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) +
                String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023));
    },

    //是否是空格 https://tc39.github.io/ecma262/#sec-white-space
    IsWhiteSpace:function(cp)
    {
        return (cp === 0x20) || (cp === 0x09) || (cp === 0x0B) || (cp === 0x0C) || (cp === 0xA0) ||
            (cp >= 0x1680 && [0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(cp) >= 0);
    },

    //是否换行 https://tc39.github.io/ecma262/#sec-line-terminators
    IsLineTerminator:function(cp)
    {
        return (cp === 0x0A) || (cp === 0x0D) || (cp === 0x2028) || (cp === 0x2029);
    },

    // https://tc39.github.io/ecma262/#sec-names-and-keywords
    IsIdentifierStart:function(cp)
    {
        return (cp === 0x24) || (cp === 0x5F) ||
            (cp >= 0x41 && cp <= 0x5A) ||
            (cp >= 0x61 && cp <= 0x7A) ||
            (cp === 0x5C) ||
            ((cp >= 0x80) && Regex.NonAsciiIdentifierStart.test(Character.FromCodePoint(cp)));
    },

    IsIdentifierPart: function (cp) 
    {
        return (cp === 0x24) || (cp === 0x5F) ||
            (cp >= 0x41 && cp <= 0x5A) ||
            (cp >= 0x61 && cp <= 0x7A) ||
            (cp >= 0x30 && cp <= 0x39) ||
            (cp === 0x5C) ||
            ((cp >= 0x80) && Regex.NonAsciiIdentifierPart.test(Character.FromCodePoint(cp)));
    },

    // https://tc39.github.io/ecma262/#sec-literals-numeric-literals
    IsDecimalDigit: function (cp) 
    {
        return (cp >= 0x30 && cp <= 0x39); // 0..9
    },

    IsHexDigit: function (cp) 
    {
        return (cp >= 0x30 && cp <= 0x39) || (cp >= 0x41 && cp <= 0x46) || (cp >= 0x61 && cp <= 0x66); // a..f
    },

    isOctalDigit: function (cp) 
    {
        return (cp >= 0x30 && cp <= 0x37); // 0..7
    }
}

var TOKEN_NAME={};
TOKEN_NAME[1 /* BooleanLiteral */] = 'Boolean';
TOKEN_NAME[2 /* EOF */] = '<end>';
TOKEN_NAME[3 /* Identifier */] = 'Identifier';
TOKEN_NAME[4 /* Keyword */] = 'Keyword';
TOKEN_NAME[5 /* NullLiteral */] = 'Null';
TOKEN_NAME[6 /* NumericLiteral */] = 'Numeric';
TOKEN_NAME[7 /* Punctuator */] = 'Punctuator';
TOKEN_NAME[8 /* StringLiteral */] = 'String';
TOKEN_NAME[9 /* RegularExpression */] = 'RegularExpression';
TOKEN_NAME[10 /* Template */] = 'Template';

//编译异常, 错误类
function ErrorHandler()
{
    this.Error=[];

    this.RecordError=function(error)
    {
        this.Error.push(error);
    }

    this.ConstructError=function(msg,column)
    {
        let error=new Error(msg);
        //通过自己抛异常并自己截获 来获取调用堆栈信息
        try
        {
            throw error;
        }
        catch(base)
        {
            if (Object.create && Object.defineProperties)
            {
                error=Object.create(base);
                error.Column=column;
            }
        }

        return error;
    }

    this.CreateError=function(index, line, col, description)
    {
        let msg='Line ' + line + ': ' + description;
        let error=this.ConstructError(msg,col);
        error.Index=index;
        error.LineNumber=line;
        error.Description=description;
        return error;
    }

    this.ThrowError=function(index, line, col, description)
    {
        let error=this.CreateError(index,line,col,description);
        throw error;
    }

    //重新下载数据
    this.ThrowDownloadJob=function(index, line, col, description,job)
    {
        let error=this.CreateError(index,line,col,description);
        error.Job=job;
        throw error;
    }
}

//扫描类
function Scanner(code, ErrorHandler)
{
    this.Source=code;
    this.ErrorHandler=ErrorHandler;
    this.Length=code.length;
    this.Index=0;
    this.LineNumber=(code.length>0)?1:0;
    this.LineStart=0;
    this.CurlyStack=[];

    this.SaveState=function()   //保存当前扫描状态
    {
        return { Index:this.Index, LineNumber:this.LineNumber, LineStart:this.LineStart };
    }

    this.RestoreState=function(state)   //还原扫描状态
    {
        this.Index=state.Index;
        this.LineNumber=state.LineNumber;
        this.LineStart=state.LineStart;
    }

    this.IsEOF=function()     //否是已经结束
    {
        return this.Index>=this.Length;
    }

    this.IsKeyword=function(id)
    {
        return false;
    }

    this.CodePointAt = function (i) 
    {
        let cp = this.Source.charCodeAt(i);
        if (cp >= 0xD800 && cp <= 0xDBFF) 
        {
            let second = this.Source.charCodeAt(i + 1);
            if (second >= 0xDC00 && second <= 0xDFFF) {
                var first = cp;
                cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
            }
        }
        return cp;
    }

    this.Lex=function()
    {
        if (this.IsEOF()) return { Type:2/*EOF*/, Value:'', LineNumber:this.LineNumber, LineStart:this.LineStart, Start:this.Index, End:this.Index };
        let cp=this.Source.charCodeAt(this.Index);

        //变量名 或 关键字
        if (Character.IsIdentifierStart(cp)) return this.ScanIdentifier();

        //( ) ; 开头 操作符扫描
        if (cp === 0x28 || cp === 0x29 || cp === 0x3B) return this.ScanPunctuator();

        //' " 开头 字符串扫描
        if (cp === 0x27 || cp === 0x22) return this.ScanStringLiteral();

        //. 开头 浮点型
        if (cp==0x2E)
        {
            if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index + 1))) 
                return this.ScanNumericLiteral();

            return this.ScanPunctuator();
        }

        //数字
        if (Character.IsDecimalDigit(cp))  return this.ScanNumericLiteral();
        
        if (cp >= 0xD800 && cp < 0xDFFF)
        {
            if (Character.IsIdentifierStart(this.CodePointAt(this.Index))) return this.ScanIdentifier();
        }

        return this.ScanPunctuator();

    }

    //关键字 变量名 https://tc39.github.io/ecma262/#sec-names-and-keywords
    this.ScanIdentifier=function()
    {
        let type;
        let start=this.Index;
        //0x5C 反斜杠
        let id=(this.Source.charCodeAt(start)=== 0x5C) ? this.GetComplexIdentifier() : this.GetIdentifier();

        if (id.length) type=3;                      //Identifier
        else if (this.IsKeyword(id)) type=4;        //Keyword
        else if (id==null) type=5;                  //NullLiteral
        else if (id=='true' || id=='false') type=1; //BooleanLiteral
        else type=3;                                //Identifier

        if (type!=3  && (start+id.length!=this.Index))
        {
            let restore=this.Index;
            this.Index=start;
            throw Messages.InvalidEscapedReservedWord;
            this.Index=restore;
        }

        if (id=='AND' || id=='OR') type=7 /*Punctuator*/;

        return { Type:type, Value:id, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index};
    }

    this.GetIdentifier=function()
    {
        let start=this.Index++; //start 保存进来的位置
        while(!this.IsEOF())
        {
            let ch=this.Source.charCodeAt(this.Index);
            if (ch==0x5C) 
            {
                this.Index=start;
                return this.GetComplexIdentifier();
            }
            else if (ch >= 0xD800 && ch < 0xDFFF)
            {
                this.Index=start;
                return this.GetComplexIdentifier();
            }

            if (Character.IsIdentifierPart(ch)) ++this.Index;
            else break;
        }

        return this.Source.slice(start,this.Index);
    }

    //操作符 https://tc39.github.io/ecma262/#sec-punctuators
    this.ScanPunctuator=function()
    {
        let start=this.Index;
        let str=this.Source[this.Index];
        switch(str)
        {
            case '(':
                ++this.Index;
                break;
            case ')':
            case ';':
            case ',':
                ++this.Index;
                break;
            default:
                str=this.Source.substr(this.Index,3);
                if (str=='AND') 
                {
                    this.Index+=3;
                }
                else
                {
                    str = this.Source.substr(this.Index, 2);
                    if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '<=' || str === '>=' || str === '=>' || str==':=' || str=='OR' || str=='<>')
                    {
                        this.Index += 2;
                    }
                    else 
                    {
                        str=this.Source[this.Index];
                        if ('<>=!+-*%&|^/:'.indexOf(str) >= 0) ++this.Index;
                    }
                }
        }

        if (this.Index==start) 
           this.ThrowUnecpectedToken();

        return { Type:7/*Punctuator*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index };
    }

    //字符串 https://tc39.github.io/ecma262/#sec-literals-string-literals
    this.ScanStringLiteral=function()
    {
        let start=this.Index;
        let quote=this.Source[this.Index];

        ++this.Index;
        var octal=false;
        let str='';
        while(!this.IsEOF())
        {
            let ch=this.Source[this.Index++];
            if (ch==quote) 
            {
                quote='';
                break;
            }
            else if (ch=='\\')  //字符串转义
            {
                throw "not complete";
            }
            else if (Character.IsLineTerminator(ch.charCodeAt(0))) 
            {
                break;
            }
            else
            {
                str+=ch;
            }
        }

        if (quote!='')
        {
            this.Index=start;
            this.ThrowUnecpectedToken();
        }

        return {Type:8/*StringLiteral*/, Value:str, LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index};
    }

    this.ScanNumericLiteral=function()
    {
        let start=this.Index;
        let ch=this.Source[this.Index];
        let num='';
        if (ch!='.')
        {
            num=this.Source[this.Index++];
            ch=this.Source[this.Index];
            // Hex number starts with '0x'. 16进制
            if (num=='0')
            {
                if (ch=='x' || ch=='X')
                {
                    ++this.Index;
                    return this.ScanHexLiteral(start);
                }
            }

            while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
            {
                num+=this.Source[this.Index++];
            }
            
            ch=this.Source[this.Index];
        }

        if (ch=='.')
        {
            num+=this.Source[this.Index++];
            while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
            {
                num+=this.Source[this.Index++];
            }
            ch=this.Source[this.Index];
        }

        //科学计数法
        if (ch=='e' || ch=='E')
        {
            num+=this.Source[this.Index++];
            ch=this.Source[this.Index];
            if (ch=='+' || ch=='-') num+=this.Source[this.Index];
            if (Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
            {
                while(Character.IsDecimalDigit(this.Source.charCodeAt(this.Index)))
                {
                    num+=this.Source[this.Index++];
                }
            }
            else
            {
                this.ThrowUnecpectedToken();
            }
        }

        if (Character.IsIdentifierStart(this.Source.charCodeAt(this.Index)))
        {
            this.ThrowUnecpectedToken();
        }

        return { Type:6/*NumericLiteral*/, Value:parseFloat(num), LineNumber:this.LineNumber, LineStart:this.LineStart, Start:start, End:this.Index };
    }

    //空格 或 注释
    this.ScanComments=function()
    {
        let comments;
        let start=(this.Index==0);
        while(!this.IsEOF())
        {
            let ch=this.Source.charCodeAt(this.Index);
            if (Character.IsWhiteSpace(ch)) //过滤掉空格
            {
                ++this.Index;
            }
            else if (Character.IsLineTerminator(ch))
            {
                ++this.Index;
                if (ch==0x0D && this.Source.charCodeAt(this.Index)==0x0A) ++this.Index; //回车+换行
                ++this.LineNumber;
                this.LineStart=this.Index;
                start=true;
            }
            else if (ch==0x2F)      // //注释
            {
                ch=this.Source.charCodeAt(this.Index+1);
                if (ch==0x2F)
                {
                    this.Index+=2;
                    let comment=this.SkipSingleLineComment(2);
                    start=true;
                }
                else
                {
                    break;
                }
            }
            else if (ch==0x7B)      //{ }  注释
            {
                this.Index+=1;
                let comment = this.SkipMultiLineComment();
            }
            else 
            {
                break;
            }
        }

        return comments;
    }

    this.SkipMultiLineComment=function()
    {
        var comments = [];
        while(!this.IsEOF())
        {
            var ch=this.Source.charCodeAt(this.Index);
            if (Character.IsLineTerminator(ch))
            {
                ++this.LineNumber;
                ++this.Index;
                this.LineStart=this.Index;
            }
            else if (ch==0x7D)
            {
                this.Index+=1;
                return comments;
            }
            else
            {
                ++this.Index;
            }
        }

        return comments;
    }

    //单行注释 https://tc39.github.io/ecma262/#sec-comments
    this.SkipSingleLineComment=function(offset)
    {
        let comments=[];
        while(!this.IsEOF())
        {
            let ch=this.Source.charCodeAt(this.Index);
            ++this.Index;
            if (Character.IsLineTerminator(ch))
            {
                if (ch === 13 && this.Source.charCodeAt(this.Index) === 10) 
                    ++this.Index;

                ++this.LineNumber;
                this.LineStart=this.Index;
                return comments;
            }
        }

        return comments;
    }

    this.ThrowUnecpectedToken=function(message)
    {
        if (!message) message = Messages.UnexpectedTokenIllegal;
	    return this.ErrorHandler.ThrowError(this.Index, this.LineNumber, this.Index - this.LineStart + 1, message);
    }

}

function Tokenizer(code)
{
    this.ErrorHandler=new ErrorHandler();   //错误信息处理类
    this.Scanner=new Scanner(code,this.ErrorHandler);
    this.Buffer=[];

    this.GetNextToken=function()
    {
        if (this.Buffer.length==0)
        {
            let comments=this.Scanner.ScanComments();
            if (!this.Scanner.IsEOF())
            {
                let token=this.Scanner.Lex();

                let entry={ Type:TOKEN_NAME[token.Type], Value:this.Scanner.Source.slice(token.Start, token.End)};

                this.Buffer.push(entry);
            }
        }

        return this.Buffer.shift();
    }
}

var Syntax = {
    AssignmentExpression: 'AssignmentExpression',
    AssignmentPattern: 'AssignmentPattern',
    ArrayExpression: 'ArrayExpression',
    ArrayPattern: 'ArrayPattern',
    ArrowFunctionExpression: 'ArrowFunctionExpression',
    AwaitExpression: 'AwaitExpression',
    BlockStatement: 'BlockStatement',
    BinaryExpression: 'BinaryExpression',
    BreakStatement: 'BreakStatement',
    CallExpression: 'CallExpression',
    CatchClause: 'CatchClause',
    ClassBody: 'ClassBody',
    ClassDeclaration: 'ClassDeclaration',
    ClassExpression: 'ClassExpression',
    ConditionalExpression: 'ConditionalExpression',
    ContinueStatement: 'ContinueStatement',
    DoWhileStatement: 'DoWhileStatement',
    DebuggerStatement: 'DebuggerStatement',
    EmptyStatement: 'EmptyStatement',
    ExportAllDeclaration: 'ExportAllDeclaration',
    ExportDefaultDeclaration: 'ExportDefaultDeclaration',
    ExportNamedDeclaration: 'ExportNamedDeclaration',
    ExportSpecifier: 'ExportSpecifier',
    ExpressionStatement: 'ExpressionStatement',
    ForStatement: 'ForStatement',
    ForOfStatement: 'ForOfStatement',
    ForInStatement: 'ForInStatement',
    FunctionDeclaration: 'FunctionDeclaration',
    FunctionExpression: 'FunctionExpression',
    Identifier: 'Identifier',
    IfStatement: 'IfStatement',
    ImportDeclaration: 'ImportDeclaration',
    ImportDefaultSpecifier: 'ImportDefaultSpecifier',
    ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
    ImportSpecifier: 'ImportSpecifier',
    Literal: 'Literal',
    LabeledStatement: 'LabeledStatement',
    LogicalExpression: 'LogicalExpression',
    MemberExpression: 'MemberExpression',
    MetaProperty: 'MetaProperty',
    MethodDefinition: 'MethodDefinition',
    NewExpression: 'NewExpression',
    ObjectExpression: 'ObjectExpression',
    ObjectPattern: 'ObjectPattern',
    Program: 'Program',
    Property: 'Property',
    RestElement: 'RestElement',
    ReturnStatement: 'ReturnStatement',
    SequenceExpression: 'SequenceExpression',
    SpreadElement: 'SpreadElement',
    Super: 'Super',
    SwitchCase: 'SwitchCase',
    SwitchStatement: 'SwitchStatement',
    TaggedTemplateExpression: 'TaggedTemplateExpression',
    TemplateElement: 'TemplateElement',
    TemplateLiteral: 'TemplateLiteral',
    ThisExpression: 'ThisExpression',
    ThrowStatement: 'ThrowStatement',
    TryStatement: 'TryStatement',
    UnaryExpression: 'UnaryExpression',
    UpdateExpression: 'UpdateExpression',
    VariableDeclaration: 'VariableDeclaration',
    VariableDeclarator: 'VariableDeclarator',
    WhileStatement: 'WhileStatement',
    WithStatement: 'WithStatement',
    YieldExpression: 'YieldExpression'
};


function Node()
{
    this.IsNeedIndexData=false;         //是否需要大盘数据
    this.IsNeedLatestIndexData=false;   //是否需要下载最新大盘数据
    this.IsNeedLatestData=false;        //是否需要最新的个股行情数据
    this.IsNeedSymbolData=false;        //是否需要下载股票数据
    this.IsNeedSymbolDayData=false;     //跨周期是否下载日线数据
    this.IsNeedFinanceData=new Set();   //需要下载的财务数据
    this.IsNeedMarginData=new Set();
    this.IsNeedNewsAnalysisData=new Set();      //新闻统计数据
    this.IsNeedBlockIncreaseData=new Set();     //是否需要市场涨跌股票数据统计
    this.IsNeedSymbolExData=new Set();          //下载股票行情的其他数据
    this.IsNeedHK2SHSZData=new Set();           //下载北上资金数据
    this.IsNeedSectionFinance=new Map();        //下载截面财务数据 { key= 报告期 , Set() 字段}

    this.IsCustomFunction=[];    //自定义函数 {Name, ID, Args:}    
    this.IsCustomVariant=[];     //自定义变量 {Name, ID}

    this.GetDataJobList=function()  //下载数据任务列表
    {
        let jobs=[];
        if (this.IsNeedSymbolData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA});
        if (this.IsNeedIndexData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA});
        if (this.IsNeedLatestData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA});
        if (this.IsNeedSymbolDayData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DAY_DATA});
        if (this.IsNeedLatestIndexData) jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_LATEST_INDEX_DATA});

        //涨跌停家数统计
        for(var blockSymbol of this.IsNeedBlockIncreaseData)    
        {
            jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA,Symbol:blockSymbol});
        }

        //加载财务数据
        for(var jobID of this.IsNeedFinanceData)
        {
            if (jobID==JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA)      //股息率 需要总市值
                jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA}); 

            jobs.push({ID:jobID});
        }

        //加载融资融券
        for(var jobID of this.IsNeedMarginData)
        {
            jobs.push({ID:jobID});
        }

        //加载北上资金
        for(var jobID of this.IsNeedHK2SHSZData)
        {
            jobs.push({ID:jobID});
        }

        //加载新闻统计
        for(var jobID of this.IsNeedNewsAnalysisData)
        {
            jobs.push({ID:jobID});
        }

        //行情其他数据
        for(var jobID of this.IsNeedSymbolExData)
        {
            jobs.push({ID:jobID});
        }

        //获取截面数据下载任务
        for(var item of this.IsNeedSectionFinance)
        {
            jobs.push({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_SF, SF:item});
        }

        for(var i in this.IsCustomVariant)
        {
            var item=this.IsCustomVariant[i];
            jobs.push({ID:item.ID, Name:item.Name});
        }

        for(var i in this.IsCustomFunction)
        {
            var item=this.IsCustomFunction[i];
            jobs.push({ID:item.ID, Name:item.Name, Args:item.Args});
        }

        return jobs;
    }

    this.VerifySymbolVariable=function(varName)
    {
        let setIndexName=new Set(['INDEXA','INDEXC','INDEXH','INDEXL',"INDEXO","INDEXV",'INDEXDEC','INDEXADV']);
        if (setIndexName.has(varName)) 
        {
            this.IsNeedIndexData=true;
            return;
        }

        let setSymbolDataName=new Set(['CLOSE','C','VOL','V','OPEN','O','HIGH','H','LOW','L','AMOUNT']);
        if (setSymbolDataName.has(varName)) 
        {
            this.IsNeedSymbolData=true;
            return;
        }

        if (varName==='VOLR')
        {
            if (!this.IsNeedSymbolExData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA))
                this.IsNeedSymbolExData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA);
        }

        if (varName=='HYBLOCK' || varName=='DYBLOCK' || varName=='GNBLOCK')
        {
            if (!this.IsNeedSymbolExData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GROUP_DATA))
                this.IsNeedSymbolExData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GROUP_DATA);
        }

        //流通股本(手)
        if (varName==='CAPITAL')
        {
            if (!this.IsNeedFinanceData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA))
                this.IsNeedFinanceData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA);
        }

        if (varName==='EXCHANGE')
        {
            if (!this.IsNeedFinanceData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA))
                this.IsNeedFinanceData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA);
        }

        if (varName=='FROMOPEN')
        {
            this.IsNeedLatestIndexData=true;
            return;
        }

        if (g_JSComplierResource.IsCustomVariant(varName)) //自定义函数( 不过滤了, 调一次就写一次)
        {
            var item={Name:varName, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA}
            this.IsCustomVariant.push(item);
        }
    }

    this.VerifySymbolFunction=function(callee,args)
    {
        if (callee.Name=='DYNAINFO') 
        {
            this.IsNeedLatestData=true;
            return;
        }

        //财务函数
        if (callee.Name=='FINANCE')
        {
            let jobID=JS_EXECUTE_JOB_ID.GetFinnanceJobID(args[0].Value);
            if (jobID && !this.IsNeedFinanceData.has(jobID))  this.IsNeedFinanceData.add(jobID);
            return;
        }

        if (callee.Name==='MARGIN')
        {
            let jobID=JS_EXECUTE_JOB_ID.GetMarginJobID(args[0].Value);
            if (jobID && !this.IsNeedMarginData.has(jobID))  this.IsNeedMarginData.add(jobID);
            return;
        }

        if (callee.Name==='NEWS')
        {
            let jobID=JS_EXECUTE_JOB_ID.GetNewsAnalysisID(args[0].Value);
            if (jobID && !this.IsNeedNewsAnalysisData.has(jobID))  this.IsNeedNewsAnalysisData.add(jobID);
            return;
        }

        if (callee.Name==='HK2SHSZ')    //北上资金
        {
            let jobID=JS_EXECUTE_JOB_ID.GetHK2SHSZJobID(args[0].Value);
            if (jobID && !this.IsNeedHK2SHSZData.has(jobID)) this.IsNeedHK2SHSZData.add(jobID);
            return;
        }

        if (callee.Name=='COST' || callee.Name=='WINNER')   //筹码都需要换手率
        {
            if (!this.IsNeedFinanceData.has(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA))
                this.IsNeedFinanceData.add(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA);
            return;
        }

        if (callee.Name==='BETA')    //beta需要下载上证指数
        {
            this.IsNeedIndexData=true;
            return;
        }

        if (callee.Name=='UPCOUNT' || callee.Name=='DOWNCOUNT')    //上涨下跌个数
        {
            var blockSymbol=args[0].Value;
            if (!this.IsNeedBlockIncreaseData.has(blockSymbol))  this.IsNeedBlockIncreaseData.add(blockSymbol);
            return;
        }

        if (callee.Name=='COVER_C' || callee.Name=='COVER_O' || callee.Name=='COVER_H' || callee.Name=='COVER_L' || callee.Name=='COVER_A' || callee.Name=='COVER_V')   //跨周期函数
        {
            var periodName=args[0].Value;
            if (periodName=='DAY' || periodName=='WEEK' || periodName=='YEAR' || periodName=='MONTH')
            this.IsNeedSymbolDayData=true;
            return;
        }

        if (callee.Name=='SF')  //Section finance
        {
            let period=JS_EXECUTE_JOB_ID.GetSectionReportPeriod(args[0].Value,args[1].Value); //报告期
            if (!period) return;
            let jobID=JS_EXECUTE_JOB_ID.GetSectionFinanceID(args[2].Value);
            if (!jobID) return;
            var sfkey=period.Year+ '-' + period.Quarter;

            if (!this.IsNeedSectionFinance.has(sfkey)) 
            {
                var finacne={ Period:period, Fields:new Set([jobID]) };
                this.IsNeedSectionFinance.set(sfkey, finacne);
            }
            else
            {
                var finacne=this.IsNeedSectionFinance.get(sfkey);
                if (!finacne.Fields.has(jobID)) finacne.Fields.add(jobID);
            }
            
            return;
        }

        if (g_JSComplierResource.IsCustomFunction(callee.Name)) //自定义函数( 不过滤了, 调一次就写一次)
        {
            var item={Name:callee.Name, ID:JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA, Args:args}
            this.IsCustomFunction.push(item);
        }
    }

    this.ExpressionStatement=function(expression)
    {
        return { Type:Syntax.ExpressionStatement, Expression:expression };
    }

    this.Script=function(body)
    {
        return {Type:Syntax.Program, Body:body, SourceType:'通达信脚本' };
    } 

    this.SequenceExpression=function(expression)
    {
        return {Type:Syntax.SequenceExpression, Expression:expression };
    }

    this.BinaryExpression=function(operator, left, right) 
    {
        let logical = (operator === '||' || operator === '&&' || operator=='AND' || operator=='OR');
        let type = logical ? Syntax.LogicalExpression : Syntax.BinaryExpression;

        return { Type:type, Operator:operator, Left:left, Right:right };
    }

    this.Literal=function(value,raw)
    {
        return { Type:Syntax.Literal, Value:value, Raw:raw };
    }

    this.Identifier=function(name)
    {
        this.VerifySymbolVariable(name);

        return { Type:Syntax.Identifier, Name:name};
    }

    this.AssignmentExpression=function (operator, left, right) 
    {
        return { Type:Syntax.AssignmentExpression, Operator:operator, Left:left, Right:right };
    }

    this.UnaryExpression=function(operator, argument) 
    {
        return { Type:Syntax.UnaryExpression, Operator:operator, Argument:argument, Prefix:true };
    }

    this.EmptyStatement=function() 
    {
        return { Type:Syntax.EmptyStatement };
    }

    this.CallExpression=function(callee, args) 
    {
        this.VerifySymbolFunction(callee, args);

        return { Type:Syntax.CallExpression, Callee:callee, Arguments:args };
    }
}



function JSParser(code)
{
    this.ErrorHandler=new ErrorHandler();
    this.Scanner=new Scanner(code, this.ErrorHandler);
    this.Node=new Node();   //节点创建

    this.LookAhead={Type:2, Value:'', LineNumber:this.Scanner.LineNumber, LineStart:0, Start:0, End:0 };
    this.HasLineTerminator=false;
    this.Context = {
        IsModule: false,
        await: false,
        allowIn: true,
        allowStrictDirective: true,
        allowYield: true,
        FirstCoverInitializedNameError: null,
        IsAssignmentTarget: false,
        IsBindingElement: false,
        InFunctionBody: false,
        inIteration: false,
        inSwitch: false,
        labelSet: {},
        Strict: false
    };

    this.PeratorPrecedence = 
    {
        ')': 0,
        ';': 0,
        ',': 0,
        ']': 0,
        '||': 1,
        'OR':1,
        '&&': 2,
        'AND':2,
        '|': 3,
        '^': 4,
        '&': 5,
        '==': 6,
        '=': 6,
        '!=': 6,
        '<>':6,
        '===': 6,
        '!==': 6,
        '<': 7,
        '>': 7,
        '<=': 7,
        '>=': 7,
        '<<': 8,
        '>>': 8,
        '>>>': 8,
        '+': 9,
        '-': 9,
        '*': 11,
        '/': 11,
        '%': 11
    };

    this.StartMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 };
    this.LastMarker={Index:0, Line: this.Scanner.LineNumber, Column:0 };

    this.Initialize=function()
    {
        this.NextToken();
        this.LastMarker={ Index:this.Scanner.Index, Line:this.Scanner.LineNumber, Column:this.Scanner.Index-this.Scanner.LineStart };
    }
    

    this.CreateNode=function()
    {
        return { Index:this.StartMarker.Index, Line:this.StartMarker.Line, Column:this.StartMarker.Column };
    }

    this.StartNode=function(token, lastLineStart)
    {
        if (lastLineStart==void 0) { lastLineStart=0; }

        let column = token.Start - token.LineStart;
	    let line = token.LineNumber;
        if (column < 0) 
        {
	        column += lastLineStart;
	        line--;
        }
        
	    return { Index: token.Start, Line: line, Column: column };
    }

    this.Match=function(value)
    {
        return this.LookAhead.Type==7 /*Punctuator*/ && this.LookAhead.Value==value;
    }

    this.Expect=function(value)
    {
        let token=this.NextToken();
        if (token.Type!=7 /*Punctuator*/ || token.Value!=value)
            this.ThrowUnexpectedToken(token);
    }

    //是否是赋值操作符
    this.MatchAssign=function()
    {
        if (this.LookAhead.Type!=7 /*Punctuator*/) return false;
        let op=this.LookAhead.Value;

        return op==':' || op==':=';
    }

    this.GetTokenRaw=function(token)
    {
        return this.Scanner.Source.slice(token.Start, token.End);
    }

    this.NextToken=function()
    {
        let token=this.LookAhead;
        this.LastMarker.Index=this.Scanner.Index;
        this.LastMarker.Line=this.Scanner.LineNumber;
        this.LastMarker.Column=this.Scanner.Index-this.Scanner.LineStart;
        this.CollectComments(); //过滤注释 空格

        if (this.Scanner.Index !== this.StartMarker.Index) 
        {
            this.StartMarker.Index = this.Scanner.Index;
            this.StartMarker.Line = this.Scanner.LineNumber;
            this.StartMarker.Column = this.Scanner.Index - this.Scanner.LineStart;
        }

        let next=this.Scanner.Lex();
        this.HasLineTerminator=(token.LineNumber!=next.LineNumber);
        if (next && this.Context.Strict && next.Type==3/*Identifier */)
        {
            //TODO:
        }

        this.LookAhead=next;

        return token;
    }

    this.CollectComments=function()
    {
        this.Scanner.ScanComments();
    }

    this.ParseScript=function()
    {
        let node=this.CreateNode();
        let body=this.ParseDirectivePrologues();
        
        while(this.LookAhead.Type!=2 /*EOF*/)
        {
            body.push(this.ParseStatementListItem())
        }

        return this.Finalize(node,this.Node.Script(body));
    }

    //https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive
    this.ParseDirective=function()
    {
        let token=this.LookAhead;
        let node=this.CreateNode();
        let expr=this.ParseExpression();
    }

    this.ParseDirectivePrologues=function()
    {
        let firstRestricted=null;
        let body=[];
        while(true)
        {
            let token=this.LookAhead;
            if (token.Type!=8 /*StringLiteral*/) break;

            let statement=this.ParseDirective();
            body.push(statement);
        }

        return body;
    }

    // https://tc39.github.io/ecma262/#sec-block
    this.ParseStatementListItem=function()
    {
        let statement;
        this.Context.IsAssignmentTarget=true;
        this.Context.IsBindingElement=true;
        if (this.LookAhead.Type==4 /*Keyword*/)
        {

        }
        else
        {
            statement=this.ParseStatement();
        }

        return statement;
    }

    // https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations
    this.ParseStatement=function()
    {
        let statement;
        switch(this.LookAhead.Type)
        {
            case 1 /* BooleanLiteral */:
            case 5 /* NullLiteral */:
            case 6 /* NumericLiteral */:
            case 8 /* StringLiteral */:
            case 10 /* Template */:
            case 9 /* RegularExpression */:
                statement = this.ParseExpressionStatement();
                break;
            case 7 /* Punctuator */:
                let value = this.LookAhead.Value;
                if (value === '(') statement = this.ParseExpressionStatement();
                else if (value === ';') statement = this.ParseEmptyStatement();
                else statement = this.ParseExpressionStatement();
                break;
            case 3 /* Identifier */:
                statement = this.ParseLabelledStatement();
                break;
            case 4 /* Keyword */:
                break;
            default:
                statement="error";
        }

        return statement;
    }
    
    // https://tc39.github.io/ecma262/#sec-empty-statement
    this.ParseEmptyStatement=function()
    {
        let node=this.CreateNode();
        this.Expect(';');
        return this.Finalize(node, this.Node.EmptyStatement());
    }

    //https://tc39.github.io/ecma262/#sec-labelled-statements
    this.ParseLabelledStatement=function()
    {
        let node=this.CreateNode();
        let expr=this.ParseExpression();
        this.ConsumeSemicolon();
        let statement = new this.Node.ExpressionStatement(expr);

        return this.Finalize(node, statement);
    }

    // https://tc39.github.io/ecma262/#sec-comma-operator
    this.ParseExpression=function()
    {
        let startToken=this.LookAhead;
        let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression);
        if (this.Match(','))
        {
            let expressions=[];
            expressions.push(expr);
            while(this.LookAhead.Type!=2 /*EOF*/)
            {
                if (!this.Match(',')) break;
                this.NextToken();
                expressions.push(this.IsolateCoverGrammar(this.ParseAssignmentExpression));
            }

            expr=this.Finalize(this.StartNode(startToken),this.Node.SequenceExpression(expressions));
        }

        return expr;
    }

    this.ParseAssignmentExpression=function()
    {
        let expr;

        let startToken=this.LookAhead;
        let token=startToken;
        expr=this.ParseConditionalExpression();

        if (this.MatchAssign())
        {
            if (!this.Context.IsAssignmentTarget) 
            {
                let marker=expr.Marker;
                this.ThrowUnexpectedError(marker.Index,marker.Line,marker.Column,Messages.InvalidLHSInAssignment);
            }

            if (!this.Match('=') && !this.Match(':'))
            {
                this.Context.IsAssignmentTarget=false;
                this.Context.IsBindingElement=false;
            }
            else
            {
                this.ReinterpretExpressionAsPattern(expr);
            }

            token=this.NextToken();
            let operator=token.Value;
            let right=this.IsolateCoverGrammar(this.ParseAssignmentExpression);
            expr=this.Finalize(this.StartNode(startToken), this.Node.AssignmentExpression(operator, expr, right));
            this.Context.FirstCoverInitializedNameError=null;
        }

        return expr;
    }

    this.ParseConditionalExpression=function()
    {
        let startToken=this.LookAhead;
        let expr=this.InheritCoverGrammar(this.ParseBinaryExpression);

        return expr;
    }

    this.ParseBinaryExpression=function()
    {
        let startToken=this.LookAhead;
        let expr=this.InheritCoverGrammar(this.ParseExponentiationExpression);
        let token=this.LookAhead;
        var prec=this.BinaryPrecedence(token);
        if (prec>0)
        {
            this.NextToken();
            this.Context.IsAssignmentTarget=false;
            this.Context.IsBindingElement=false;
            let markers=[startToken,this.LookAhead];
            let left=expr;
            let right=this.IsolateCoverGrammar(this.ParseExponentiationExpression);
            let stack=[left,token.Value,right];
            let precedences = [prec];
            while(true)
            {
                prec=this.BinaryPrecedence(this.LookAhead);
                if (prec<=0) break;

                while(stack.length>2 && prec<=precedences[precedences.length-1])
                {
                    right=stack.pop();
                    let operator=stack.pop();
                    precedences.pop();
                    left=stack.pop();
                    markers.pop();
                    let node=this.StartNode(markers[markers.length - 1]);
                    stack.push(this.Finalize(node, this.Node.BinaryExpression(operator, left, right)));
                }

                //Shift
                stack.push(this.NextToken().Value);
                precedences.push(prec);
                markers.push(this.LookAhead);
                stack.push(this.IsolateCoverGrammar(this.ParseExponentiationExpression));
            }

            let i=stack.length-1;
            expr=stack[i];
            let lastMarker=markers.pop();
            while(i>1)
            {
                let marker=markers.pop();
                let lastLineStart=lastMarker && lastMarker.LineStart;
                let node=this.StartNode(marker, lastLineStart);
                let operator=stack[i-1];
                expr=this.Finalize(node, this.Node.BinaryExpression(operator, stack[i - 2], expr));
                i-=2;
                lastMarker=marker;
            }
        }

        return expr;
    }

    this.ParseExponentiationExpression=function()
    {
        let startToken=this.LookAhead;
        let expr=this.InheritCoverGrammar(this.ParseUnaryExpression);

        return expr;
    }

    this.ParseUnaryExpression=function()
    {
        let expr;
        if (this.Match('+') || this.Match('-'))
        {
            let node=this.StartNode(this.LookAhead);
            let token=this.NextToken();
            expr=this.InheritCoverGrammar(this.ParseUnaryExpression);
            expr=this.Finalize(node, this.Node.UnaryExpression(token.Value, expr));
            this.Context.IsAssignmentTarget=false;
            this.Context.IsBindingElement=false;
        }
        else
        {
            expr=this.ParseUpdateExpression();
        }

        return expr;
    }

    // https://tc39.github.io/ecma262/#sec-update-expressions
    this.ParseUpdateExpression=function()
    {
        let expr;
        let startToken=this.LookAhead;
        expr=this.InheritCoverGrammar(this.ParseLeftHandSideExpressionAllowCall);

        return expr;
    }

    this.ParseLeftHandSideExpressionAllowCall=function()
    {
        let startToken=this.LookAhead;
        let expr;
        expr=this.InheritCoverGrammar(this.ParsePrimaryExpression);

        while(true)
        {
            if (this.Match('('))
            {
                this.Context.IsBindingElement=false;
                this.Context.IsAssignmentTarget=false;
                var args=this.ParseArguments(); //解析 调用参数
                expr=this.Finalize(this.StartNode(startToken), this.Node.CallExpression(expr,args));
            }
            else
            {
                break;
            }
        }

        return expr;
    }

    // https://tc39.github.io/ecma262/#sec-left-hand-side-expressions
    this.ParseArguments=function()
    {
        this.Expect('(');
        var args=[];
        if (!this.Match(')'))
        {
            while(true)
            {
                let expr=this.IsolateCoverGrammar(this.ParseAssignmentExpression);
                args.push(expr);

                if (this.Match(')')) break;

                this.ExpectCommaSeparator();

                if (this.Match(')')) break;
            }
        }

        this.Expect(')');
        return args;
    }

    // Quietly expect a comma when in tolerant mode, otherwise delegates to expect().
    this.ExpectCommaSeparator=function()
    {
        this.Expect(',');
    }

    // https://tc39.github.io/ecma262/#sec-primary-expression
    this.ParsePrimaryExpression=function()
    {
        let node=this.CreateNode();
        let expr;
        var token, raw;
        switch(this.LookAhead.Type)
        {
            case 3:/* Identifier */
                expr=this.Finalize(node, this.Node.Identifier(this.NextToken().Value));
                break;
            case 6:/* NumericLiteral */
            case 8:/* StringLiteral */
                this.Context.IsAssignmentTarget=false;
                this.Context.IsBindingElement=false;
                token=this.NextToken();
                raw=this.GetTokenRaw(token);
                expr=this.Finalize(node, this.Node.Literal(token.Value,raw));
                break;
            case 7:/* Punctuator */
                switch(this.LookAhead.Value)
                {
                    case '(':
                        this.Context.IsBindingElement=false;
                        expr=this.InheritCoverGrammar(this.ParseGroupExpression);
                        break;
                    default:
                        expr=this.ThrowUnexpectedToken(this.NextToken())
                }
                break;
            default:
                expr = this.ThrowUnexpectedToken(this.NextToken());
        }

        return expr;
    }

    this.ParseGroupExpression=function()
    {
        let expr;
        this.Expect('(');
        if (this.Match(')'))
        {
            this.NextToken();
        }
        else
        {
            let startToken=this.LookAhead;
            let params=[];
            let arrow=false;
            this.Context.IsBindingElement=true;
            expr=this.InheritCoverGrammar(this.ParseAssignmentExpression);
            if (this.Match(','))
            {
                let expressions=[];
                this.Context.IsAssignmentTarget=false;
                expressions.push(expr);
                while(this.LookAhead.Type!=2 /* EOF */)
                {
                    if (!this.Match(',')) break;

                    this.NextToken();
                    if (this.Match(')'))
                    {

                    }
                }
            }

            if (!arrow)
            {
                this.Expect(')');
                this.Context.IsBindingElement=false;
            }
        }

        return expr;
    }

    // https://tc39.github.io/ecma262/#sec-expression-statement
    this.ParseExpressionStatement=function()
    {
        let node=this.CreateNode();
        let expr=this.ParseExpression();
        this.ConsumeSemicolon();

        return this.Finalize(node,this.Node.ExpressionStatement(expr));
    }

    this.ConsumeSemicolon=function()
    {
        if (this.Match(';')) 
        {
            this.NextToken();
        }
        else if (!this.HasLineTerminator)
        {
            //if (this.LookAhead.Type!=2/*EOF*/ && !this.Match('}'))

            this.LastMarker.Index=this.StartMarker.Index;
            this.LastMarker.Line=this.StartMarker.Line;
            this.LastMarker.Column=this.StartMarker.Column;
        }
    }

    this.ReinterpretExpressionAsPattern=function(expr)
    {
        switch(expr.Type)
        {
            case Syntax.Identifier:
            case Syntax.AssignmentExpression:
                break;
            default:
                break;
        }
    }

    this.Finalize=function(marker,node)
    {
        node.Marker={ Line:marker.Line, Column:marker.Column, Index:marker.Index };
        return node;
    }

    this.BinaryPrecedence = function (token) 
    {
        let op = token.Value;
        let precedence;

        if (token.Type === 7 /* Punctuator */) precedence = this.PeratorPrecedence[op] || 0;
        else precedence = 0;
        
        return precedence;
    };

    this.IsolateCoverGrammar=function(parseFunction)
    {
        let previousIsBindingElement=this.Context.IsBindingElement;
        let previousIsAssignmentTarget=this.Context.IsAssignmentTarget;
        let previousFirstCoverInitializedNameError=this.Context.FirstCoverInitializedNameError;

        this.Context.IsBindingElement=true;
        this.Context.IsAssignmentTarget=true;
        this.Context.FirstCoverInitializedNameError=null;
        let result=parseFunction.call(this);

        if (this.Context.FirstCoverInitializedNameError!=null)
        {
            //错误 this.throwUnexpectedToken(this.context.firstCoverInitializedNameError);
        }

        this.Context.IsBindingElement=previousIsBindingElement;
        this.Context.IsAssignmentTarget=previousIsAssignmentTarget;
        this.Context.FirstCoverInitializedNameError=previousFirstCoverInitializedNameError;

        return result;
    }

    this.InheritCoverGrammar = function (parseFunction) 
    {
        let previousIsBindingElement = this.Context.IsBindingElement;
        let previousIsAssignmentTarget = this.Context.IsAssignmentTarget;
        let previousFirstCoverInitializedNameError = this.Context.FirstCoverInitializedNameError;
        this.Context.IsBindingElement = true;
        this.Context.IsAssignmentTarget = true;
        this.Context.FirstCoverInitializedNameError = null;

        let result = parseFunction.call(this);

        this.Context.IsBindingElement = this.Context.IsBindingElement && previousIsBindingElement;
        this.Context.IsAssignmentTarget = this.Context.IsAssignmentTarget && previousIsAssignmentTarget;
        this.Context.FirstCoverInitializedNameError = previousFirstCoverInitializedNameError || this.Context.FirstCoverInitializedNameError;

        return result;
    };

    this.ThrowUnexpectedToken=function(token,message)
    {
        throw this.UnexpectedTokenError(token,message);
    }

    this.ThrowUnexpectedError=function(index,line,column,message)
    {
        let msg=message || "执行异常";
       
        return this.ErrorHandler.ThrowError(index,line,column,msg);
    }

    this.UnexpectedTokenError=function(token,message)
    {
        let msg=message || Messages.UnexpectedToken;
        let value='ILLEGAL';
        if (token)
        {
            if (!message)
            {

            }
            value=token.Value;
        }

        msg=msg.replace("%0",value);
        if (token && typeof(token.LineNumber)=='number')
        {
            let index=token.Start;
            let line=token.LineNumber;
            let lastMarkerLineStart=this.LastMarker.Index-this.LastMarker.Column;
            let column=token.Start-lastMarkerLineStart+1;
            return this.ErrorHandler.CreateError(index,line,column,msg);
        }
        else
        {
            let index=this.LastMarker.Index;
            let line=this.LastMarker.Line;
            let column=this.LastMarker.Column+1;
            return this.ErrorHandler.CreateError(index,line,column,msg);
        }
    }
}


/*
    算法类
*/
function JSAlgorithm(errorHandler,symbolData)
{
    this.ErrorHandler=errorHandler;
    this.SymbolData=symbolData; //股票数据

    //相加
    this.Add=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值相加
        if (isNumber && isNumber2) return data+data2;

        //都是数组相加
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( this.IsNumber(data[i]) && this.IsNumber(data2[i]) ) result[i]=data[i]+data2[i];
                }
            }

            return result;
        }

        //单数据和数组相加
        let value;
        let aryData;
        if (isNumber) 
        {
            value=data;
            aryData=data2;
        }
        else
        {
            value=data2;
            aryData=data;
        }

        for(let i in aryData)
        {
            result[i]=null;
            if (this.IsNumber(aryData[i]) && this.IsNumber(value)) result[i]=value+aryData[i];
        }

        return result;
    }

    //相减
    this.Subtract=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

         //单数值相减
         if (isNumber && isNumber2) return data-data2;

         //都是数组相减
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( this.IsNumber(data[i]) &&  this.IsNumber(data2[i]) ) result[i]=data[i]-data2[i];
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if (this.IsNumber(data) && this.IsNumber(data2[i])) result[i]=data-data2[i];
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if (this.IsNumber(data[i]) && this.IsNumber(data2)) result[i]=data[i]-data2;
            }
        }

        return result;
    }

    //相乘
    this.Multiply=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值相乘
        if (isNumber && isNumber2) return data*data2;

        //都是数组相乘
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( this.IsNumber(data[i]) && this.IsNumber(data2[i]) ) result[i]=data[i]*data2[i];
                }
            }

            return result;
        }

        //单数据和数组相乘
        let value;
        let aryData;
        if (isNumber) 
        {
            value=data;
            aryData=data2;
        }
        else
        {
            value=data2;
            aryData=data;
        }

        for(let i in aryData)
        {
            result[i]=null;
            if (this.IsNumber(aryData[i]) && this.IsNumber(value)) result[i]=value*aryData[i];
        }

        return result;
    }

    //相除
    this.Divide=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值相除
        if (isNumber && isNumber2) 
        {
            if (data2==0) return null;  //除0判断
            return data/data2;
        }

        //都是数组相除
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( this.IsNumber(data[i]) && this.IsDivideNumber(data2[i]) ) result[i]=data[i]/data2[i];
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( this.IsNumber(data) && this.IsDivideNumber(data2[i]) ) result[i]=data/data2[i];
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( this.IsNumber(data[i]) && this.IsDivideNumber(data2) ) result[i]=data[i]/data2;
            }
        }

        return result;

    }

    //大于
    this.GT=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值比较
        if (isNumber && isNumber2) return (data>data2 ? 1 : 0);

        //都是数组比较
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]>data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>data2 ? 1 : 0);
            }
        }

        return result;
    }

    //大于等于
    this.GTE=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值比较
        if (isNumber && isNumber2) return (data>=data2 ? 1 : 0);

        //都是数组比较
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]>=data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data>=data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]>=data2 ? 1 : 0);
            }
        }

        return result;
    }

    //小于
    this.LT=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值比较
        if (isNumber && isNumber2) return (data<data2 ? 1 : 0);

        //都是数组比较
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]<data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data<data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]<data2 ? 1 : 0);
            }
        }

        return result;
    }

    //小于等于
    this.LTE=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值比较
        if (isNumber && isNumber2) return (data>=data2 ? 1 : 0);

        //都是数组比较
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]<=data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data<=data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]<=data2 ? 1 : 0);
            }
        }

        return result;
    }

    //等于
    this.EQ=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值比较
        if (isNumber && isNumber2) return (data==data2 ? 1 : 0);

        //都是数组比较
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]==data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data==data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]==data2 ? 1 : 0);
            }
        }

        return result;
    }

    //不等于
    this.NEQ=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值比较
        if (isNumber && isNumber2) return (data!=data2 ? 1 : 0);

        //都是数组比较
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i]!=data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data!=data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i]!=data2 ? 1 : 0);
            }
        }

        return result;
    }

    //AND  &&
    this.And=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值 &&
        if (isNumber && isNumber2) return (data && data2 ? 1 : 0);

        //都是数组 &&
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i] && data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data && data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i] && data2 ? 1 : 0);
            }
        }

        return result;
    }

    //OR  ||
    this.Or=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值 &&
        if (isNumber && isNumber2) return (data || data2 ? 1 : 0);

        //都是数组 &&
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=(data[i] || data2[i] ? 1:0);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=(data || data2[i] ? 1 : 0);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=(data[i] || data2 ? 1 : 0);
            }
        }

        return result;
    }

    this.IF=function(data,trueData,falseData)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(trueData)=='number';
        let isNumber3=typeof(falseData)=='number';
        
        //单数值
        if (isNumber) 
        {
            if (isNumber2 && isNumber3) return data?trueData:falseData;

            return data? trueData:falseData;
        }
        
        //都是数组
        let result=[];
        for(let i in data)
        {
            if (data[i])
            {
                if (isNumber2) result[i]=trueData;
                else result[i]=trueData[i];
            }
            else
            {
                if (isNumber3) result[i]=falseData;
                else result[i]=falseData[i];
            }
        }

        return result;
    }

    /*
    根据条件求不同的值,同IF判断相反.
    用法: IFN(X,A,B)若X不为0则返回B,否则返回A
    例如: IFN(CLOSE>OPEN,HIGH,LOW)表示该周期收阴则返回最高值,否则返回最低值
    */

    this.IFN=function(data,trueData,falseData)
    {
        return this.IF(data,falseData,trueData);
    }

    //指标函数 函数名全部大写
    this.REF=function(data,n)
    {
        let result=[];
        if (typeof(n)=='number')
        {
            if (data.length<=0) return result;
            if (n>=data.length) return result;

            result=data.slice(0,data.length-n);

            for(let i=0;i<n;++i)
                result.unshift(null);
        }
        else    //n 为数组的情况
        {
            for(let i=0;i<data.length;++i)
            {
                result[i]=null;
                if (i>=n.length) continue;
                var value=n[i];
                if (value>0 && value<=i) result[i]=data[i-value];
                else if (i) result[i]=result[i-1];
                else result[i]=data[i];
            }
        }

        return result;
        
    }

    this.MAX=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值
        if (isNumber && isNumber2) return Math.max(data,data2);

        //都是数组
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=Math.max(data[i], data2[i]);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=Math.max(data, data2[i]);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=Math.max(data[i], data2);
            }
        }

        return result;
    }

    this.MIN=function(data,data2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值
        if (isNumber && isNumber2) return Math.min(data,data2);

        //都是数组
        let result=[];
        if (!isNumber && !isNumber2)
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( !isNaN(data[i]) && !isNaN(data2[i]) ) result[i]=Math.min(data[i], data2[i]);
                }
            }

            return result;
        }

        if (isNumber)   //单数据-数组
        {
            for(let i in data2)
            {
                result[i]=null;
                if ( !isNaN(data) && !isNaN(data2[i]) ) result[i]=Math.min(data, data2[i]);
            }
        }
        else            //数组-单数据
        {
            for(let i in data)
            {
                result[i]=null;
                if ( !isNaN(data[i]) && !isNaN(data2) ) result[i]=Math.min(data[i], data2);
            }
        }

        return result;
    }

    //取正数
    this.ABS=function(data)
    {
        let result=[];

        for(let i in data)
        {
            result[i]=null;
            if (!isNaN(data[i])) result[i]=Math.abs(data[i]);
        }

        return result;
    }

    this.MA=function(data,dayCount)
    {
        let result=[];
        if (!Array.isArray(dayCount))
        {
            if (dayCount<=0) dayCount=1;
            if (!data || !data.length) return result;
            
            for(var i=0;i<data.length;++i)
            {
                result[i]=null;
                if (this.IsNumber(data[i])) break;
            }

            var data=data.slice(0); //复制一份数据出来
            for(var days=0;i<data.length;++i,++days)
            {
                if (days<dayCount-1)
                {
                    result[i]=null;
                    continue;
                }

                let preValue=data[i-(dayCount-1)];
                let sum=0;
                for(let j=dayCount-1;j>=0;--j)
                {
                    var value=data[i-j];
                    if (!this.IsNumber(value)) 
                    {
                        value=preValue;  //空数据就取上一个数据
                        data[i-j]=value; 
                    }
                    else 
                    {
                        preValue=value;
                    }
                    sum+=value;
                }

                result[i]=sum/dayCount;
            }
        }
        else
        {
            for(var i=0;i<data.length;++i)
            {
                result[i]=null;
                if (i>=dayCount.length) continue;
                var sumCount=dayCount[i];
                if (!this.IsNumber(sumCount)) continue;
                if (sumCount<=0) sumCount=i;    //0计算当前所有的
                
                var sum=0;
                for(var j=i, k=0;j>=0 && k<sumCount;--j,++k)
                {
                    sum+=data[j];
                }

                result[i]=sum/sumCount;
            }
        }

        return result;
    }

    //指数平均数指标 EMA(close,10)
    this.EMA=function(data,dayCount)
    {
        var result = [];

        var offset=0;
        if (offset>=data.length) return result;

        //取首个有效数据
        for(;offset<data.length;++offset)
        {
            if (data[offset]!=null && !isNaN(data[offset]))
                break;
        }

        var p1Index=offset;
        var p2Index=offset+1;

        result[p1Index]=data[p1Index];
        for(var i=offset+1;i<data.length;++i,++p1Index,++p2Index)
        {
            result[p2Index]=((2*data[p2Index]+(dayCount-1)*result[p1Index]))/(dayCount+1);
        }

        return result;
    }

    /* 
        SMA 移动平均
        返回移动平均。
        用法: SMA(X,N,M) X的M日移动平均,M为权重,如Y=(X*M+Y'*(N-M))/N 
    */
    this.SMA=function(data,n,m)
    {
        var result = [];

        var i=0;
        var lastData=null;
        for(;i<data.length; ++i)
        {
            if (data[i]==null || isNaN(data[i])) continue;
            lastData=data[i];
            result[i]=lastData; //第一天的数据
            break;
        }

        for(++i;i<data.length;++i)
        {
            result[i]=(m*data[i]+(n-m)*lastData)/n;
            lastData=result[i];
        }

        return result;
    }

    /*
    求动态移动平均.
    用法: DMA(X,A),求X的动态移动平均.
    算法: 若Y=DMA(X,A)则 Y=A*X+(1-A)*Y',其中Y'表示上一周期Y值,A必须小于1.
    例如:DMA(CLOSE,VOL/CAPITAL)表示求以换手率作平滑因子的平均价
    */
    this.DMA=function(data,data2)
    {
        var result = [];
        if (data.length<0 || data.length!=data2.length) return result;

        var index=0;
        for(;index<data.length;++index)
        {
            if (data[index]!=null && !isNaN(data[index]) && data2[index]!=null && !isNaN(data2[index]))
            {
                result[index]=data[index];
                break;
            }
        }

        for(index=index+1;index<data.length;++index)
        {
            if (data[index]==null || data2[index]==null)
                result[index]=null;
            else
            {
                if (data2[index]<1)
                    result[index]=(data2[index]*data[index])+(1-data2[index])*result[index-1];
                else
                    result[index]= data[index];
            }
        }

        return result;
    }
    /*
    返回加权移动平均
    用法:WMA(X,N):X的N日加权移动平均.
    算法:Yn=(1*X1+2*X2+...+n*Xn)/(1+2+...+n)
     */
    this.WMA=function(data, dayCount)
    {
        let result=[];
        if (!data || !data.length) return result;
        if (dayCount < 1) dayCount = 1;
        var i = 0;
        for(i = 0; i < data.length && !this.IsNumber(data[i]); ++i)
        {
            result[i] = null;
        }
        var data = data.slice(0);
        for(var days=0; i < data.length; ++i,++days)
        {
            if (days < dayCount-1)
            {
                result[i] = null;
                continue;
            }
            var preValue = data[i - (dayCount-1)];
            var sum = 0;
            var count = 0;
            for (var j = dayCount-1; j >= 0; --j)
            {
               var value = data[i-j];
               if (!this.IsNumber(value))
               {
                   value = preValue;
                   data[i-j] = value;
               }
               else
                    preValue = value;

                count += dayCount - j;
                sum += value * (dayCount - j);
            }
            result[i] = sum / count;
        }
        return result;
    }
    /*
    返回平滑移动平均
    用法:MEMA(X,N):X的N日平滑移动平均,如Y=(X+Y'*(N-1))/N
    MEMA(X,N)相当于SMA(X,N,1)
    */
    this.MEMA=function(data, dayCount)
    {
        let result=[];
        if (!data || !data.length) return result;
        var i = 0, j = 0;
        for (j = 0; j < data.length && !this.IsNumber(data[j]); ++j)
        {
            result[j] = null;
        }
        i = j;
        if (dayCount < 1 || i+dayCount >= data.length) return result;
        var sum = 0;
        var data = data.slice(0);
        for (; i < j+dayCount; ++i)
        {
            result[i] = null;
            if (!this.IsNumber(data[i]) && i-1 >= 0)
                data[i] = data[i-1];
            sum += data[i];
        }
        result[i-1] = sum / dayCount;
        for (; i < data.length; ++i)
        {
            if (this.IsNumber(result[i-1]) && this.IsNumber(data[i]))
                result[i] = (data[i]+result[i-1]*(dayCount-1)) / dayCount;
            else if (i-1 > -1 && this.IsNumber(result[i-1]))
                result[i] = result[i-1];
            else
                result[i] = null;    
        }
        return result;
    }
    /*
    加权移动平均
    返回加权移动平均
    用法:EXPMA(X,M):X的M日加权移动平均
    EXPMA[i]=buffer[i]*para+(1-para)*EXPMA[i-1] para=2/(1+__para)
    */
    this.EXPMA=function(data,dayCount)
    {
        let result=[];
        if (dayCount>=data.length) return result;
    
        let i=dayCount;
        for(;i<data.length;++i) //获取第1个有效数据
        {
            if (data[i]!=null)
            {
                result[i]=data[i];
                break;
            }
        }
    
        for (i=i+1; i < data.length; ++i)
        {
            if (result[i-1]!=null && data[i]!=null)
                result[i]=(2*data[i]+(dayCount-1)*result[i-1])/(dayCount+1);
            else if (result[i-1]!=null)
                result[i]=result[i-1];
        }
    
        return result;
    }

    //加权平滑平均,MEMA[i]=SMA[i]*para+(1-para)*SMA[i-1] para=2/(1+__para)
    this.EXPMEMA=function(data,dayCount)
    {
        var result=[];
        if (dayCount>=data.length) return result;

        var index=0;
        for(;index<data.length;++index)
        {
            if (data[index] && !isNaN(data[index])) break;
        }

        var sum=0;
        for(var i=0; index<data.length && i<dayCount;++i, ++index)
        {
            if (data[index] && !isNaN(data[index]))
                sum+=data[index];
            else
                sum+=data[index-1];
        }

        result[index-1]=sum/dayCount;
        for(;index<data.length;++index)
        {
            if(result[index-1]!=null && data[index]!=null)
                result[index]=(2*data[index]+(dayCount-1)*result[index-1])/(dayCount+1);
            else if(result[index-1]!=null)
                result[index] = result[index-1];
        }

        return result;
    }
    /*
    向前累加到指定值到现在的周期数.
    用法:SUMBARS(X,A):将X向前累加直到大于等于A,返回这个区间的周期数
    例如:SUMBARS(VOL,CAPITAL)求完全换手到现在的周期数
     */
    this.SUMBARS=function(data, data2)
    {
        var result = [];
        if (!data || !data.length || !data2 || !data2.length) return result;
        var start = 0, i = 0, j = 0;
        for(; start < data.length && !this.IsNumber(data[start]); ++start)
        {
            result[start] = null;
        }
        
        for (i = data.length-1; i >= start; --i)
        {
            var total = 0;
            for (j = i, total = 0; j >= start && total < data2[i]; --j)
                total += data[j];
            if (j < start) result[i] = null;
            else result[i] = i - j;
        }
        for(i = start+1; i < data.length; ++i)
        {
            if (result[i]==null)
                result[i] = result[i-1];
        }
        return result;
    }
    /*
    求相反数.
    用法:REVERSE(X)返回-X.
    例如:REVERSE(CLOSE)返回-CLOSE
     */
    this.REVERSE=function(data)
    {
        var result = [];
        var i = 0;
        for (; i<data.length && !this.isNumber(data[i]); ++i)
        {
            result[i] = null;
        }
        for (; i < data.length; ++i)
        {
            if (!this.isNumber(data[i]))
                result[i] = null;
            else
                result[i] = 0-data[i];
        }
        return result;
    }
    this.COUNT=function(data,n)
    {
        let result=[];

        for(let i=0; i<data.length; ++i)
        {
            let count=0;
            for(let j=0;j<n && i-j>=0;++j)
            {
                if (data[i-j]) ++count;
            }

            result[i]=count;
        }

        return result;
    }

    /*
    HHV 最高值
    求最高值。
    用法: HHV(X,N) 求N周期内X最高值,N=0则从第一个有效值开始。
    例如: HHV(HIGH,30) 表示求30日最高价。
    */
    this.HHV=function(data,n)
    {
        let result = [];
        if (Array.isArray(n))
        {
            var max=null;
            for(var i=0,j=0;i<data.length;++i)
            {
                result[i]=null;
                if (i>=n.length) continue;

                max=null;
                var count=n[i];
                if (count>0 && count<=i)
                {
                    for(j=i-count;j<=i;++j)
                    {
                        if (max==null || max<data[j]) max=data[j];
                    }
                }
                else
                {
                    count=i;
                    for(j=0;j<=i;++j)
                    {
                        if (max==null || max<data[j]) max=data[j];
                    }
                }

                result[i]=max;
            }
        }
        else
        {
            if (n>data.length) return result;
            if (n<=0) n=data.length-1;

            var nMax=0;
            for(nMax=0;nMax<data.length;++nMax)
            {
                if (this.IsNumber(data[nMax])) break;
            }

            if (nMax<data.length) result[nMax]=data[nMax];
            for(var i=nMax+1,j=2;i<data.length && j<n;++i,++j)
            {
                if (data[i]>=data[nMax]) nMax=i;
                result[i]=data[nMax];
            }

            for(;i<data.length;++i)
            {
                if (i-nMax<n) 
                {
                    nMax=data[i]<data[nMax]?nMax:i;
                }
                else
                {
                    for(j=nMax=(i-n+2);j<=i;++j)
                    {
                        nMax=data[j]<data[nMax]?nMax:j;
                    }
                }

                result[i]=data[nMax];
            }
        }

        return result;
    }

    /*
    LLV 最低值
    求最低值。
    用法: LLV(X,N) 求N周期内X最低值,N=0则从第一个有效值开始。
    例如: LLV(LOW,0) 表示求历史最低价。
    */
    this.LLV=function(data,n)
    {
        var result = [];
        if (Array.isArray(n))
        {
            for(var i=0;i<data.length;++i)
            {
                result[i]=null;
                if (i>=n.length) continue;

                var min=null;
                var count=n[i];
                if (count>0 && count<=i)
                {
                    for(var j=i-count;j<=i;++j)
                    {
                        if (min==null || min>data[j]) min=data[j];
                    }
                }
                else
                {
                    count=i;
                    for(var j=0;j<=i;++j)
                    {
                        if (min==null || min>data[j]) min=data[j];
                    }
                }

                result[i]=min;
            }
        }
        else
        {
            if (n>data.length) return result;
            if (n<=0) n=data.length-1;

            var min=null;
            for(var i=n;i<data.length;++i,++j)
            {
                if(min==null || i<n+min)    //最小值是否在当前周期里面
                {
                    min=data[i]>data[min]?min:i;
                }
                else
                {
                    for(var j=(min=i-n+1)+1;j<=i;++j)
                    {
                        if(data[j]<data[min])
                            min = j;
                    }
                }
                result[i] = data[min];
            }
        }

        return result;
    }

    this.STD=function(data,n)
    {
        var result=[];

        var total=0;
        var averageData=[]; //平均值
        for(var i=n-1;i<data.length;++i)
        {
            total=0;
            for(var j=0;j<n;++j)
            {
                total+=data[i-j];
            }

            averageData[i]=total/n;
        }

        for(var i=n-1;i<data.length;++i)
        {
            total=0;
            for(var j=0;j<n;++j)
            {
                total+=Math.pow((data[i-j]-averageData[i]),2);
            }

            result[i]=Math.sqrt(total/n);
        }


        return result;
    }

    //平均绝对方差
    this.AVEDEV=function(data,n)
    {
        var result=[];

        var total=0;
        var averageData=[]; //平均值
        for(var i=n-1;i<data.length;++i)
        {
            total=0;
            for(var j=0;j<n;++j)
            {
                total+=data[i-j];
            }

            averageData[i]=total/n;
        }

        for(var i=n-1;i<data.length;++i)
        {
            total=0;
            for(var j=0;j<n;++j)
            {
                total+=Math.abs(data[i-j]-averageData[i]);
            }

            result[i]=total/n;
        }


        return result;
    }

    //上穿
    this.CROSS=function(data,data2)
    {
        var result=[];
        
        if (Array.isArray(data) && Array.isArray(data2))
        {
            if (data.length!=data2.length) return result=[];

            var index=0;
            for(;index<data.length;++index)
            {
                if (this.IsNumber(data[index])  && this.IsNumber(data2[index]))
                    break;
            }

            for(++index;index<data.length;++index)
            {
                result[index]= (data[index]>data2[index] && data[index-1]<data2[index-1]) ? 1:0;
            }
        }
        else if (Array.isArray(data) && typeof(data2)=='number')
        {
            var index=0;
            for(;index<data.length;++index)
            {
                if (this.IsNumber(data[index])) break;
            }

            for(++index;index<data.length;++index)
            {
                result[index]= (data[index]>data2 && data[index-1]<data2) ? 1:0;
            }
        }
        else if (typeof(data)=='number' && Array.isArray(data2))
        {
            var index=0;
            for(;index<data2.length;++index)
            {
                if (this.IsNumber(data2[index])) break;
            }

            for(++index;index<data2.length;++index)
            {
                result[index]= (data2[index]<data && data2[index-1]>data) ? 1:0;
            }
        }

        return result;
    }

    //累乘
    this.MULAR=function(data,n)
    {
        var result=[];
        if(data.length<n) return result;

        var index=n;
        for(;index<data.length;++index)
        {
            if (data[index]!=null && !isNaN(data[index]))
            {
                result[index]=data[index];
                break;
            }
        }

        for(++index;index<data.length;++index)
        {
            result[index]=result[index-1]*data[index];
        }

        return result;
    }

    this.SUM=function(data,n)
    {
        var result=[];

        if (!Array.isArray(n))
        { 
            if (n==0)
            {
                result[0]=data[0];
        
                for (var i=1; i<data.length; ++i)
                {
                    result[i] = result[i-1]+data[i];
                }
            }
            else
            {
                for(var i=n-1,j=0;i<data.length;++i,++j)
                {
                    for(var k=0;k<n;++k)
                    {
                        if (k==0) result[i]=data[k+j];
                        else result[i]+=data[k+j];
                    }
                }
            }
        }
        else
        {
            for(var i=0;i<data.length;++i)
            {
                result[i]=null;
                var totalCount=n[i];
                if (!(totalCount>0)) continue;

                for(var j=i, k=0 ;j>=0 && k<totalCount ;--j,++k)
                {
                    if (k==0) result[i]=data[j];
                    else result[i]+=data[j];
                }
            }
        }
    
        return result;
    }

    /*
        BARSCOUNT 有效数据周期数
        求总的周期数。
        用法: BARSCOUNT(X) 第一个有效数据到当前的天数。
        例如: BARSCOUNT(CLOSE) 对于日线数据取得上市以来总交易日数,对于分笔成交取得当日成交笔数,对于1分钟线取得当日交易分钟数。
    */
    this.BARSCOUNT=function(data)
    {
        let result=[];
        let days=null;
        for(let i in data)
        {
            result[i]=0;
            if (days==null)
            {
                if (!this.IsNumber(data[i])) continue;

                days=0;
            }
                
            result[i]=days;
            ++days;
        }

        return result;
    }

    //DEVSQ 数据偏差平方和
    //DEVSQ(X,N)  返回数据偏差平方和。
    this.DEVSQ=function(data,n)
    {
        var result=[];
        if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        var i = 0, j = 0, k = 0;
        var E = 0, DEV = 0;
        for(i = 0; i < datanum && !this.isNumber(data[i]); ++i)
        {
            result[i] = null;
        }
        if (num < 1 || i+num>datanum) return result;
        for(E=0; i < datanum && j < num; ++i,++j)
            E += data[i]/num;
        if (j == num)
        {
            DEV = 0;
            for(i--; k < num; k++)
                DEV += (data[i-k]-E) * (data[i-k]-E);
            result[i] = DEV;
            i++;
        }
        for(; i < datanum; ++i)
        {
            E += (data[i] - data[i-num]) / num;
            for(DEV=0, k = 0; k < num; ++k)
                DEV += (data[i-k]-E) * (data[i-k]-E);
            result[i] = DEV;
        }
        return result;
    }

    //NOT 取反
    //求逻辑非。
    //用法: NOT(X) 返回非X,即当X=0时返回1,否则返回0。
    //例如: NOT(ISUP) 表示平盘或收阴。
    this.NOT=function(data)
    {
        let isNumber=typeof(data)=='number';
        if (isNumber) return data? 0:1;

        let result=[];
        for(let i in data)
        {
            result[i]=null;
            if (this.IsNumber(data[i])) result[i]=data[i]?0:1;
        }

        return result;
    }

    //FORCAST 线性回归预测值
    //FORCAST(X,N)  返回线性回归预测值。
    this.FORCAST=function(data,n)
    {
        var result=[];
        if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num < 1 || num >= datanum)
            return result;
        var Ex = 0, Ey = 0, Sxy = 0, Sxx = 0, Const, Slope;
        var i, j;
        for(j = 0; j < datanum && !this.IsNumber(data[j]); ++j)
        {
            result[j] = null;
        }
        for(i = j+num-1; i < datanum; ++i)
        {
           Ex = Ey = Sxy = Sxx = 0;
           for(j = 0; j < num && j <= i; ++j)
           {
               Ex += (i - j);
               Ey += data[i - j];
           }
           Ex /= num;
           Ey /= num;
           for(j = 0; j < num && j <= i; ++j)
           {
               Sxy += (i-j-Ex)*(data[i-j]-Ey);
               Sxx += (i-j-Ex)*(i-j-Ex);
           }
           Slope = Sxy / Sxx;
           Const = Ey - Ex*Slope/num;
           result[i] = Slope * num + Const;
        }

        return result;
    }

    //SLOPE 线性回归斜率
    //SLOPE(X,N)  返回线性回归斜率。
    this.SLOPE=function(data,n)
    {
        let result=[];
        if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
        if (n<1 || !data.length) return result;
        if (n>=data.length) return result;

        let start=0;
        for(let i=0;i<data.length;++i,++start)
        {
            result[i]=null;
            if (this.IsNumber(data[i])) break;
        }

        let x,y,xy,xx;
        for(let i=start+n-1;i<data.length;++i)
        {
            result[i]=null;
            x=y=xy=xx=0;
            for(var j=0;j<n && j<=i; ++j)
            {
                x+=(i-j);       //数据索引相加
                y+=data[i-j];   //数据相加
            }

            x=x/n; y=y/n;
            for(j=0;j<n && j<=i; ++j)
            {
                xy+=(i-j-x)*(data[i-j]-y);
                xx+=(i-j-x)*(i-j-x);
            }

            if (xx) result[i]= xy/xx;
            else if (i) result[i]=result[i-1];
        }

        return result;
    }

    //STDP 总体标准差
    //STDP(X,N)  返回总体标准差。
    this.STDP=function(data,n)
    {
        var result=[];
        if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num < 1 || num >= datanum)
            return result;
        var i = 0, j = 0;
        for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i)
        {
            result[i] = null;
        }
        var SigmaPowerX = 0, SigmaX = 0, MidResult;
        for (; i < datanum && j < num; ++i, ++j)
        {
            SigmaPowerX += data[i] * data[i];
            SigmaX += data[i];
        }
        if (j == num)
        {
            MidResult = num*SigmaPowerX - SigmaX*SigmaX;
            result[i-1] = Math.sqrt(MidResult) / num;
        }
        for(; i < datanum; ++i)
        {
            SigmaPowerX += data[i]*data[i] - data[i-num]*data[i-num];
            SigmaX += data[i] - data[i-num];
            MidResult = num*SigmaPowerX - SigmaX*SigmaX;
            result[i] = Math.sqrt(MidResult) / num;
        }

        return result;
    }

    //VAR 估算样本方差
    //VAR(X,N)  返回估算样本方差。
    this.VAR=function(data,n)
    {
        var result=[];
        if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num <= 1 || num >= datanum)
            return result;
        var i, j;
        for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i)
        {
            result[i] = null;
        }
        var SigmaPowerX, SigmaX;
        for (j = 0, i = i+num-1; i < datanum; ++i)
        {
            SigmaPowerX = SigmaX = 0;
            for(j=0; j < num && j <= i; ++j)
            {
                SigmaPowerX += data[i-j] * data[i-j];
                SigmaX += data[i-j];
            }
            result[i] = (num*SigmaPowerX - SigmaX*SigmaX) / num * (num -1);
        }

        return result;
    }

    //VARP 总体样本方差
    //VARP(X,N)  返回总体样本方差 。
    this.VARP=function(data,n)
    {
        var result=[];
        if (typeof(n)!='number') n=parseInt(n); //字符串的转成数值型
        var num = n;
        var datanum = data.length;
        if (num < 1 || num >= datanum)
            return result;
        var i = 0, j = 0;
        for(i = 0; i < datanum && !this.IsNumber(data[i]); ++i)
        {
            result[i] = null;
        }
        var SigmaPowerX = 0, SigmaX = 0;
        for (; i < datanum && j < num; ++i, ++j)
        {
            SigmaPowerX += data[i] * data[i];
            SigmaX += data[i];
        }
        if (j == num)
            result[i-1] = (num*SigmaPowerX - SigmaX*SigmaX) / (num*num);
        for(; i < datanum; ++i)
        {
            SigmaPowerX += data[i]*data[i] - data[i-num]*data[i-num];
            SigmaX += data[i] - data[i-num];
            result[i] = (num*SigmaPowerX - SigmaX*SigmaX) / (num*num);
        }

        return result;
    }

    //RANGE(A,B,C)表示A>B AND A<C;
    this.RANGE=function(data,range,range2)
    {
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(range)=='number';
        let isNumber3=typeof(range2)=='number';

        if (isNumber && isNumber2 && isNumber3)
        {
            if (data>Math.min(range,range2) && data<Math.max(range,range2)) return 1;
            else return 0;
        }

        let result=[];
        let value, rangeValue,rangValue2;
        for(let i=0; i<data.length; ++i)
        {
            result[i]=null;
            value=data[i];
            if (!this.IsNumber(value)) continue;

            if (!isNumber2) 
            {
                if (i>=range.length) continue;

                rangeValue=range[i];
            }
            else 
            {
                rangeValue=range;
            }
            if (!this.IsNumber(rangeValue)) continue;

            if (!isNumber3) 
            {
                if (i>=range2.length) continue;

                rangeValue2=range2[i];
            }
            else 
            {
                rangeValue2=range2;
            }
            if (!this.IsNumber(rangeValue2)) continue;


            result[i]= (value>Math.min(rangeValue,rangeValue2) && value<Math.max(rangeValue,rangeValue2)) ? 1:0;
        }

        return result;
    }

    this.EXIST=function(data,n)
    {
        n=parseInt(n);

        if (typeof(data)=='number') return 0;

        var latestID=null; //最新满足条件的数据索引
        var result=[];
        var value;
        for(let i=0;i<data.length;++i)
        {
            result[i]=null;
            value=data[i];
            if (this.IsNumber(value) && value>0) latestID=i;

            if (latestID!=null && i-latestID<n) result[i]=1;
            else result[i]=0;
        }

        return result;
    }

    this.TFILTER=function(data,data2,n)
    {
        n=parseInt(n);

        var result=[];

        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(range)=='number';

        let count=Math.max(data.length, data2.length);
        for(let i=0;i<count;++i)
        {

        }

        return result;
    }

    /*
    过滤连续出现的信号.
    用法:FILTER(X,N):X满足条件后,将其后N周期内的数据置为0,N为常量.
    例如:
    FILTER(CLOSE>OPEN,5)查找阳线,5天内再次出现的阳线不被记录在内
    */
    this.FILTER=function(data,n)
    {
        var result=[];
        for(let i=0,j=0; i<data.length; ++i)
        {
            if (data[i])
            {
                result[i]=1;
                for(j=0;j<n && j+i+1<data.length;++j)
                {
                    result[j+i+1]=0;
                }
                i+=n;
            }
            else
            {
                result[i]=0;
            }
        }

        return result;
    }

    this.BARSLAST=function(data)
    {
        var result=[];
        if (!data) return result;

        let day=null;
        for(let i=0;i<data.length;++i)
        {
            result[i]=null;

            if (data[i]>0)  day=0;
            else if (day!=null) ++day;

            if (day!=null) result[i]=day;
        }

        return result;
    }

    /*
    N周期内第一个条件成立到当前的周期数.
    用法:
    BARSSINCEN(X,N):N周期内第一次X不为0到现在的天数,N为常量
    例如:
    BARSSINCEN(HIGH>10,10)表示10个周期内股价超过10元时到当前的周期数
    */
    this.BARSSINCEN=function(data,n)
    {
        var result=[];
        var day=null;
        for(let i=0;i<data.length;++i)
        {
            result[i]=null;
            if (day==null)
            {
                if (data[i]) day=0;
            }
            else
            {
                if (data[i]) 
                {
                    if (day+1<n) ++day;
                }
                else 
                {
                    day=null;
                }
            }

            if (day) result[i]=day;
        }

        return result;
    }

    /*
    第一个条件成立到当前的周期数.
    用法:
    BARSSINCE(X):第一次X不为0到现在的天数
    例如:
    BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数
    */
    this.BARSSINCE=function(data)
    {
        var result=[];
        var day=null;

        for(let i=0;i<data.length;++i)
        {
            result[i]=null;
            if (day==null)
            {
                if (data[i]) day=0;
            }
            else
            {
                ++day;
            }

            if (day) result[i]=day;
        }

        return result;
    }

    /*三角函数调用 func 三角函数 
    反正切值. 用法: ATAN(X)返回X的反正切值
    反余弦值. 用法: ACOS(X)返回X的反余弦值
    反正弦值. 用法: ASIN(X)返回X的反正弦值
    余弦值.  用法: COS(X)返回X的余弦值
    正弦值.  用法: SIN(X)返回X的正弦值
    正切值.  用法: TAN(X)返回X的正切值

    求自然对数. 用法: LN(X)以e为底的对数 例如: LN(CLOSE)求收盘价的对数
    求10为底的对数. 用法: LOG(X)取得X的对数 例如: LOG(100)等于2
    指数. 用法: EXP(X)为e的X次幂 例如: EXP(CLOSE)返回e的CLOSE次幂
    开平方. 用法: SQRT(X)为X的平方根 例如: SQRT(CLOSE)收盘价的平方根
    */
    this.Trigonometric=function(data,func)
    {
        if (!Array.isArray(data))
        {
            if (this.IsNumber(data)) return func(data);

            return null;
        }
        else
        {
            var result=[];
            for(let i in data)
            {
                var item=data[i];
                if (this.IsNumber(item)) result[i]=func(item);
                else result[i]=null;
            }

            return result;
        }
    }

    /*
    LAST(X,A,B):持续存在.
    用法:
    LAST(CLOSE>OPEN,10,5) 
    表示从前10日到前5日内一直阳线
    若A为0,表示从第一天开始,B为0,表示到最后日止
    */
    this.LAST=function(data,n,n2)
    {
        var result=[];
        if (n2<=0) n2=data.length-1;
        if (n2>n) return result;

        var day=0;

        for(let i=0,j=0;i<data.length;++i)
        {
            result[i]=0;
            day=0;
            var start=i-n;
            var end=i-n2;
            if (start<0 || end<0) continue;

            for(j=start;j<data.length && j<=end;++j,++day)
            {
                if (!data[j]) break;
            }

            if (day==end-start+1)   //[start,end]
                result[i]=1;
        }

        return result;
    }

    /*
    属于未来函数,之字转向.
    用法: ZIG(K,N),当价格变化量超过N%时转向,K表示0:开盘价,1:最高价,2:最低价,3:收盘价,其余:数组信息
    例如: ZIG(3,5)表示收盘价的5%的ZIG转向
    */
    this.ZIG=function(data,n)
    {
        var hisData=this.SymbolData.Data;
        var result=[];
        if (typeof(data)=='number')
        {
            switch(data)
            {
                case 0:
                    data=hisData.GetOpen();
                    break;
                case 1:
                    data=hisData.GetHigh();
                    break;
                case 2:
                    data=hisData.GetLow();
                    break;
                case 3:
                    data=hisData.GetClose();
                    break;
                default:
                    return result;
            }
        }
        
        var bFirstPoint=false;
        var bSecondPont=false;
        var firstData={}, secondData={}, thridData={};
        var lastData={};
        for(let i in data)
        {
            result[i]=null;
            var item=data[i];
            if (!this.IsNumber(item)) continue;

            if (bFirstPoint==false)
            {
                bFirstPoint=true;
                firstData={ID:parseInt(i), Value:item};  //第1个点
            }
            else if (bFirstPoint==true && bSecondPont==false)
            {
                var temp=(item-firstData.Value)/firstData.Value*100;
                if (temp>n)
                {
                    secondData={ID:parseInt(i), Value:item, Up:true};
                    lastData={ID:parseInt(i), Value:item};
                    bSecondPont=true;
                }
                else if (temp<-n)
                {
                    secondData={ID:parseInt(i), Value:item, Up:false};
                    lastData={ID:parseInt(i), Value:item};
                    bSecondPont=true;
                }
            }
            else if (bFirstPoint==true && bSecondPont==true)
            {
                var temp=(item-lastData.Value)/lastData.Value*100;
                if (secondData.Up==true)    //找下跌的点
                {
                    if (temp<-n)
                    {
                        thridData={ID:parseInt(i), Value:item, Up:false};
                        this.CalculateZIGLine(firstData,secondData,thridData,data,result);
                        lastData={ID:parseInt(i), Value:item};
                    }
                    else
                    {
                        if (item>lastData.Value) lastData={ID:parseInt(i), Value:item};
                    }
                }
                else
                {
                    if (temp>n)
                    {
                        thridData={ID:parseInt(i), Value:item, Up:true};
                        this.CalculateZIGLine(firstData,secondData,thridData,data,result);
                        lastData={ID:parseInt(i), Value:item};
                    }
                    else
                    {
                        if (item<lastData.Value) lastData={ID:parseInt(i), Value:item};
                    }
                }
            }
        }

        //计算最后1组数据
        thridData={ID:data.length-1, Value:data[data.length-1], Up:!secondData.Up};
        this.CalculateZIGLine(firstData,secondData,thridData,data,result);
       
        return result;
    }

    this.JSDraw=null;
    this.CalculateZIGLine=function(firstData,secondData,thridData,data,result)
    {
        if (this.JSDraw==null) this.JSDraw=new JSDraw(this.ErrorHandler);
        var isUp=secondData.Up;
        var findData=firstData;
        if (isUp)
        {
            for(var i=firstData.ID+1;i<thridData.ID;++i)  //查找最高点
            {
                var subItem=data[i];
                if (!this.IsNumber(subItem)) continue;
                if (findData.Value<subItem) findData={ID:i, Value:subItem};
            }
        }
        else
        {
            for(var i=firstData.ID+1;i<thridData.ID;++i)  //查找最低点
            {
                var subItem=data[i];
                if (!this.IsNumber(subItem)) continue;
                if (findData.Value>subItem) findData={ID:i, Value:subItem};
            }
        }

        secondData.Value=findData.Value;
        secondData.ID=findData.ID;

        var lineCache={ Start:{ID:firstData.ID, Value:firstData.Value}, End:{ID:secondData.ID,Value:secondData.Value}}; 
        var lineData=this.JSDraw.CalculateDrawLine(lineCache);//计算2个点的线上 其他点的数值
        for(var i in lineData)
        {
            var lineItem=lineData[i];
            result[lineItem.ID]=lineItem.Value;
        }

        if (thridData.ID==data.length-1)    //最后一组数据
        {
            //最后2个点的数据连成线
            lineCache={ Start:{ID:secondData.ID, Value:secondData.Value}, End:{ID:thridData.ID,Value:thridData.Value} };
            lineData=this.JSDraw.CalculateDrawLine(lineCache);//计算2个点的线上 其他点的数值
            for(var i in lineData)
            {
                var lineItem=lineData[i];
                result[lineItem.ID]=lineItem.Value;
            }
        }
        else
        {
            firstData.ID=secondData.ID;
            firstData.Value=secondData.Value;

            secondData.ID=thridData.ID;
            secondData.Value=thridData.Value;
            secondData.Up=firstData.Value < secondData.Value;
        }
    }

    /*
    属于未来函数,前M个ZIG转向波谷到当前距离.
    用法:
    TROUGHBARS(K,N,M)表示之字转向ZIG(K,N)的前M个波谷到当前的周期数,M必须大于等于1
    例如:
    TROUGHBARS(2,5,2)表示%5最低价ZIG转向的前2个波谷到当前的周期数
    */
    this.TROUGHBARS=function(data,n,n2)
    {
        var zigData=this.ZIG(data,n);   //计算ZIG
        var i=0,result=[];
        for(i=0;i<zigData.length;++i) 
        {
            result[i]=null;
            if (this.IsNumber(zigData[i])) break;
        }

        var trough=[];
        var start=i,j=0;
        for(;i<zigData.length;++i)  //第1个波谷
        {
            if (i+1<zigData.length && i-1>=0 &&  zigData[i]<zigData[i-1] && zigData[i]<zigData[i+1]) //波谷
            {
                trough[0]=i;
                break;
            }
        }

        for(i+=1;i<zigData.length;++i)
        {
            result[i]=null;
            if (i+1<zigData.length && i-1>=0 &&  zigData[i]<zigData[i-1] && zigData[i]<zigData[i+1]) //波谷
            {
                JSConsole.Complier.Log('[TROUGHBARS] i',i,zigData[i]);
                ++j;
                trough[j]=i;
                if (j+1==n2)
                {
                    result[i]=i-start;
                }
                else if (j+1>n2)
                {
                    trough.shift(); //大于计算的波谷数,去掉第1个波谷
                    start=trough[0];
                    --j;
                    result[i]=i-start;
                }
            }
            else
            {
                if (j+1===n2) result[i]=i-start;
            }
        }

        return result;
    }

    /*
    属于未来函数,前M个ZIG转向波峰到当前距离.
    用法:
    PEAKBARS(K,N,M)表示之字转向ZIG(K,N)的前M个波峰到当前的周期数,M必须大于等于1
    例如:
    PEAKBARS(0,5,1)表示%5开盘价ZIG转向的上一个波峰到当前的周期数
    */
    this.PEAKBARS=function(data,n,n2)
    {
        var zigData=this.ZIG(data,n);   //计算ZIG
        var i=0,result=[];
        for(i=0;i<zigData.length;++i) 
        {
            result[i]=null;
            if (this.IsNumber(zigData[i])) break;
        }

        var trough=[];
        var start=i,j=0;
        for(;i<zigData.length;++i)  //第1个波峰
        {
            if (i+1<zigData.length && i-1>=0 &&  zigData[i]>zigData[i-1] && zigData[i]>zigData[i+1]) //波峰
            {
                trough[0]=i;
                break;
            }
        }

        for(i+=1;i<zigData.length;++i)
        {
            result[i]=null;
            if (i+1<zigData.length && i-1>=0 &&  zigData[i]>zigData[i-1] && zigData[i]>zigData[i+1]) //波峰
            {
                JSConsole.Complier.Log('[TROUGHBARS] i',i,zigData[i]);
                ++j;
                trough[j]=i;
                if (j+1==n2)
                {
                    result[i]=i-start;
                }
                else if (j+1>n2)
                {
                    trough.shift(); //大于计算的波谷数,去掉第1个波谷
                    start=trough[0];
                    --j;
                    result[i]=i-start;
                }
            }
            else
            {
                if (j+1===n2) result[i]=i-start;
            }
        }

        return result;
    }

    /*
    一直存在.
    例如:
    EVERY(CLOSE>OPEN,N) 
    表示N日内一直阳线(N应大于0,小于总周期数,N支持变量)
    */
    this.EVERY=function(data,n)
    {
        var result=[];
        if (n<1) return result;
        var i=0;
        for(;i<data.length;++i)
        {
            result[i]=null;
            if (this.IsNumber(data[i])) break;
        }

        var flag=0;
        for(;i<data.length;++i)
        {
            if (data[i]) flag+=1;
            else flag=0;
            
            if (flag==n)
            {
                result[i]=1;
                --flag;
            }
            else 
            {
                result[i]=0;
            }
        }
        return result;
    }

    /*
    成本分布情况.
    用法:
    COST(10),表示10%获利盘的价格是多少,即有10%的持仓量在该价格以下,其余90%在该价格以上,为套牢盘
    该函数仅对日线分析周期有效
    */
    this.COST=function(data)
    {
        var result=[];
        var exchangeID=201;
        var exchangeData=this.SymbolData.GetFinanceCacheData(exchangeID);    //换手率
        if (!exchangeData) return result;

        var isNumber=Array.isArray(data)?false:true;
        var singleData=null;
        if (isNumber) singleData=parseFloat(data);
        var compareData=null;

        for(let i=this.SymbolData.Data.Data.length-1, j=0,k=0 ; i>=0; --i)
        {
            result[i]=null;
            var chipData=this.CalculateChip(i,exchangeData,this.SymbolData.Data.Data,1);
            if (chipData.Max==null || chipData.Min==null || chipData.Max<=0 || chipData.Min<=0) continue;

            var max=parseInt(chipData.Max*100);
            var min=parseInt(chipData.Min*100);
           
            if (singleData!=null) 
            {
                compareData=singleData;
            }
            else 
            {
                if (i>=data.length) continue;
                compareData=data[i];
            }
            
            var totalVol=0,vol=0;
            var aryMap=new Map();
            for(j=i; j>=0; --j)
            {
                var item=chipData.Data[j];
                var start=parseInt(item.Low*100);
                var end=parseInt(item.High*100);
                if ((end-start+1)<=0) continue;

                var iAverageVolume=item.Vol;
                iAverageVolume=iAverageVolume/(end-start+1);
                if (iAverageVolume<=0) continue;

                for(k=start;k<=end && k<=max;++k)
                {
                    if (aryMap.has(k)) 
                    {
                        vol=aryMap.get(k);
                        aryMap.set(k,vol+iAverageVolume);
                    }
                    else
                    {
                        aryMap.set(k,iAverageVolume);
                    }
                }

                totalVol+=item.Vol;
            }

            //计算获利盘
            vol=0;
            for(var priceData of aryMap)
            {
                vol+=priceData[1];
                result[i]=priceData[0]/100;
                if (vol/totalVol*100>compareData)
                    break;
            }
        }

        return result;
    }

    /*
    获利盘比例.
    用法:
    WINNER(CLOSE),表示以当前收市价卖出的获利盘比例,例如返回0.1表示10%获利盘;WINNER(10.5)表示10.5元价格的获利盘比例
    该函数仅对日线分析周期有效
    !!!!计算比较耗时间
    */
    this.WINNER=function(data)
    {
        var result=[];
        var exchangeID=201;
        var exchangeData=this.SymbolData.GetFinanceCacheData(exchangeID);    //换手率
        if (!exchangeData) return result;

        var isNumber=Array.isArray(data)?false:true;
        var singleData=null;
        if (isNumber) singleData=parseInt(parseFloat(data)*100);
        var compareData=null;

        for(let i=this.SymbolData.Data.Data.length-1, j=0,k=0 ; i>=0; --i)
        {
            result[i]=null;
            var chipData=this.CalculateChip(i,exchangeData,this.SymbolData.Data.Data,1);
            if (chipData.Max==null || chipData.Min==null || chipData.Max<=0 || chipData.Min<=0) continue;

            var max=parseInt(chipData.Max*100);
            var min=parseInt(chipData.Min*100);
           
            if (singleData!=null) 
            {
                compareData=singleData;
            }
            else 
            {
                if (i>=data.length) continue;
                compareData=parseInt(data[i]*100);
            }
            
            var totalVol=0,vol=0;
            for(j=i; j>=0; --j)
            {
                var item=chipData.Data[j];
                var start=parseInt(item.Low*100);
                var end=parseInt(item.High*100);
                if ((end-start+1)<=0) continue;

                var iAverageVolume=item.Vol;
                iAverageVolume=iAverageVolume/(end-start+1);
                if (iAverageVolume<=0) continue;

                var profitVol=0;    //获利的成交量
                if (compareData>end) profitVol=item.Vol;
                else if (compareData<start) profitVol=0;
                else profitVol=item.Vol*(compareData-start+1)/(end-start+1);

                vol+=profitVol;
                totalVol+=item.Vol;
            }

            if (totalVol>0) result[i]=vol/totalVol;
        }

        return result;
    }

    //计算截至到某一天的历史所有筹码
    this.CalculateChip=function(index,exchangeData,hisData,dRate)
    {
        var result={Min:null,Max:null,Data:[]};
        var seed=1;//筹码历史衰减换手系数
        var max=null, min=null;
        for(let i=index; i>=0; --i)
        {
            let item={};    //Vol:量 High:最高 Low:最低
            var kData=hisData[i];
            if (i==index) item.Vol=kData.Vol*exchangeData[i];
            else item.Vol=kData.Vol*seed;

            item.Date=kData.Date;
            item.High=kData.High;
            item.Low=kData.Low;

            if (max==null) max=item.High;
            else if (max<item.High) max=item.High;
            if (min==null) min=item.Low;
            else if (min<item.Low) min=item.Low;

            result.Data[i]=item;

            seed*=(1-(exchangeData[i]/100)*dRate);	//换手率累乘
        }

        result.Max=max;
        result.Min=min;

        return result;
    }

    /*
    返回是否连涨周期数.
    用法:
    UPNDAY(CLOSE,M)
    表示连涨M个周期,M为常量
    */
    this.UPNDAY=function(data,n)
    {
        var result=[];
        if (n<1) return result;
        if (data==null || n>data.length) return result;

        var days=0;
        for(let i=0;i<data.length;++i)
        {
            result[i]=0;
            if (i-1<0) continue;
            if (!this.IsNumber(data[i]) || !this.IsNumber(data[i-1])) //无效数都不算连涨
            {
                days=0;
                continue;
            }

            if (data[i]>data[i-1]) ++days;
            else days=0;

            if (days==n)
            {
                result[i]=1;
                --days;
            }
        }

        return result;
    }

    /*
    返回是否连跌周期.
    用法:
    DOWNNDAY(CLOSE,M)
    表示连跌M个周期,M为常量
    */
    this.DOWNNDAY=function(data,n)
    {
        var result=[];
        if (n<1) return result;
        if (data==null || n>data.length) return result;

        var days=0;
        for(let i=0;i<data.length;++i)
        {
            result[i]=0;
            if (i-1<0) continue;
            if (!this.IsNumber(data[i]) || !this.IsNumber(data[i-1])) //无效数都不算连涨
            {
                days=0;
                continue;
            }

            if (data[i]<data[i-1]) ++days;
            else days=0;

            if (days==n)
            {
                result[i]=1;
                --days;
            }
        }

        return result;
    }

    /*
    返回是否持续存在X>Y
    用法:
    NDAY(CLOSE,OPEN,3)
    表示连续3日收阳线
    */
    this.NDAY=function(data,data2,n)
    {
        var result=[];
        if (n<1) return result;
        if (!Array.isArray(data) && !Array.isArray(data2)) return result; 
        if (data==null || data2==null ) return result;

        if (Array.isArray(data) && Array.isArray(data2))
        {
            if (n>=data.length || n>=data2.length) return result;
            var count=Math.max(data.length,data2.length);
            var days=0;
            for(let i=0;i<count;++i)
            {
                result[i]=0;
                if (i>=data.length || i>=data2.length) continue;
                if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i]))
                {
                    days=0;
                    continue;
                }

                if (data[i]>data2[i]) ++days;
                else days=0;

                if (days==n)
                {
                    result[i]=1;
                    --days;
                }
            }
        }
        else if (Array.isArray(data) && !Array.isArray(data2))
        {
            if (n>=data.length || !this.IsNumber(data2)) return;
            var days=0;
            for(let i in data)
            {
                result[i]=0;
                if (!this.IsNumber(data[i])) 
                {
                    days=0;
                    continue;
                }

                if (data[i]>data2) ++days;
                else days=0;

                if (days==n)
                {
                    result[i]=1;
                    --days;
                }
            }
        }
        else if (!Array.isArray(data) && Array.isArray(data2))
        {
            if (n>=data2.length || !this.IsNumber(data)) return;
            var days=0;
            for(let i in data2)
            {
                result[i]=0;
                if (!this.IsNumber(data2[i])) 
                {
                    days=0;
                    continue;
                }

                if (data>data2[i]) ++days;
                else days=0;

                if (days==n)
                {
                    result[i]=1;
                    --days;
                }
            }
        }

        return result;
    }

    /*
    两条线维持一定周期后交叉.
    用法:LONGCROSS(A,B,N)表示A在N周期内都小于B,本周期从下方向上穿过B时返回1,否则返回0
    */
    this.LONGCROSS=function(data,data2,n)
    {
        var result=[];
        var count=Math.max(data.length,data2.length);
        for(let i=0;i<count;++i)
        {
            result[i]=0;
            if (i-1<0) continue;
            if (i>=data.length || i>=data2.length) continue;
            if (!this.IsNumber(data[i]) || !this.IsNumber(data2[i]) || !this.IsNumber(data[i-1]) || !this.IsNumber(data2[i-1])) continue;

            if (data[i]>data2[i] && data[i-1]<data2[i-1]) result[i]=1;
        }

        for(let i=0,j=0;i<count;++i)
        {
            if (!result[i]) continue;

            for(j=1;j<=n && i-j>=0;++j)
            {
                if (data[i-j]>=data2[i-j]) 
                {
                    result[i]=0;
                    break;
                }
            }
        }

        return result;
    }

    /*
    EXISTR(X,A,B):是否存在(前几日到前几日间).
    例如: EXISTR(CLOSE>OPEN,10,5) 
    表示从前10日内到前5日内存在着阳线
    若A为0,表示从第一天开始,B为0,表示到最后日止
    */
    this.EXISTR=function(data,n,n2)
    {
        var result=[];
        if (!Array.isArray(data)) return result;

        n=parseInt(n);
        n2=parseInt(n2);
        if (n<=0) n=data.length;
        if (n2<=0) n2=1;
        if (n2>n) return result;

        var result=[];
        var value;
        for(let i=0,j=0;i<data.length;++i)
        {
            result[i]=null;
            if (i-n<0 || i-n2<0) continue;

            result[i]=0;
            for(j=n;j>=n2;--j)
            {
                var value=data[i-j];
                if (this.IsNumber(value) && value)
                {
                    result[i]=1;
                    break;
                }
            }
        }

        return result;
    }

    /*
    RELATE(X,Y,N) 返回X和Y的N周期的相关系数
    RELATE(X,Y,N)=(∑[(Xi-Avg(X))(Yi-Avg(y))])/N ÷ √((∑(Xi-Avg(X))^2)/N * (∑(Yi-Avg(Y))^2)/N)
    其中 avg(x)表示x的N周期均值:  avg(X) = (∑Xi)/N  
    √(...)表示开平方
    */
    this.RELATE=function(data,data2,n)
    {
        var result=[];
        if (n<1) n=1;

        if (!Array.isArray(data)|| !Array.isArray(data2)) return result;

        var dataAverage=this.CalculateAverage(data,n);
        var data2Average=this.CalculateAverage(data2,n);

        var count=Math.max(data.length,data2.length);
        for(let i=0,j=0;i<count;++i)
        {
            result[i]=null;

            if (i>=data.length || i>=data2.length || i>=dataAverage.length || i>=data2Average.length) continue;

            var average=dataAverage[i];
            var average2=data2Average[i];

            var total=0,total2=0,total3=0;
            for(j=i-n+1;j<=i;++j)
            {
                total+=(data[j]-average)*(data2[j]-average2);   //∑[(Xi-Avg(X))(Yi-Avg(y))])
                total2+=Math.pow(data[j]-average,2);            //∑(Xi-Avg(X))^2
                total3+=Math.pow(data2[j]-average2,2);          //∑(Yi-Avg(Y))^2)
            }

            result[i]=(total/n)/(Math.sqrt(total2/n)*Math.sqrt(total3/n));
        }

        return result;
    }

    //计算数组n周期内的均值
    this.CalculateAverage=function(data,n)
    {
        var result=[];
        if (n<1) return result;

        var total=0;

        for(var i=0;i<data.length;++i)  //去掉开始的无效数
        {
            if (this.IsNumber(data[i])) break;
        }

        for(;i<data.length && i<n;++i)  //计算第1个周期的数据
        {
            result[i]=null;
            var value=data[i];
            if (!this.IsNumber(value)) continue;
            total+=value;
        }
        result[i-1]=total/n;

        for(;i<data.length;++i)         //计算后面的周期数据
        {
            var value=data[i];
            var preValue=data[i-n];     //上一个周期的第1个数据
            if (!this.IsNumber(value)) value=0;
            if (!this.IsNumber(preValue)) preValue=0;

            total=total-preValue+value; //当前周期的数据 等于上一个周期数据 去掉上一个周期的第1个数据 加上这个周期的最后1个数据
            result[i]=total/n;
        }

        return result;
    }

    /*
    COVAR(X,Y,N) 返回X和Y的N周期的协方差
    */
    this.COVAR=function(data,data2,n)
    {
        var result=[];
        if (n<1) n=1;

        if (!Array.isArray(data)|| !Array.isArray(data2)) return result;

        var dataAverage=this.CalculateAverage(data,n);
        var data2Average=this.CalculateAverage(data2,n);

        var count=Math.max(data.length,data2.length);

        var count=Math.max(data.length,data2.length);
        for(let i=0,j=0;i<count;++i)
        {
            result[i]=null;

            if (i>=data.length || i>=data2.length || i>=dataAverage.length || i>=data2Average.length) continue;

            var average=dataAverage[i];
            var average2=data2Average[i];

            var total=0;
            for(j=i-n+1;j<=i;++j)
            {
                total+=(data[j]-average)*(data2[j]-average2);   
            }

            result[i]=(total/n);
        }


        return result;
    }

    /*
    求上一高点到当前的周期数.
    用法:
    HHVBARS(X,N):求N周期内X最高值到当前周期数,N=0表示从第一个有效值开始统计
    例如:
    HHVBARS(HIGH,0)求得历史新高到到当前的周期数
    */
    this.HHVBARS=function(data,n)
    {
        var result=[];
        if (!Array.isArray(data)) return result;
        if (n<1) n=data.length;

        var nMax=null;  //最大值索引
        for(var i=0;i<data.length;++i)
        {
            result[i]=null;
            if (this.IsNumber(data[i])) 
            {
                nMax=i;
                break;
            }
        }

        var j=0;
        for(i=nMax+1;i<data.length && j<n ;++i,++j) //求第1个最大值
        {
            if (data[i]>=data[nMax]) nMax=i;
            if(n==data.length) result[i]=(i-nMax);
        }

        for(;i<data.length;++i)
        {
            if (i-nMax<n)
            {
                if (data[i]>=data[nMax]) nMax=i;
            }
            else
            {
                nMax=i-n+1;
                for(j=nMax;j<=i;++j)    //计算区间最大值
                {
                    if (data[j]>=data[nMax]) nMax=j;
                }
            }

            result[i]=i-nMax;
        }

        return result;
    }

    /*
    求上一低点到当前的周期数.
    用法: LLVBARS(X,N):求N周期内X最低值到当前周期数,N=0表示从第一个有效值开始统计
    例如: LLVBARS(HIGH,20)求得20日最低点到当前的周期数
    */
    this.LLVBARS=function(data,n)
    {
        var result=[];
        if (!Array.isArray(data)) return result;
        if (n<1) n=data.length;

        var nMin=null;  //最小值索引
        for(var i=0;i<data.length;++i)
        {
            result[i]=null;
            if (this.IsNumber(data[i])) 
            {
                nMin=i;
                break;
            }
        }

        var j=0;
        for(i=nMin+1;i<data.length && j<n ;++i,++j) //求第1个最大值
        {
            if (data[i]<=data[nMin]) nMin=i;
            if(n==data.length) result[i]=(i-nMin);
        }

        for(;i<data.length;++i)
        {
            if (i-nMin<n)
            {
                if (data[i]<=data[nMin]) nMin=i;
            }
            else
            {
                nMin=i-n+1;
                for(j=nMin;j<=i;++j)    //计算区间最小值
                {
                    if (data[j]<=data[nMin]) nMin=j;
                }
            }

            result[i]=i-nMin;
        }

        return result;
    }

    /*
    β(Beta)系数
    BETA(N) 返回当前证券N周期收益与对应大盘指数收益相比的贝塔系数
    需要下载上证指数历史数据
    涨幅(X)=(现价-上一个交易日收盘价)/上一个交易日收盘价
    公式=股票和指数协方差/指数方差
    */
    this.BETA=function(n)
    {
        var result=[];
        var stockData= this.SymbolData.Data;
        var indexData=this.SymbolData.IndexData;
        if (n<=0) n=1;

        var stockProfit=[]; //股票涨幅
        var indexProfit=[]; //指数涨幅

        for(let i=0;i<stockData.Data.length;++i)
        {
            stockProfit[i]=0;
            indexProfit[i]=0;

            var stockItem=stockData.Data[i];
            var indexItem=indexData.Data[i];

            if (stockItem.Close>0 && stockItem.YClose>0) stockProfit[i]=(stockItem.Close-stockItem.YClose)/stockItem.YClose;
            if (indexItem.Close>0 && indexItem.YClose>0) indexProfit[i]=(indexItem.Close-indexItem.YClose)/indexItem.YClose;
        }

        //计算均值数组
        var averageStockProfit=this.CalculateAverage(stockProfit,n);    
        var averageIndexProfit=this.CalculateAverage(indexProfit,n);

        for(var i=0,j=0;i<stockData.Data.length;++i)
        {
            result[i]=null;

            if (i>=stockProfit.length || i>=indexProfit.length || i>=averageStockProfit.length || i>=averageIndexProfit.length) continue;

            var averageStock=averageStockProfit[i];
            var averageIndex=averageIndexProfit[i];

            var covariance=0;   //协方差
            var variance=0;     //方差
            for(j=i-n+1;j<=i;++j)
            {
                var value=(indexProfit[j]-averageIndex);
                var value2=(stockProfit[j]-averageStock);
                covariance+=value*value2; 
                variance+=value*value; 
            }

            if (this.IsDivideNumber(variance) && this.IsNumber(covariance))
                result[i]=covariance/variance;  //(covariance/n)/(variance/n)=covariance/variance;
        }

        return result;
    }

    /*
    用法:BETA2(X,Y,N)为X与Y的N周期相关放大系数,表示Y变化1%,则X将变化N%
    例如:BETA2(CLOSE,INDEXC,10)表示收盘价与大盘指数之间的10周期相关放大率
    */
    this.BETA2=function(x,y,n)
    {
        var result=[];
        if (n<=0) n=1;

        var xProfit=[null]; //x数据的涨幅
        var yProfit=[null]; //y数据的涨幅

        var count=Math.max(x.length,y.length);

        var lastItem={X:x[0], Y:y[0]};
        for(var i=1;i<count;++i)
        {
            xProfit[i]=0;
            yProfit[i]=0;

            var xItem=x[i];
            var yItem=y[i];

            if (lastItem.X>0) xProfit[i]=(xItem-lastItem.X)/lastItem.X;
            if (lastItem.Y>0) yProfit[i]=(yItem-lastItem.Y)/lastItem.Y;

            lastItem={X:xItem, Y:yItem};
        }

        //计算均值数组
        var averageXProfit=this.CalculateAverage(xProfit,n);    
        var averageYProfit=this.CalculateAverage(yProfit,n);

        for(var i=0,j=0;i<count;++i)
        {
            result[i]=null;

            if (i>=xProfit.length || i>=yProfit.length || i>=averageXProfit.length || i>=averageYProfit.length) continue;

            var averageX=averageXProfit[i];
            var averageY=averageYProfit[i];

            var covariance=0;   //协方差
            var variance=0;     //方差
            for(j=i-n+1;j<=i;++j)
            {
                var value=(xProfit[j]-averageX);
                var value2=(yProfit[j]-averageY);
                covariance+=value*value2; 
                variance+=value*value; 
            }

            if (this.IsDivideNumber(variance) && this.IsNumber(covariance))
                result[i]=covariance/variance;  //(covariance/n)/(variance/n)=covariance/variance;
        }

        return result;
    }

    /*
    抛物转向.
    用法:
    SAR(N,S,M),N为计算周期,S为步长,M为极值
    例如:
    SAR(10,2,20)表示计算10日抛物转向,步长为2%,极限值为20%
    */
    this.SAR=function(n,step,exValue)
    {
        var result=[];
        var stockData= this.SymbolData.Data;
        if (n>=stockData.Data.length) return result;

        var high=null,low=null;
        for(var i=0;i<n;++i)
        {
            var item=stockData.Data[i];
            if (high==null) high=item.High;
            else if (high<item.High) high=item.High;
            if (low==null) low=item.Low;
            else if (low>item.Low) low=item.Low;
        }

        const SAR_LONG=0, SAR_SHORT=1;
        var position=SAR_LONG;
        result[n-1]=low;
        var nextSar=low, sip=stockData.Data[0].High,af=step/100;
        for(var i=n;i<stockData.Data.length;++i)
        {
            var ysip=sip;
            var item=stockData.Data[i];
            var yitem=stockData.Data[i-1];

            if (position==SAR_LONG)
            {
                if (item.Low<result[i-1])
                {
                    position=SAR_SHORT;
                    sip=item.Low;
                    af=step/100;
                    nextSar =Math.max(item.High,yitem.High);
				    nextSar =Math.max(nextSar,ysip+af*(sip-ysip));
                }
                else
                {
                    position = SAR_LONG;
                    if(item.High>ysip)
                    {	
                        sip=item.High;
                        af=Math.min(af+step/100,exValue/100);
                    }
                    nextSar=Math.min(item.Low,yitem.Low);
                    nextSar=Math.min(nextSar,result[i-1]+af*(sip-result[i-1]));
                }
            }
            else if (position==SAR_SHORT)
            {
                if(item.High>result[i-1])
                {
                    position=SAR_LONG;
                    sip=item.High;
                    af=step/100;
                    nextSar =Math.min(item.Low,yitem.Low);
                    nextSar =Math.min(nextSar,result[i-1]+af*(sip-ysip));
                }
                else
                {
                    position = SAR_SHORT;
                    if(item.Low<ysip)
                    {	
                        sip=item.Low;
                        af=Math.min(af+step/100,exValue/100);
                    }
                    nextSar=Math.max(item.High,yitem.High);
                    nextSar=Math.max(nextSar,result[i-1]+af*(sip-result[i-1]));
                }
            }

            result[i]=nextSar;
        }

        return result;
    }

    /*
    抛物转向点.
    用法:
    SARTURN(N,S,M),N为计算周期,S为步长,M为极值,若发生向上转向则返回1,若发生向下转向则返回-1,否则为0
    其用法与SAR函数相同
    */
    this.SARTURN=function(n,step,exValue)
    {
        var result=[];
        var sar=this.SAR(n,step,exValue);
        var stockData= this.SymbolData.Data;
        var index=0;
        for(index=0;index<sar.length;++index)
        {
                if (this.IsNumber(sar[index])) break;
        }
        var flag=0;
        if (index<stockData.Data.length) flag=stockData.Data[index].Close>sar[index];

        for(var i=index+1;i<stockData.Data.length;++i)
        {
            var item=stockData.Data[i];
            if (item.Close<sar[i] && flag) result[i]=-1;
            else result[i]= (item.Close>sar[i] && !flag)? 1:0;
            
            flag=item.Close>sar[i];
        }

        return result;
    }

    /*
    属于未来函数,将当前位置到若干周期前的数据设为1.
    用法:
    BACKSET(X,N),若X非0,则将当前位置到N周期前的数值设为1.
    例如:
    BACKSET(CLOSE>OPEN,2)若收阳则将该周期及前一周期数值设为1,否则为0
    */
    this.BACKSET=function(condition,n)
    {
        var result=[];
        if (!condition) return result;
        var dataCount=condition.length;
        if (!this.IsNumber(dataCount) || dataCount<=0) return result;

        for(var i=0;i<dataCount;++i)    //初始化0
        {
            result[i]=0;
        }

        for(var pos=0;pos<dataCount;++pos)
        {
            if (this.IsNumber(condition[pos])) break;
        }
        if (pos==dataCount) return result;

        var num=Math.min(dataCount-pos,Math.max(n,1));

        for(var i=dataCount-1,j=0;i>=0;--i)
        {
            var value=condition[i];
            if (this.IsNumber(value) && value)
            {
                for(j=i;j>i-num;--j)
                {
                    result[j]=1;
                }
            }
        }

        if (condition[i]) 
        {
            for(j=i;j>=pos;--j) result[j]=1;
        }

        return result;
    }

    //用法:BETWEEN(A,B,C)表示A处于B和C之间时返回1,否则返回0 
    //例如:BETWEEN(CLOSE,MA(CLOSE,20),MA(CLOSE,10))表示收盘价介于10日均线和20日均线之间
    this.BETWEEN=function(condition, data, data2)
    {
        var result=[];
        var isNumber=typeof(condition)=='number';
        var isNumber2=typeof(data)=='number';
        var isNumber3=typeof(data2)=='number';

        if (isNumber && isNumber2 && isNumber3)   //单数值
        {
            return (condition>=data && condition<=data2) ? 1 : 0;
        }

        for(var i in condition)
        {
            result[i]=0;
            var item=condition[i];
            var left=null, right=null;

            if (isNumber2) left=data;
            else if (i<data.length-1) left=data[i];
            
            if (isNumber3) right=data2;
            else if (i<data2.length-1) right=data2[i];

            if (left==null || right==null) continue;

            if (left>right)
            {
                if (item>=right && item<=left) 
                    result[i]=1;
            }
            else
            {
                if (item<=right && item>=left) 
                    result[i]=1;
            }
        }

        return result;
    }

    //STRCAT(A,B):将两个字符串A,B(非序列化)相加成一个字符串C.
    //用法: STRCAT('多头','开仓')将两个字符串'多头','开仓'相加成一个字符串'多头开仓'
    this.STRCAT=function(str1, str2)
    {
        var result=str1+str2;
        return result;
    }

    //CON2STR(A,N):取A最后的值(非序列值)转为字符串,小数位数N.
    //用法: CON2STR(FINANCE(20),3)表示取营业收入,以3位小数转为字符串
    this.CON2STR=function(data,n)
    {
        var result=[];
        if (Array.isArray(data))
        {
            for(var i=data.length-1 ; i>=0; --i)
            {
                var item=data[i];
                if (IFrameSplitOperator.IsNumber(item))
                {
                    result=item.toFixed(n);
                    return result;
                }
            }
        }
        else
        {
            if (IFrameSplitOperator.IsNumber(data)) 
                result=data.toFixed(n);
        }

        return result;
    }


    

    ////////////////////////////////////////////////////////////////////////
    //  跨周期函数COVER_C(), COVER_O(), COVER_H(), COVER_L(), COVER_A(), COVER_V()
    this.CoverPeriod=function(name, periodName,n)
    {
        var periodInfo=this.GetPeriodInfo({Name:periodName});
        if (!periodInfo) return null;

        var curPeriodInfo=this.GetPeriodInfo({PeriodID:this.SymbolData.Data.Period});
        if (!curPeriodInfo) return null;

        if (curPeriodInfo.Order>curPeriodInfo.Order) return null;   //只能小周期转大周期

        var result;
        if (curPeriodInfo.Period==periodInfo.Period) 
        {
            result=this.SymbolData.Data;
        }
        else
        {
            var hisData;
            if (ChartData.IsMinutePeriod(curPeriodInfo.Period,true) && ChartData.IsDayPeriod(periodInfo.Period,true))
            {
                if (periodInfo.Period==0) hisData=this.SymbolData.DayData.Data; //日线直接用
                else hisData=this.SymbolData.DayData.GetPeriodData(periodInfo.Period);  //分钟数据不复权 直接算周期就可以了
            }
            else
            {
                var bindData=new ChartData();
                bindData.Data=this.SymbolData.SourceData.Data;
                bindData.Period=this.SymbolData.Period;
                bindData.Right=this.SymbolData.Right;

                if (ChartData.IsDayPeriod(periodInfo.Period,true) && bindData.Right>0) //日线数据才复权
                {
                    var rightData=bindData.GetRightData(bindData.Right);
                    bindData.Data=rightData;
                }

                hisData=bindData.GetPeriodData(periodInfo.Period);
            }

            var data=this.SymbolData.Data.CoverTo(hisData, curPeriodInfo.Period, periodInfo.Period);
            result=new ChartData();
            result.Data=data;
        }

        if (IFrameSplitOperator.IsPlusNumber(n))
        {
            var refResult=new ChartData();
            var data=result.GetRef(n);
            refResult.Data=data;
            result=refResult;
        }

        switch(name)
        {
            case 'COVER_C':
                return result.GetClose();
            case 'COVER_O':
                return result.GetOpen();
            case 'COVER_H':
                return result.GetHigh();
            case 'COVER_L':
                return result.GetLow();
            case 'COVER_A':
                return result.GetAmount();
            case 'COVER_V':
                return result.GetVol();
            default:
                return null;
        }
    }

    this.MOD=function(data, data2)
    {
        var result=[];
        let isNumber=typeof(data)=='number';
        let isNumber2=typeof(data2)=='number';

        //单数值
        if (isNumber && isNumber2) 
        {
            return JSAlgorithm.MOD(data,data2);
        }
        else if (!isNumber && !isNumber2) //都是数组相加
        {
            let count=Math.max(data.length, data2.length);
            for(let i=0;i<count;++i)
            {
                result[i]=null; //初始化

                if (i<data.length && i<data2.length)
                {
                    if ( this.IsNumber(data[i]) && this.IsNumber(data2[i]) ) result[i]=JSAlgorithm.MOD(data[i],data2[i]);
                }
            }

            return result;
        }
        else if (isNumber && !isNumber2)  //单数字 数组
        {
            for(var i in data2)
            {
                result[i]=null;
                if (this.IsNumber(data) && this.IsNumber(data2[i])) result[i]=JSAlgorithm.MOD(data,data2[i]);
            }

            return result;
        }
        else if (!isNumber && isNumber2)  //数组 单数字
        {
            for(var i in data)
            {
                result[i]=null;
                if (this.IsNumber(data[i]) && this.IsNumber(data2)) result[i]=JSAlgorithm.MOD(data[i],data2);
            }

            return result;
        }

        return result;
    }

    this.POW=function(data, n)
    {
        var result=[];
        if (!IFrameSplitOperator.IsNumber(n)) return result;

        let isNumber=typeof(data)=='number';
       
        if (isNumber) return Math.pow(data,n);

        for(var i in data)
        {
            var item=data[i];
            if (IFrameSplitOperator.IsNumber(item)) result[i]=Math.pow(item,n);
            else result[i]=null;
        }
        
        return result;
    }

    this.CEILING=function(data)
    {
        let isNumber=typeof(data)=='number';
        if (isNumber) return parseInt(data);

        var result=[];
        for(var i in data)
        {
            var item=data[i];
            if (IFrameSplitOperator.IsNumber(item)) result[i]=parseInt(item);
            else result[i]=null;
        }

        return result;
    }

    this.FLOOR=function(data)
    {
        let isNumber=typeof(data)=='number';
        if (isNumber) return parseInt((data-1));

        var result=[];
        for(var i in data)
        {
            var item=data[i];
            if (IFrameSplitOperator.IsNumber(item)) result[i]=parseInt((item-1));
            else result[i]=null;
        }

        return result;
    }

    //函数调用
    this.CallFunction=function(name,args,node,symbolData)
    {
        switch(name)
        {
            case 'MAX':
                return this.MAX(args[0], args[1]);
            case 'MIN':
                return this.MIN(args[0], args[1]);
            case 'REF':
                return this.REF(args[0], args[1]);
            case 'ABS':
                return this.ABS(args[0]);
            case 'MA':
                return this.MA(args[0], args[1]);
            case "EMA":
                return this.EMA(args[0], args[1]);
            case "SMA":
                return this.SMA(args[0], args[1],args[2]);
            case "DMA":
                return this.DMA(args[0], args[1]);
            case 'EXPMA':
                return this.EXPMA(args[0], args[1]);
            case 'EXPMEMA':
                return this.EXPMEMA(args[0], args[1]);
            case 'COUNT':
                return this.COUNT(args[0], args[1]);
            case 'LLV':
                return this.LLV(args[0], args[1]);
            case 'LLVBARS':
                return this.LLVBARS(args[0], args[1]);
            case 'HHV':
                return this.HHV(args[0], args[1]);
            case 'HHVBARS':
                return this.HHVBARS(args[0], args[1]);
            case 'MULAR':
                return this.MULAR(args[0], args[1]);
            case 'CROSS':
                return this.CROSS(args[0], args[1]);
            case 'LONGCROSS':
                return this.LONGCROSS(args[0], args[1], args[2]);
            case 'AVEDEV':
                return this.AVEDEV(args[0], args[1]);
            case 'STD':
                return this.STD(args[0], args[1]);
            case 'IF':
            case 'IFF':
                return this.IF(args[0], args[1], args[2]);
            case 'IFN':
                return this.IFN(args[0], args[1], args[2]);
            case 'NOT':
                return this.NOT(args[0]);
            case 'SUM':
                return this.SUM(args[0], args[1]);
            case 'RANGE':
                return this.RANGE(args[0],args[1],args[2]);
            case 'EXIST':
                return this.EXIST(args[0],args[1]);
            case 'EXISTR':
                return this.EXISTR(args[0],args[1],args[2]);
            case 'FILTER':
                return this.FILTER(args[0],args[1]);
            case 'TFILTER':
                return this.TFILTER(args[0],args[1],args[2]);
            case 'SLOPE':
                return this.SLOPE(args[0],args[1]);
            case 'BARSLAST':
                return this.BARSLAST(args[0]);
            case 'BARSCOUNT':
                return this.BARSCOUNT(args[0]);
            case 'BARSSINCEN':
                return this.BARSSINCEN(args[0],args[1]);
            case 'BARSSINCE':
                return this.BARSSINCE(args[0]);
            case 'LAST':
                return this.LAST(args[0],args[1],args[2]);
            case 'EVERY':
                return this.EVERY(args[0],args[1]);
			case 'DEVSQ':
                return this.DEVSQ(args[0], args[1]);
            case 'ZIG':
                return this.ZIG(args[0],args[1]);
            case 'TROUGHBARS':
                return this.TROUGHBARS(args[0],args[1],args[2]);
            case 'PEAKBARS':
                return this.PEAKBARS(args[0],args[1],args[2]);
            case 'COST':
                return this.COST(args[0]);
            case 'WINNER':
                return this.WINNER(args[0]);
            case 'FORCAST':
                return this.FORCAST(args[0], args[1]);
            case 'STDP':
                return this.STDP(args[0], args[1]);
            case 'VAR':
                return this.VAR(args[0], args[1]);
            case 'VARP':
                return this.VARP(args[0], args[1]);
            case 'UPNDAY':
                return this.UPNDAY(args[0],args[1]);
            case 'DOWNNDAY':
                return this.DOWNNDAY(args[0],args[1]);
            case 'NDAY':
                return this.NDAY(args[0],args[1],args[2]);
            case 'RELATE':
                return this.RELATE(args[0],args[1],args[2]);
            case 'COVAR':
                return this.COVAR(args[0],args[1],args[2]);
            case 'BETA':
                return this.BETA(args[0]);
            case 'BETA2':
                return this.BETA2(args[0],args[1],args[2]);
            case 'WMA':
                return this.WMA(args[0], args[1]);
            case 'MEMA':
                return this.MEMA(args[0], args[1]);
            case 'SUMBARS':
                return this.SUMBARS(args[0], args[1]);
            case 'REVERSE':
                return this.REVERSE(args[0]);
            case 'SAR':
                return this.SAR(args[0], args[1], args[2]);
            case 'SARTURN':
                return this.SARTURN(args[0], args[1], args[2]);
            case 'BACKSET':
                return this.BACKSET(args[0], args[1]);
            case 'BETWEEN':
                return this.BETWEEN(args[0], args[1], args[2]);
            case 'STRCAT':
                return this.STRCAT(args[0], args[1]);
            case 'CON2STR':
                return this.CON2STR(args[0], args[1]);
            case 'COVER_C':
            case 'COVER_O':
            case 'COVER_H':
            case 'COVER_L':
            case 'COVER_A':
            case 'COVER_V':
                if (args.length==2) return this.CoverPeriod(name,args[0],args[1]);
                return this.CoverPeriod(name,args[0]);
            case 'MOD':
                return this.MOD(args[0],args[1]);
            case 'POW':
                return this.POW(args[0],args[1]);
            case 'CEILING':
                return this.CEILING(args[0]);
            case 'FLOOR':
                return this.FLOOR(args[0]);
            //三角函数
            case 'ATAN':
                return this.Trigonometric(args[0],Math.atan);
            case 'ACOS':
                return this.Trigonometric(args[0],Math.acos);
            case 'ASIN':
                return this.Trigonometric(args[0],Math.asin);
            case 'COS':
                return this.Trigonometric(args[0],Math.cos);
            case 'SIN':
                return this.Trigonometric(args[0],Math.sin);
            case 'TAN':
                return this.Trigonometric(args[0],Math.tan);
            case 'LN':
                return this.Trigonometric(args[0],Math.log);
            case 'LOG':
                return this.Trigonometric(args[0],Math.log10);
            case 'EXP':
                return this.Trigonometric(args[0],Math.exp);
            case 'SQRT':
                return this.Trigonometric(args[0],Math.sqrt);
            default:
                if (g_JSComplierResource.IsCustomFunction(name))
                    return this.CallCustomFunction(name, args, symbolData);

                this.ThrowUnexpectedNode(node,'函数'+name+'不存在');
        }
    }

    this.CallCustomFunction=function(name, args,symbolData)
    {
        var item=g_JSComplierResource.CustomFunction.Data.get(name);
        if (!item || !item.Invoke) return [];

        var obj={ Name:name, Args:args, Symbol:symbolData.Symbol, Period:symbolData.Period, Right:symbolData.Right, KData:symbolData.Data };
        return item.Invoke(obj);
    }

    this.ThrowUnexpectedNode=function(node,message)
    {
        let marker=node.Marker;
        let msg=message || "执行异常";
       
        return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg);
       
    }
}


JSAlgorithm.prototype.GetPeriodInfo=function(obj)    //{Name:周期名字  PeriodID:周期ID}
{
    const PERIOD_LIST=
    [
        {Name:'MIN1', Period:4, Order:1},
        {Name:'MIN5', Period:5, Order:2},
        {Name:'MIN15', Period:6, Order:3},
        {Name:'MIN30', Period:7, Order:4},
        {Name:'MIN60', Period:8, Order:5},

        {Name:'DAY', Period:0, Order:1000},
        {Name:'WEEK', Period:1, Order:1001},
        {Name:'MONTH', Period:2, Order:1002},
        {Name:"YEAR", Period:3, Order:1003}
    ];

    if (obj.Name)
    {
        for(var i in PERIOD_LIST)
        {
            if (obj.Name && PERIOD_LIST[i].Name==obj.Name) 
                return PERIOD_LIST[i];
        }

        return null;
    }

    if (IFrameSplitOperator.IsNumber(obj.PeriodID))
    {
        for(var i in PERIOD_LIST)
        {
            if (PERIOD_LIST[i].Period==obj.PeriodID)
                return PERIOD_LIST[i];
        }

        return null;
    }

    return null;
}

//是否有是有效的数字
JSAlgorithm.prototype.IsNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;

    return true;
}

//是否有是有效的除数
JSAlgorithm.prototype.IsDivideNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;
    if (value==0) return false;

    return true;
}

//取模
JSAlgorithm.MOD=function(number,divisor)
{
    if( (number < 0 && divisor < 0) || (number >=0 && divisor >= 0))     //同号
    {   
        if(parseInt(number) == number && parseInt(divisor) == divisor)  //全为整数
        {        
            return number%divisor;
        }
        else  //被除数-(整商×除数)之后在第一位小数位进行四舍五入
        {
            var value = parseFloat((number - (Math.floor(number/divisor) * divisor)).toFixed(1));
            return value;
        }
    }
    else    //异号
    {   
        var absNumber = Math.abs(number);        //绝对值
        var absDivisor = Math.abs(divisor);      //绝对值
        var value = Math.abs(Math.abs(divisor) * (Math.floor(absNumber/absDivisor) + 1) - Math.abs(number));
        if(divisor < 0) value = -value
        return value;
    }     
}

/*
   绘图函数 
*/
function JSDraw(errorHandler,symbolData)
{
    this.ErrorHandler=errorHandler;
    this.SymbolData=symbolData;

    this.DRAWTEXT=function(condition,price,text)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWTEXT',Text:text};
        if (condition.length<=0) return result;

        var IsNumber=typeof(price)=="number";

        for(var i in condition)
        {
            drawData[i]=null;

            if (isNaN(condition[i]) || !condition[i]) continue;

            if (IsNumber) 
            {
                drawData[i]=price;
            }
            else 
            {
                if (this.IsNumber(price[i])) drawData[i]=price[i];
            }
        }

        return result;
    }

    this.DRAWTEXT_FIX=function(condition,x,y,type,text)
    {
        let result={Position:null, DrawType:'DRAWTEXT_FIX',Text:text};
        if (condition.length<=0) return result;

        for(var i in condition)
        {
            if (isNaN(condition[i]) || !condition[i]) continue;

            result.Position={X:x, Y:y, Type:type};
            return result;
        }

        return result;
    }

    //direction 文字Y轴位置 0=middle 1=价格的顶部 2=价格的底部
    //offset 文字Y轴偏移
    this.SUPERDRAWTEXT=function(condition,price,text,direction,offset)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'SUPERDRAWTEXT',Text:text,YOffset:offset,Direction:direction,TextAlign:'center'};
        if (condition.length<=0) return result;

        var IsNumber=typeof(price)=="number";

        for(var i in condition)
        {
            drawData[i]=null;

            if (isNaN(condition[i]) || !condition[i]) continue;

            if (IsNumber) 
            {
                drawData[i]=price;
            }
            else 
            {
                if (this.IsNumber(price[i])) drawData[i]=price[i];
            }
        }

        return result;
    }

    /*
    STICKLINE 绘制柱线
    在图形上绘制柱线。
    用法: STICKLINE(COND,PRICE1,PRICE2,WIDTH,EMPTY),当COND条件满足时,在PRICE1和PRICE2位置之间画柱状线,宽度为WIDTH(10为标准间距),EMPTH不为0则画空心柱。
    例如: STICKLINE(CLOSE>OPEN,CLOSE,OPEN,0.8,1)表示画K线中阳线的空心柱体部分。
    */
    this.STICKLINE=function(condition,data,data2,width,type)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'STICKLINE',Width:width, Type:type};

        if(condition.length<=0) return result;

        var IsNumber=typeof(data)=="number";
        var IsNumber2=typeof(data2)=="number";
   
        for(var i in condition)
        {
            drawData[i]=null;

            if (isNaN(condition[i]) || !condition[i]) continue;

            if (IsNumber && IsNumber2)
            {
                drawData[i]={Value:data,Value2:data2};
            }
            else if (IsNumber && !IsNumber2)
            {
                if (isNaN(data2[i])) continue;
                drawData[i]={Value:data,Value2:data2[i]};
            }
            else if (!IsNumber && IsNumber2)
            {
                if (isNaN(data[i])) continue;
                drawData[i]={Value:data[i],Value2:data2};
            }
            else
            {
                if (isNaN(data[i]) || isNaN(data2[i])) continue;
                drawData[i]={Value:data[i],Value2:data2[i]};
            }
        }

        return result;
    }

    /*
    DRAWLINE 绘制直线段
    在图形上绘制直线段。
    用法: DRAWLINE(COND1,PRICE1,COND2,PRICE2,EXPAND)
    当COND1条件满足时,在PRICE1位置画直线起点,当COND2条件满足时,在PRICE2位置画直线终点,EXPAND为延长类型。
    例如: DRAWLINE(HIGH>=HHV(HIGH,20),HIGH,LOW<=LLV(LOW,20),LOW,1) 表示在创20天新高与创20天新低之间画直线并且向右延长。
    */
    this.DRAWLINE=function(condition,data,condition2,data2,expand)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWLINE', Expand:expand};

        if(condition.length<=0) return result;
        let count=Math.max(condition.length,condition2.length);

        let bFirstPoint=false;
        let bSecondPont=false;
        let lineCache={Start:{ },End:{ }, List:new Array()};

        for(let i=0;i<count;++i)
        {
            drawData[i]=null;
            if (i<condition.length && i<condition2.length)
            {
                if (bFirstPoint==false && bSecondPont==false)
                {
                    if (condition[i]==null || !condition[i]) continue;

                    bFirstPoint=true;
                    lineCache.Start={ID:i, Value:data[i]};  //第1个点
                }
                else if (bFirstPoint==true && bSecondPont==false)
                {
                    var bCondition2=(condition2[i]!=null && condition2[i]); //条件2
                    if (!bCondition2) continue;

                    if (bCondition2)
                    {
                        bSecondPont=true;
                        lineCache.End={ID:i, Value:data2[i]};   //第2个点
                    }
                }

                if (bFirstPoint==true && bSecondPont==true)
                {
                    let lineData=this.CalculateDrawLine(lineCache);     //计算2个点的线上 其他点的数值

                    for(let j in lineData)
                    {
                        let item=lineData[j];
                        drawData[item.ID]=item.Value;
                    }

                    bFirstPoint=bSecondPont=false;
                    lineCache={Start:{ },End:{ }};
                }  
            }
        }

        return result;
    }

    /*
    画出带状线.
    用法: DRAWBAND(VAL1,COLOR1,VAL2,COLOR2),当VAL1>VAL2时,在VAL1和VAL2之间填充COLOR1;当VAL1<VAL2时,填充COLOR2,这里的颜色均使用RGB函数计算得到.
    例如: DRAWBAND(OPEN,RGB(0,224,224),CLOSE,RGB(255,96,96));
    */
    this.DRAWBAND=function(data,color,data2,color2)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWBAND', Color:[color.toLowerCase(),color2.toLowerCase()]};  //颜色使用小写字符串
        let count=Math.max(data.length, data2.length);

        for(let i=0;i<count;++i)
        {
            let item={Value:null, Value2:null};
            if (i<data.length) item.Value=data[i];
            if (i<data2.length) item.Value2=data2[i];

            drawData.push(item);
        }

        return result;
    }

    this.DRAWKLINE=function(high,open,low,close)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWKLINE'};
        let count=Math.max(high.length, open.length,low.length,close.length);

        for(let i=0;i<count;++i)
        {
            let item={Open:null,High:null, Low:null, Close:null};

            if (i<high.length && i<open.length && i<low.length && i<close.length)
            {
                item.Open=open[i];
                item.High=high[i];
                item.Low=low[i];
                item.Close=close[i];
            }

            drawData[i]=item;
        }


        return result;
    }

    //满足条件画一根K线
    this.DRAWKLINE_IF=function(condition,high,open,low,close)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWKLINE_IF'};
        let count=Math.max(condition.length,high.length, open.length,low.length,close.length);

        for(let i=0;i<count;++i)
        {
            let item={Open:null,High:null, Low:null, Close:null};

            if (i<high.length && i<open.length && i<low.length && i<close.length && i<condition.length)
            {
                if (condition[i])
                {
                    item.Open=open[i];
                    item.High=high[i];
                    item.Low=low[i];
                    item.Close=close[i];
                }
            }

            drawData[i]=item;
        }


        return result;
    }

    /*
    PLOYLINE 折线段
    在图形上绘制折线段。
    用法: PLOYLINE(COND,PRICE),当COND条件满足时,以PRICE位置为顶点画折线连接。
    例如: PLOYLINE(HIGH>=HHV(HIGH,20),HIGH)表示在创20天新高点之间画折线。
    */
    this.POLYLINE=function(condition,data)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'POLYLINE'};
        let isNumber=typeof(data)=='number';

        let bFirstPoint=false;
        let bSecondPont=false;
        if (isNumber)
        {
            for(let i in condition)
            {
                drawData[i]=null;
                if (bFirstPoint==false)
                {
                    if (!condition[i]) continue;

                    drawData[i]=data;
                    bFirstPoint=true;
                }
                else
                {
                    drawData[i]=data;
                }
            }
        }
        else
        {
            let lineCache={Start:{ },End:{ }, List:new Array()};
            for(let i in condition)
            {
                drawData[i]=null;
                if (bFirstPoint==false && bSecondPont==false)
                {
                    if (condition[i]==null || !condition[i]) continue;
                    if (i>=data.length || !this.IsNumber(data[i])) continue;

                    bFirstPoint=true;
                    lineCache.Start={ID:parseInt(i), Value:data[i]};  //第1个点
                }
                else if (bFirstPoint==true && bSecondPont==false)
                {
                    if (condition[i]==null || !condition[i]) continue;
                    if (i>=data.length || !this.IsNumber(data[i])) continue;

                    lineCache.End={ID:parseInt(i), Value:data[i]};   //第2个点
                    //根据起始点和结束点 计算中间各个点的数据
                    let lineData=this.CalculateDrawLine(lineCache);     //计算2个点的线上 其他点的数值

                    for(let j in lineData)
                    {
                        let item=lineData[j];
                        drawData[item.ID]=item.Value;
                    }

                    let start={ ID:lineCache.End.ID,Value:lineCache.End.Value };
                    lineCache={Start:start,End:{ } };
                }
            }
        }

        return result
    }

    /*
    画出数字.
    用法:
    DRAWNUMBER(COND,PRICE,NUMBER),当COND条件满足时,在PRICE位置书写数字NUMBER.
    例如:
    DRAWNUMBER(CLOSE/OPEN>1.08,LOW,C)表示当日实体阳线大于8%时在最低价位置显示收盘价.
    */
    this.DRAWNUMBER=function(condition,data,data2)
    {
        let drawData={ Value:new Array(), Text:new Array() };
        let result={DrawData:drawData, DrawType:'DRAWNUMBER'};
        let isNumber=typeof(data2)=='number';
        let text;
        if (isNumber) text=data2.toFixed(2);

        for(let i in condition)
        {
            drawData.Value[i]=null;
            if (!condition[i]) continue;
            if (i>=data.length || !this.IsNumber(data[i])) continue;

            if (isNumber)
            {
                drawData.Value[i]=data[i];
                drawData.Text[i]=text;
            }
            else
            {
                if (i>=data2.length || !data2[i]) continue;
                drawData.Value[i]=data[i];
                if (typeof(data2[i])=='number')
                    drawData.Text[i] = data2[i].toFixed(2);
                else
                    drawData.Text[i] = data2[i].toString();
            }
        }

        return result;
    }

    /*
    在图形上绘制小图标.
    用法:
    DRAWICON(COND,PRICE,TYPE),当COND条件满足时,在PRICE位置画TYPE号图标(TYPE为1--41).
    例如:
    DRAWICON(CLOSE>OPEN,LOW,1)表示当收阳时在最低价位置画1号图标.
    */
    this.DRAWICON=function(condition,data,type)
    {
        var icon=g_JSComplierResource.GetDrawIcon(type);
       
        if (!icon)
        {
            //图标对应的字符代码
            let mapIcon=new Map([
                [1,{Symbol:'↑',Color:'rgb(238,44,44)'} ],[2,{Symbol:'↓',Color:'rgb(0,139,69)'} ],
                [3,{Symbol:'😧'} ],[4,{Symbol:'😨'} ],[5,{Symbol:'😁'} ],[6,{Symbol:'😱'} ],
                [7,{Symbol:'B',Color:'rgb(238,44,44)'} ],[8,{Symbol:'S',Color:'rgb(0,139,69)'} ],
                [9,{Symbol:'💰'} ],[10,{Symbol:'📪'} ],[11,{Symbol:'👆'} ],[12,{Symbol:'👇'} ],
                [13,{Symbol:'B',Color:'rgb(178,34,34)'}, ],[14,{Symbol:'S',Color:'rgb(0,139,69)'} ],
                [36,{Symbol:'Χ',Color:'rgb(238,44,44)'} ],[37,{Symbol:'X',Color:'rgb(0,139,69)'} ],
                [38,{Symbol:'▲',Color:'rgb(238,44,44)'} ],[39,{Symbol:'▼',Color:'rgb(0,139,69)'} ],
                [40,{Symbol:'◉',Color:'rgb(238,44,44)'}], [41,{Symbol:'◈',Color:'rgb(238,44,44)'}],
                [42,{Symbol:'📌'}], [43,{Symbol:'💎'}], [44,{Symbol:'🥇'}],[45,{Symbol:'🥈'}],[46,{Symbol:'🥉'}],[47,{Symbol:'🏅'}]
            ]);

            icon=mapIcon.get(type);
        }

        if (!icon) icon={Symbol:'🚩'};
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWICON',Icon:icon};
        if (condition.length<=0) return result;

        var IsNumber=typeof(data)=="number";
        if (typeof(condition)=='number')
        {
            if (!condition) return result;

            for(var i=0;i<this.SymbolData.Data.Data.length;++i)
            {
                if (IsNumber) 
                {
                    drawData[i]=data;
                }
                else 
                {
                    if (i<data.length && this.IsNumber(data[i])) drawData[i]=data[i];
                    else drawData[i]=null;
                }
            }
            return result;
        }

        for(var i in condition)
        {
            drawData[i]=null;

            if (!condition[i]) continue;

            if (IsNumber) 
            {
                drawData[i]=data;
            }
            else 
            {
                if (this.IsNumber(data[i])) drawData[i]=data[i];
            }
        }

        return result;
    }

    /*
    绘制通道
    condition:条件
    data,data2:通道顶部和底部
    borderColor: 通道顶部和底部线段颜色RGB(24,30,40) 不填就不画
    borderWidth: 通道顶部和底部线段宽度
    areaColor: 通道面积颜色 RGB(200,30,44) 不填使用默认颜色
    dotted: 通道顶部和底部虚线设置 '3,4' , 不填默认 3,3
    */
    this.DRAWCHANNEL=function(condition, data, data2, borderColor, borderWidth, dotted, areaColor)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'DRAWCHANNEL', Border:{ }};
        if (condition.length<=0) return result;

        if (borderColor) result.Border.Color=borderColor;
        if (areaColor) result.AreaColor=areaColor;
        if (borderWidth>0) result.Border.Width=borderWidth;
        if (dotted) 
        {
            let ary=dotted.split(',');
            result.Border.Dotted=[];
            for(var i in ary)
            {
                var item=ary[i];
                if (!item) continue;
                var value=parseInt(ary[i]);
                if (value<=0) continue;
                result.Border.Dotted.push(value);
            }
            if (result.Border.Dotted.length<=0) result.Border.Dotted=null;
        }

        var IsNumber=typeof(data)=="number";
        var IsNumber2=typeof(data2)=="number";
        if (typeof(condition)=='number')
        {
            if (!condition) return result;  //条件是否

            for(var i=0;i<this.SymbolData.Data.Data.length;++i)
            {
                drawData[i]=null;

                if (IsNumber && IsNumber2)
                {
                    drawData[i]={Value:data,Value2:data2};
                }
                else if (IsNumber && !IsNumber2)
                {
                    if (isNaN(data2[i])) continue;
                    drawData[i]={Value:data,Value2:data2[i]};
                }
                else if (!IsNumber && IsNumber2)
                {
                    if (isNaN(data[i])) continue;
                    drawData[i]={Value:data[i],Value2:data2};
                }
                else
                {
                    if (isNaN(data[i]) || isNaN(data2[i])) continue;
                    drawData[i]={Value:data[i],Value2:data2[i]};
                }
            }
        }
        else
        {
            for(var i=0;i<condition.length;++i)
            {
                drawData[i]=null;

                if (isNaN(condition[i]) || !condition[i]) continue;

                if (IsNumber && IsNumber2)
                {
                    drawData[i]={Value:data,Value2:data2};
                }
                else if (IsNumber && !IsNumber2)
                {
                    if (isNaN(data2[i])) continue;
                    drawData[i]={Value:data,Value2:data2[i]};
                }
                else if (!IsNumber && IsNumber2)
                {
                    if (isNaN(data[i])) continue;
                    drawData[i]={Value:data[i],Value2:data2};
                }
                else
                {
                    if (isNaN(data[i]) || isNaN(data2[i])) continue;
                    drawData[i]={Value:data[i],Value2:data2[i]};
                }
            }
        }

        return result;
    }

    this.RGB=function(r,g,b)
    {
        var rgb=`RGB(${r},${g},${b})`;
        return rgb;
    }

    this.RGBA=function(r,g,b,a)
    {
        var rgba=`RGB(${r},${g},${b},${a})`;
        return rgba;
    }

    //用法:PARTLINE(PRICE,COND1,COLOR1,COND2,COLOR2...),
    //绘制PRICE线,当COND1条件满足时,用COLOR1颜色,当COND2条件满足时,用COLOR2颜色,否则不绘制,从COLOR1之后的参数均可以省略,最多可以有10组条件
    //例如:PARTLINE(CLOSE,CLOSE>OPEN,RGB(255,0,0),CLOSE<OPEN,RGB(0,255,0),1,RGB(0,0,255))
    //表示画收盘价线,阳线时用红色,阴线时用绿色,平盘用蓝色.注意最后一个条件为1,表示前面都不满足时必然满足这个条件
    this.PARTLINE=function(args)
    {
        let drawData=[];
        let result={DrawData:drawData, DrawType:'PARTLINE'};
        if (args.length<3) return result;

        var data=args[0];
        var condition=[];
        for(var i=1;i<args.length && i+1<args.length;i+=2)
        {
            condition.push({Cond:args[i], RGB:args[i+1]});
        }

        for(var i in data)
        {
            var drawItem={Value:null, RGB:null};
            drawData[i]=drawItem;

            var value=data[i];
            if (!this.IsNumber(value)) continue;

            var rgb=null;
            for(var j in condition)
            {
                var item=condition[j];
                if (i<item.Cond.length && item.Cond[i])
                {
                    rgb=item.RGB;
                    break;
                }
            }

            if (rgb) 
            {
                drawItem.Value=value;
                drawItem.RGB=rgb;
            }
        }

        return result;
    }

    //填充背景.
    //用法: DRAWGBK(COND,COLOR1,COLOR2,colorAngle)  colorAngle=渐近色角度
    //例如: DRAWGBK(O>C,RGB(0,255,0),RGB(255,0,0),0);
    this.DRAWGBK=function(condition, color, color2, colorAngle)
    {
        let drawData={ Color:[],  Angle:colorAngle };
        if (color) drawData.Color.push(color);
        if (color2) drawData.Color.push(color2);

        let result={DrawData:null, DrawType:'DRAWGBK'};
        if (Array.isArray(condition))
        {
            for(var i in condition)
            {
                var item=condition[i];
                if (item) 
                {
                    result.DrawData=drawData;
                    break;
                }
            }
        }
        else
        {
            if (condition) result.DrawData=drawData;
        }

        return result;
    }

    //画文字 及线段
    this.DRAWTEXT_LINE=function(condition, price, text, textcolor, fontSize, linetype, linecolor)
    {
        let drawData={ Text:{ Title:text, Color:textcolor }, Line:{ Type:linetype, Color:linecolor } };
        if (fontSize<=0) fontSize=12;
        drawData.Text.Font=fontSize*GetDevicePixelRatio()+'px 微软雅黑'; 

        let result={DrawData:null, DrawType:'DRAWTEXT_LINE'};
        
        if (Array.isArray(condition))
        {
            for(var i in condition)
            {
                var item=condition[i];
                if (item) 
                {
                    if (Array.isArray(price)) drawData.Price=price[i];
                    else drawData.Price=price;
                    result.DrawData=drawData;
                   
                    break;
                }
            }
        }
        else
        {
            if (condition) 
            {
                if (Array.isArray(price)) drawData.Price=price[0];
                else drawData.Price=price;
                result.DrawData=drawData;
            }
        }

        return result;
    }

    // 相对位置上画矩形.
    //用法: DRAWRECTREL(LEFT,TOP,RIGHT,BOTTOM,COLOR),以图形窗口(LEFT,TOP)为左上角,(RIGHT,BOTTOM)为右下角绘制矩形,坐标单位是窗口沿水平和垂直方向的1/1000,取值范围是0—999,超出范围则可能显示在图形窗口外,矩形中间填充颜色COLOR,COLOR为0表示不填充.
    //例如: DRAWRECTREL(0,0,500,500,RGB(255,255,0))表示在图形最左上部1/4位置用黄色绘制矩形
    this.DRAWRECTREL=function(left, top, right,bottom, color)
    {
        
        let drawData={ Rect:{Left:left, Top:top, Right:right, Bottom:bottom }, Color:color };
        if (color==0) drawData.Color=null;
        let result={DrawData:drawData, DrawType:'DRAWRECTREL'};

        return result;
    }
}


JSDraw.prototype.CalculateDrawLine=function(lineCache)
{
    lineCache.List=[];
    for(let i=lineCache.Start.ID; i<=lineCache.End.ID; ++i) lineCache.List.push(i);

    let height=Math.abs(lineCache.Start.Value-lineCache.End.Value);
    let width=lineCache.List.length-1;

    var result=[];
    result.push({ID:lineCache.Start.ID, Value:lineCache.Start.Value});  //第1个点

    if (lineCache.Start.Value>lineCache.End.Value)
    {
        for(let i=1;i<lineCache.List.length-1;++i)
        {
            var value=height*(lineCache.List.length-1-i)/width+lineCache.End.Value;
            result.push({ID:lineCache.List[i], Value:value});
        }
    }
    else
    {
        for(let i=1;i<lineCache.List.length-1;++i)
        {
            var value=height*i/width+lineCache.Start.Value;
            result.push({ID:lineCache.List[i], Value:value});
        }
    }

    result.push({ID:lineCache.End.ID, Value:lineCache.End.Value});      //最后一个点

    return result;
}

//是否有是有效的数字
JSDraw.prototype.IsNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;

    return true;
}

JSDraw.prototype.IsDrawFunction=function(name)
{
    let setFunctionName=new Set(
    [
        "STICKLINE","DRAWTEXT",'SUPERDRAWTEXT','DRAWLINE','DRAWBAND','DRAWKLINE','DRAWKLINE_IF','PLOYLINE',
        'POLYLINE','DRAWNUMBER','DRAWICON','DRAWCHANNEL','PARTLINE','DRAWTEXT_FIX','DRAWGBK','DRAWTEXT_LINE','DRAWRECTREL'
    ]);
    if (setFunctionName.has(name)) return true;

    return false;
}

//http://www.newone.com.cn/helpcontroller/index?code=zszy_pc
var DYNAINFO_ARGUMENT_ID=
{
    YCLOSE:3,
    OPEN:4,
    HIGH:5,
    LOW:6,
    CLOSE:7,
    VOL:8,
    AMOUNT:10,
    AMPLITUDE:13,   //振幅
    INCREASE:14,    //涨幅
    EXCHANGERATE:37,    //换手率
};

function JSSymbolData(ast,option,jsExecute)
{
    this.AST=ast;               //语法树
    this.Execute=jsExecute;

    this.Symbol='600000.sh';
    this.Name;
    this.Data=null;             //个股数据
    this.DayData=null;          //日线数据
    this.SourceData=null;       //不复权的个股数据
    this.MarketValue=null;      //总市值
    this.Period=0;              //周期
    this.Right=0;               //复权
    this.DataType=0;            //默认K线数据 2=分钟走势图数据 3=多日分钟走势图
    this.IsBeforeData=false;    //当日走势图数据是否包含开盘前数据
    this.DayCount;              //多日分时图天数

    this.KLineApiUrl= g_JSComplierResource.Domain+"/API/KLine2";                   //日线
    this.MinuteKLineApiUrl= g_JSComplierResource.Domain+'/API/KLine3';             //分钟K线
    this.RealtimeApiUrl= g_JSComplierResource.Domain+'/API/stock';                 //实时行情
    this.HistoryMinuteApiUrl=g_JSChartResource.Domain+'/API/StockMinuteData';      //历史分钟数据(多日分时图)
    this.StockHistoryDayApiUrl= g_JSComplierResource.Domain+'/API/StockHistoryDay';  //历史财务数据
    this.StockHistoryDay3ApiUrl= g_JSComplierResource.Domain+'/API/StockHistoryDay3';  //历史财务数据
    this.StockNewsAnalysisApiUrl= g_JSComplierResource.CacheDomain+'/cache/newsanalyze';                //新闻分析数据
    this.HKToSHSZApiUrl=    //北上资金
    [ 
        g_JSComplierResource.CacheDomain+'/cache/historyday/all/hk2shsz.json',      //日线数据
        g_JSComplierResource.CacheDomain+'/cache/analyze/hk2shsz/hk2shsz.json',     //最新分钟
        g_JSComplierResource.Domain+'/API/HKToSHSZMinuteData'                       //多日分时分钟
    ] ;          

    this.MaxRequestDataCount=1000;
    this.MaxRequestMinuteDayCount=5;

    this.LatestData;            //最新行情
    this.IndexData;             //大盘指数
    this.LatestIndexData;       //最新大盘数据
    this.FinanceData=new Map(); //财务数据
    this.MarginData=new Map();  //融资融券
    this.HKToSHSZData=new Map();    //北上资金
    this.NewsAnalysisData=new Map();    //新闻统计
    this.ExtendData=new Map();          //其他的扩展数据
    this.UserData=new Map();            //用户数据

    this.SectionFinanceData=new Map();  //截面报告数据
    this.ThrowSFPeirod=new Set();       //重新获取数据

    this.NetworkFilter;                 //网络请求回调 function(data, callback);
    
   
    //使用option初始化
    if (option)
    {
        if (option.HQDataType) this.DataType=option.HQDataType;
        if (option.Data) 
        {
            this.Data=option.Data;
            if (this.DataType!=HQ_DATA_TYPE.MINUTE_ID && this.DataType!=HQ_DATA_TYPE.MULTIDAY_MINUTE_ID && this.DataType!=HQ_DATA_TYPE.HISTORY_MINUTE_ID)   //分钟走势图数据 没有周期和复权
            {
                this.Period=option.Data.Period; //周期
                this.Right=option.Data.Right;   //复权
            }
            //this.Data=null;
        }

        if (option.SourceData) this.SourceData=option.SourceData;
        if (option.Symbol) this.Symbol=option.Symbol;
        if (option.Name) this.Name=option.Name;
        if (option.MaxRequestDataCount>0) this.MaxRequestDataCount=option.MaxRequestDataCount;
        if (option.MaxRequestMinuteDayCount>0) this.MaxRequestMinuteDayCount=option.MaxRequestMinuteDayCount;
        if (option.KLineApiUrl) this.KLineApiUrl=option.KLineApiUrl;
        if (option.Right) this.Right=option.Right;
        if (option.Period) this.Period=option.Period;
        if (option.IsBeforeData===true) this.IsBeforeData=option.IsBeforeData;
        if (option.NetworkFilter) this.NetworkFilter=option.NetworkFilter;
        if (option.DayCount>0) this.DayCount=option.DayCount;
    }

    this.RecvError=function(request)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvError] ajax error.',request.status);
        throw {FunctionName:'RecvError', Request:request};
    }

    //最新行情
    this.GetLatestData=function()
    {
        if (this.LatestData) return this.Execute.RunNextJob();

        var self=this;

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'JSSymbolData::GetLatestData', //类名::
                Explain:'DYNAINFO()',
                Request:{ Url:self.RealtimeApiUrl,  Type:'POST' ,
                    Data: { symbol:[this.Symbol], field: ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","exchangerate","amplitude"] } }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvLatestData(data);
                self.Execute.RunNextJob();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: self.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","exchangerate","amplitude"],
                "symbol": [this.Symbol]
            },
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvLatestData(recvData);
                self.Execute.RunNextJob();
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvLatestData=function(data)
    {
        if (!data.stock || data.stock.length!=1) return;

        let stock=data.stock[0];
        this.LatestData={ Symbol:stock.symbol, Name:stock.name, Date:stock.date, Time:stock.time,
            YClose:stock.yclose,Price:stock.price, Open:stock.open, High:stock.high, Low:stock.low, Vol:stock.vol, Amount:stock.amount, 
            Increase:stock.increase, Exchangerate:stock.exchangerate, Amplitude:stock.amplitude};

        JSConsole.Complier.Log('[JSSymbolData::RecvLatestData]', this.LatestData);
    }

    this.GetLatestCacheData=function(dataname)
    {
        if (!this.LatestData) return  null;

        switch(dataname)
        {
            case DYNAINFO_ARGUMENT_ID.YCLOSE:
                return this.LatestData.YClose;
            case DYNAINFO_ARGUMENT_ID.OPEN:
                return this.LatestData.Open;
            case DYNAINFO_ARGUMENT_ID.HIGH:
                return this.LatestData.High;
            case DYNAINFO_ARGUMENT_ID.LOW:
                return this.LatestData.Low;
            case DYNAINFO_ARGUMENT_ID.VOL:
                return this.LatestData.Vol;
            case DYNAINFO_ARGUMENT_ID.AMOUNT:
                return this.LatestData.Amount;
            case DYNAINFO_ARGUMENT_ID.INCREASE:
                return this.LatestData.Increase;
            case DYNAINFO_ARGUMENT_ID.EXCHANGERATE:
                return this.LatestData.Exchangerate;
            case DYNAINFO_ARGUMENT_ID.AMPLITUDE:
                return this.LatestData.Amplitude;
            case DYNAINFO_ARGUMENT_ID.CLOSE:
                return this.LatestData.Price;
            default:
                return null;
        }
    }

    this.GetLatestIndexData=function()
    {
        if (this.LatestIndexData) return this.Execute.RunNextJob();

        var self=this;

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'JSSymbolData::GetLatestIndexData', //类名:: 函数
                Explain:'最新大盘数据',
                Request:{ Url:self.RealtimeApiUrl,  Type:'POST' ,
                    Data: { symbol:['000001.sh'], field: ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","amplitude"] } }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvLatestIndexData(data);
                self.Execute.RunNextJob();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: self.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","time","increase","amplitude"],
                "symbol": ['000001.sh']
            },
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvLatestIndexData(recvData);
                self.Execute.RunNextJob();
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvLatestIndexData=function(data)
    {
        if (!data.stock || data.stock.length!=1) return;

        let stock=data.stock[0];
        this.LatestIndexData={ Symbol:stock.symbol, Name:stock.name, Date:stock.date, Time:stock.time,
            YClose:stock.yclose,Price:stock.price, Open:stock.open, High:stock.high, Low:stock.low, Vol:stock.vol, Amount:stock.amount, 
            Increase:stock.increase, Amplitude:stock.amplitude};

        JSConsole.Complier.Log('[JSSymbolData::RecvLatestIndexData]', this.LatestIndexData);
    }

    this.GetLatestIndexCacheData=function(dataname)
    {
        if (!this.LatestIndexData) return  null;

        switch(dataname)
        {
            case 'FROMOPEN':
                {
                    var value=parseInt(this.LatestIndexData.Time/100);
                    var time=parseInt(value/100)*60+(value%100);    //转化为分钟个数
                    var index=0;
                    if(time <= 9 * 60 + 25) index=0;
                    else if(time < 9 * 60 + 30) index=1;
                    else if(time <= 11 * 60 + 30) index=(time - (9 * 60 + 30) + 1);
                    else if(time < 13 * 60) index=121;
                    else if(time <= 15 * 60) index=(122 + time - 13 * 60);
                    else index=242;
                    return index;
                }
            default:
                return null;
        }
    }
    

    this.GetVolRateData=function(job,node)
    {
        var volrKey=job.ID.toString()+'-VolRate-'+this.Symbol;
        if (this.ExtendData.has(volrKey)) return this.Execute.RunNextJob();

        var self=this;
        JSNetwork.HttpRequest({
            url: self.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","avgvol5", 'date'],
                "symbol": [this.Symbol]
            },
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvVolRateData(recvData,volrKey);
                self.Execute.RunNextJob();
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvVolRateData=function(data,key)
    {
        if (!data.stock || data.stock.length!=1) return;
        var avgVol5=data.stock[0].avgvol5;
        var date=data.stock[0].date;
        var item={AvgVol5:avgVol5, Date:date};
        this.ExtendData.set(key,item);

        JSConsole.Complier.Log('[JSSymbolData::RecvVolRateData]', item);
    }

    this.GetVolRateCacheData=function(node)
    {
        var key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA.toString()+'-VolRate-'+this.Symbol;
        if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node,'不支持VOLR');

        var result=[];
        var value=this.ExtendData.get(key);
        var avgVol5=value.AvgVol5/241;
        var totalVol=0;
        //5日成交总量只取了最新一天的,历史的暂时没有取,所以数据计算的时候只计算最新的一天, 其他都空
        for(var i=0, j=0;i<this.Data.Data.length;++i)
        {
            result[i]=null;
            var item=this.Data.Data[i];
            var dateTime=item.DateTime; //日期加时间
            if (!dateTime) continue;
            var aryValue=dateTime.split(' ');
            if (aryValue.length!=2) continue;
            var date=parseInt(aryValue[0]);
            if (date!=value.Date) continue;

            totalVol+=item.Vol;
            if (avgVol5>0) result[i]=totalVol/(j+1)/avgVol5*100;
            ++j;
        }

        return result;
    }

    this.GetGroupData=function(job)
    {
        var key=job.ID.toString()+'-Group-'+this.Symbol;
        if (this.ExtendData.has(key)) return this.Execute.RunNextJob();

        var self=this;

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'JSSymbolData::GetGroupData', //类名::
                Explain:'HYBLOCK|DYBLOCK',
                Request:{ Url:self.RealtimeApiUrl,  Type:'POST' ,
                    Data: { symbol:[this.Symbol], field: ["name","symbol","industry", 'concept','region'] } }, 
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvGroupData(data,key);
                self.Execute.RunNextJob();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        
        JSNetwork.HttpRequest({
            url: self.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","industry", 'concept','region'],
                "symbol": [this.Symbol]
            },
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvGroupData(recvData,key);
                self.Execute.RunNextJob();
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvGroupData=function(data,key)
    {
        if (!data.stock) return;
        if (data.stock.length!=1) return;
        var stock=data.stock[0];
        var industry=stock.industry;
        var concept=stock.concept;
        var region=stock.region;

        var groupData={Industry:[],Concept:[], Region:[] };
        if (industry)
        {
            for(var i in industry)
            {
                var item=industry[i];
                groupData.Industry.push({Name:item.name, Symbol:item.symbol});
            }
        }

        if (concept)
        {
            for(var i in concept)
            {
                var item=concept[i];
                groupData.Concept.push({Name:item.name, Symbol:item.symbol});
            }
        }

        if (region)
        {
            for(var i in region)
            {
                var item=region[i];
                groupData.Region.push({Name:item.name, Symbol:item.symbol});
            }
        }


        this.ExtendData.set(key,groupData);
    }


    //获取大盘指数数据
    this.GetIndexData=function()
    {
        if (this.IndexData) return this.Execute.RunNextJob();

        var self=this;
        if (ChartData.IsDayPeriod(this.Period,true))     //请求日线数据 9=季线
        {
            if (this.NetworkFilter)
            {
                var obj=
                {
                    Name:'JSSymbolData::GetIndexData', //类名::
                    Explain:'大盘数据',
                    Period:self.Period,
                    Request:{ Url:self.KLineApiUrl,  Type:'POST' ,
                        Data: { field:[ "name", "symbol","yclose","open","price","high","low","vol",'up','down','stop','unchanged'],
                            symbol: '000001.sh', start: -1 , count: self.MaxRequestDataCount+500 } },
                    Self:this,
                    PreventDefault:false
                };
                this.NetworkFilter(obj, function(data) 
                { 
                    self.RecvIndexHistroyData(data);
                    self.Execute.RunNextJob();
                });

                if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
            }

            JSNetwork.HttpRequest({
                url: self.KLineApiUrl,
                data:
                {
                    "field": [ "name", "symbol","yclose","open","price","high","low","vol",'up','down','stop','unchanged'],
                    "symbol": '000001.sh',
                    "start": -1,
                    "count": self.MaxRequestDataCount+500   //多请求2年的数据 确保股票剔除停牌日期以后可以对上
                },
                type:"post",
                dataType: "json",
                async:true, 
                success: function (recvData)
                {
                    self.RecvIndexHistroyData(recvData);
                    self.Execute.RunNextJob();
                },
                error: function(request)
                {
                    self.RecvError(request);
                }
            });
        }
        else  if (ChartData.IsMinutePeriod(this.Period, true))          //请求分钟数据
        {
            if (this.NetworkFilter)
            {
                var obj=
                {
                    Name:'JSSymbolData::GetIndexData', //类名::
                    Explain:'大盘数据',
                    Period:self.Period,
                    Request:{ Url:self.MinuteKLineApiUrl,  Type:'POST' ,
                        Data: { field:["name","symbol","yclose","open","price","high","low","vol"],
                            symbol: '000001.sh', start: -1 , count: self.MaxRequestDataCount+5 } },
                    Self:this,
                    PreventDefault:false
                };
                this.NetworkFilter(obj, function(data) 
                { 
                    self.RecvIndexMinuteHistroyData(data);
                    self.Execute.RunNextJob();
                });

                if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
            }

            JSNetwork.HttpRequest({
                url: self.MinuteKLineApiUrl,
                data:
                {
                    "field": ["name","symbol","yclose","open","price","high","low","vol"],
                    "symbol": '000001.sh',
                    "start": -1,
                    "count": self.MaxRequestMinuteDayCount+5
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    self.RecvIndexMinuteHistroyData(data);
                    self.Execute.RunNextJob();
                },
                error: function(request)
                {
                    self.RecvError(request);
                }
            });
        }

    }

    this.RecvIndexHistroyData=function(data)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvIndexHistroyData] recv data' , data);

        let hisData=this.JsonDataToHistoryData(data);
        this.IndexData=new ChartData();
        this.IndexData.DataType=0; /*日线数据 */
        this.IndexData.Data=hisData;

        var aryOverlayData=this.SourceData.GetOverlayData(this.IndexData.Data);      //和主图数据拟合以后的数据
        this.IndexData.Data=aryOverlayData;

        if (ChartData.IsDayPeriod(this.Period,false))   //周期数据
        {
            let periodData=this.IndexData.GetPeriodData(this.Period);
            this.IndexData.Data=periodData;
        }
    }

    this.RecvIndexMinuteHistroyData=function(data)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvIndexMinuteHistroyData] recv data' , data);

        let hisData=this.JsonDataToMinuteHistoryData(data);
        this.IndexData=new ChartData();
        this.IndexData.DataType=1; /*分钟线数据 */
        this.IndexData.Data=hisData;

        if (ChartData.IsMinutePeriod(this.Period,false))   //周期数据
        {
            let periodData=this.IndexData.GetPeriodData(this.Period);
            this.IndexData.Data=periodData;
        }
    }

    //获取大盘指数缓存数据
    this.GetIndexCacheData=function(dataName)
    {
        if (!this.IndexData) return  new Array();

        switch(dataName)
        {
        case 'INDEXA':
            return this.IndexData.GetAmount();
        case 'INDEXC':
            return this.IndexData.GetClose();
        case 'INDEXH':
            return this.IndexData.GetHigh();
        case 'INDEXL':
            return this.IndexData.GetLow();
        case 'INDEXO':
            return this.IndexData.GetOpen();
        case 'INDEXV':
            return this.IndexData.GetVol();
        case 'INDEXADV':
            return this.IndexData.GetUp();
        case 'INDEXDEC':
            return this.IndexData.GetDown();
        }
    }

    //分钟涨幅股票个数统计数据下载
    this.GetIndexIncreaseData=function(job)
    {
        var upKey=job.ID.toString()+'-UpCount-'+job.Symbol;
        var downKey=job.ID.toString()+'-DownCount-'+job.Symbol;
        if (this.ExtendData.has(upKey) && this.ExtendData.has(downKey)) return this.Execute.RunNextJob();

        var symbol=job.Symbol;
        symbol=symbol.replace('.CI','.ci');
        var self=this;
        if (this.DataType===HQ_DATA_TYPE.MINUTE_ID || this.DataType===HQ_DATA_TYPE.MULTIDAY_MINUTE_ID)  //走势图数据
        {
            var apiUrl=g_JSComplierResource.CacheDomain+'/cache/analyze/increaseanalyze/'+symbol+'.json';
            JSConsole.Complier.Log('[JSSymbolData::GetIndexIncreaseData] minute Get url=' , apiUrl);
            JSNetwork.HttpRequest({
                url: apiUrl,
                type:"get",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    self.RecvMinuteIncreaseData(data, {UpKey:upKey,DownKey:downKey});
                    self.Execute.RunNextJob();
                },
                error: function(request)
                {
                    self.RecvError(request);
                }
            });
        }
        else if (this.DataType===HQ_DATA_TYPE.KLINE_ID && this.Period===0) //K线图 日线
        {
            JSConsole.Complier.Log('[JSSymbolData::GetIndexIncreaseData] K day Get url=' , self.KLineApiUrl);
            JSNetwork.HttpRequest({
                url: self.KLineApiUrl,
                data:
                {
                    "symbol": symbol,
                    "start": -1,
                    "count": self.MaxRequestDataCount
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    self.RecvHistoryIncreaseData(data, {UpKey:upKey,DownKey:downKey});
                    self.Execute.RunNextJob();
                },
                error: function(request)
                {
                    self.RecvError(request);
                }
            });
        }
    }

    this.RecvHistoryIncreaseData=function(data,key)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvHistoryIncreaseData] recv data' , data);

        var upData=[],downData=[];
        for(var i in data.data)
        {
            var item=data.data[i];
            let upItem=new SingleData();
            let downItem=new SingleData();
            upItem.Date=item[0];
            upItem.Value=item[8];
            upData[i]=upItem;
            downItem.Date=item[0];
            downItem.Value=item[9];
            downData[i]=downItem;
        }

        var aryFixedData=this.Data.GetFittingData(upData);
        var bindData=new ChartData();
        bindData.Data=aryFixedData; 
        bindData.Period=this.Period;    //只支持日线
        var data=bindData.GetValue();
        this.ExtendData.set(key.UpKey,data);

        aryFixedData=this.Data.GetFittingData(downData);
        bindData=new ChartData();
        bindData.Data=aryFixedData; 
        bindData.Period=this.Period;    //只支持日线
        data=bindData.GetValue();
        this.ExtendData.set(key.DownKey,data);
    }

    this.RecvMinuteIncreaseData=function(data,key)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvMinuteIncreaseData] recv data' , data);
        if (!data.minute) return;
        var minuteData=data.minute;
        if (!minuteData.time || !minuteData.up || !minuteData.down) return;
        var upData=[],downData=[];

        if (this.IsBeforeData)
        {
            for(var i=0, j=0;i<this.Data.Data.length;++i)
            {
                upData[i]=null;
                downData[i]=null;
                var item=this.Data.Data[i];
                if (item.Before) continue;  //盘前数据
                var dateTime=item.DateTime; //日期加时间
                if (!dateTime) continue;
                var aryValue=dateTime.split(' ');
                if (aryValue.length!=2) continue;
                var date=parseInt(aryValue[0]);
                if (date!=data.date) continue;

                upData[i]=minuteData.up[j];
                downData[i]=minuteData.down[j];
                ++j;
            }
        }
        else
        {
            for(var i=0, j=0;i<this.Data.Data.length;++i)
            {
                upData[i]=null;
                downData[i]=null;
                var item=this.Data.Data[i];
                var dateTime=item.DateTime; //日期加时间
                if (!dateTime) continue;
                var aryValue=dateTime.split(' ');
                if (aryValue.length!=2) continue;
                var date=parseInt(aryValue[0]);
                if (date!=data.date) continue;

                upData[i]=minuteData.up[j];
                downData[i]=minuteData.down[j];
                ++j;
            }
        }

        this.ExtendData.set(key.UpKey,upData);
        this.ExtendData.set(key.DownKey,downData);
    }

    //分钟涨幅股票个数统计数据
    this.GetIndexIncreaseCacheData=function(funcName,symbol,node)
    {
        var key;
        if (funcName=='UPCOUNT') key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA.toString()+'-UpCount-'+symbol;
        else if (funcName=='DOWNCOUNT') key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA.toString()+'-DownCount-'+symbol;
        
        if (!key || !this.ExtendData.has(key)) this.Execute.ThrowUnexpectedNode(node,'不支持函数'+funcName+'('+symbol+')');

        return this.ExtendData.get(key);
    }


    this.GetSymbolData=function()
    {
        if (this.Data) return this.Execute.RunNextJob();

        let self=this;

        if (this.DataType===HQ_DATA_TYPE.MINUTE_ID)  //当天分钟数据
        {
            JSNetwork.HttpRequest({
                url: self.RealtimeApiUrl,
                data:
                {
                    "field": ["name","symbol","yclose","open","price","high","low","vol","amount","date","minute","time","minutecount"],
                    "symbol": [self.Symbol],
                    "start": -1
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (recvData)
                {
                    self.RecvMinuteData(recvData);
                    self.Execute.RunNextJob();
                }
            });
            return;
        }

        if (this.DataType===HQ_DATA_TYPE.MULTIDAY_MINUTE_ID)
        {
            JSNetwork.HttpRequest({
                url: self.HistoryMinuteApiUrl,
                data:
                {
                    "symbol": self.Symbol,
                    "daycount": self.DayCount
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (recvData)
                {
                    self.RecvMultiDayMinuteData(recvData);
                    self.Execute.RunNextJob();
                }
            });
            return;
        }

        if (ChartData.IsDayPeriod(this.Period,true))     //请求日线数据
        {
            JSNetwork.HttpRequest({
                url: self.KLineApiUrl,
                data:
                {
                    "field": [ "name", "symbol","yclose","open","price","high","low","vol"],
                    "symbol": self.Symbol,
                    "start": -1,
                    "count": self.MaxRequestDataCount
                },
                type:"post",
                dataType: "json",
                async:true, 
                success: function (recvData)
                {
                    self.RecvHistroyData(recvData);
                    self.Execute.RunNextJob();
                },
                error: function(request)
                {
                    self.RecvError(request);
                }
            });
        }
        else if (ChartData.IsMinutePeriod(this.Period, true))               //请求分钟数据
        {
            JSNetwork.HttpRequest({
                url: this.MinuteKLineApiUrl,
                data:
                {
                    "field": ["name","symbol","yclose","open","price","high","low","vol"],
                    "symbol": self.Symbol,
                    "start": -1,
                    "count": self.MaxRequestMinuteDayCount
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (data)
                {
                    self.RecvMinuteHistroyData(data);
                    self.Execute.RunNextJob();
                },
                error: function(request)
                {
                    self.RecvError(request);
                }
            });
        }
    }

    this.RecvHistroyData=function(data)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvHistroyData] recv data' , data);

        let hisData=this.JsonDataToHistoryData(data);
        this.Data=new ChartData();
        this.Data.DataType=0; /*日线数据 */
        this.Data.Data=hisData;
        this.SourceData=new ChartData;
        this.SourceData.Data=hisData;

        if (this.Right>0)    //复权
        {
            let rightData=this.Data.GetRightDate(this.Right);
            this.Data.Data=rightData;
        }

        if (ChartData.IsDayPeriod(this.Period,false))   //周期数据
        {
            let periodData=this.Data.GetPeriodData(this.Period);
            this.Data.Data=periodData;
        }

        this.Data.Right=this.Right;
        this.Data.Period=this.Period;
        this.Name=data.name;
    }

    this.RecvMinuteHistroyData=function(data)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvMinuteHistroyData] recv data' , data);

        let hisData=this.JsonDataToMinuteHistoryData(data);
        this.Data=new ChartData();
        this.Data.DataType=1; /*分钟线数据 */
        this.Data.Data=hisData;
        this.SourceData=new ChartData;
        this.SourceData.Data=hisData;

        if (ChartData.IsMinutePeriod(this.Period,false))   //周期数据
        {
            let periodData=this.Data.GetPeriodData(this.Period);
            this.Data.Data=periodData;
        }

        this.Data.Period=this.Period;
        this.Name=data.name;
    }

    //最新的分钟数据走势图
    this.RecvMinuteData=function(data)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvMinuteData] recv data' , data);

        var aryMinuteData=this.JsonDataToMinuteData(data);
        this.Data=new ChartData();
        this.Data.DataType=2; /*分钟走势图数据 */
        this.Data.Data=aryMinuteData;

        this.Name=data.stock[0].name;
    }

    this.RecvMultiDayMinuteData=function(data)
    {
        var aryMinuteData=this.JsonDataToMultiDayMinuteData(data);
        this.Data=new ChartData();
        this.Data.DataType=2; /*分钟走势图数据 */
        this.Data.Data=aryMinuteData;

        this.Name=data.name;
    }

    this.GetSymbolCacheData=function(dataName)
    {
        if (!this.Data) return new Array();

        switch(dataName)
        {
            case 'CLOSE':
            case 'C':
                return this.Data.GetClose();
            case 'VOL':
            case 'V':
                return this.Data.GetVol();
            case 'OPEN':
            case 'O':
                return this.Data.GetOpen();
            case 'HIGH':
            case 'H':
                return this.Data.GetHigh();
            case 'LOW':
            case 'L':
                return this.Data.GetLow();
            case 'AMOUNT':
                return this.Data.GetAmount();
        }
    }

    this.GetSymbolDayData=function()
    {
        //分钟周期才需要下载日线
        if (this.DataType!=HQ_DATA_TYPE.KLINE_ID || !ChartData.IsMinutePeriod(this.Period,true) || this.DayData)
            return this.Execute.RunNextJob();
        
        var self=this;
        JSNetwork.HttpRequest({
            url: self.KLineApiUrl,
            data:
            {
                "field": [ "name", "symbol","yclose","open","price","high","low","vol"],
                "symbol": self.Symbol,
                "start": -1,
                "count": self.MaxRequestDataCount
            },
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvSymbolDayData(recvData);
                self.Execute.RunNextJob();
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvSymbolDayData=function(data)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvSymbolDayData] recv data' , data);

        let hisData=this.JsonDataToHistoryData(data);
        var dayData=new ChartData();
        dayData.DataType=0; /*日线数据 */
        dayData.Data=hisData;   //保存原始数据 不复权
        
        dayData.Right=this.Right;
        dayData.Period=this.Period;

        this.DayData=dayData;
    }

    this.GetCurrBarsCount=function()
    {
        let result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        let lCount=this.Data.Data.length;
        for(let i=lCount-1;i>=0;--i)
            result.push(i);

        return result;
    }

    this.GetIsLastBar=function()
    {
        let result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result

        let lCount=this.Data.Data.length;
        for(let i=0;i<lCount;++i)
        {
            if (i==lCount-1) result.push(1);
            else result.push(0);
        }

        return result;
    }

    //财务函数
    this.GetFinanceCacheData=function(id, node)
    {
        let jobID=JS_EXECUTE_JOB_ID.GetFinnanceJobID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node,'不支持FINANCE('+id+')');
        if(this.FinanceData.has(jobID)) return this.FinanceData.get(jobID);

        return [];
    }

    this.GetCompanyReleaseDate=function(jobID)
    {
        if (this.FinanceData.has(jobID)) return this.Execute.RunNextJob();

        var self=this;
        JSNetwork.HttpRequest({
            url: self.RealtimeApiUrl,
            data:
            {
                "field": ["name","symbol","company.releasedate"],   //公司上司日期
                "symbol": [this.Symbol]
            },
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvCompanyReleaseDate(jobID,recvData);
                self.Execute.RunNextJob();
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvCompanyReleaseDate=function(jobID,data)
    {
        if (!data.stock || data.stock.length!=1) return;
        let value=data.stock[0].company.releasedate;
        let releaseDate=new Date(value/10000,value%10000/100,value%100);

        let aryData=[];
        for(let i in this.Data.Data)
        {
            value=this.Data.Data[i].Date;
            let date=new Date(value/10000,value%10000/100,value%100);
            let item=new SingleData();
            item.Date=value;
            item.Value=parseInt((date-releaseDate)/(1000 * 60 * 60 * 24));
            aryData[i]=item;
        }

        JSConsole.Complier.Log('[JSSymbolData::RecvCompanyReleaseDate] jobID=', jobID, aryData)

        var bindData=new ChartData();
        bindData.Data=aryData;
        bindData.Period=this.Period;    //周期

        if (bindData.Period>0)          //周期数据
        {
            var periodData=bindData.GetPeriodSingleData(bindData.Period);
            bindData.Data=periodData;
        }

        let stockData=bindData.GetValue();
        this.FinanceData.set(jobID,stockData);
    }

    //下载财务数据
    this.GetFinanceData=function(jobID)
    {
        if (this.FinanceData.has(jobID)) return this.Execute.RunNextJob();

        JSConsole.Complier.Log('[JSSymbolData::GetFinanceData] jobID=', jobID);
        var self=this;
        let fieldList=["name","date","symbol"];
        
        switch(jobID)
        {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA:   //流通股本(万股)
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:       //流通股本(手)
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:   //流通市值
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:      //换手率
                fieldList.push("capital.a");
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:  //总股本(万股)
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:   //总市值
                fieldList.push('capital.total');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
                fieldList.push('finance.peruprofit');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
                fieldList.push('finance.pernetasset');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
                fieldList.push('finance.percreserve');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
                fieldList.push('finance.persearning');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
                fieldList.push('finance.nprofit');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
                fieldList.push('finance.alratio');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
                fieldList.push('finance.profityoy');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:    //过去4个季度现金分红总额
                fieldList.push('execdividend.quarter4');
                break;
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                fieldList.push('shareholder');                //股东信息
                break;
        }

        if (this.NetworkFilter)
        {
            var obj=
            {
                Name:'JSSymbolData::GetFinanceData', //类名::
                Explain:'财务数据',
                JobID:jobID,
                Request:{ Url:self.StockHistoryDayApiUrl,  Type:'POST' ,
                    Data:{ field: fieldList, symbol: [this.Symbol], orderfield:"date" }, },
                Self:this,
                PreventDefault:false
            };
            this.NetworkFilter(obj, function(data) 
            { 
                self.RecvFinanceData(data,jobID);
                self.Execute.RunNextJob();
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: this.StockHistoryDayApiUrl,
            data:
            {
                "field": fieldList,
                "symbol": [this.Symbol],
                "orderfield":"date"
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvFinanceData(recvData,jobID);
                self.Execute.RunNextJob();
            }
        });
    }

    this.RecvFinanceData=function(recvData,jobID)
    {
        switch(jobID)
        {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                return this.RecvStockDayData(recvData,jobID);
        }
    }

    this.RecvStockDayData=function(recvData,jobID)
    {
        //JSConsole.Complier.Log(recvData);
        if (!recvData.stock || recvData.stock.length!=1) return;

        let stock=recvData.stock[0];
        var bFinanceData=false; //是否是定期的财务数据
        var bMarketValue=false; //是否计算市值
        var aryData=new Array();
        for(let i in stock.stockday)
        {
            var item=stock.stockday[i];
            let indexData=new SingleData();
            indexData.Date=item.date;

            switch(jobID)
            {
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:
                    var financeData=item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value=financeData.a/100;    //流通股本(手)
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:
                    var financeData=item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.total)) continue;
                    indexData.Value=financeData.total/10000; //总股本(万股)
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA: 
                    var financeData=item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value=financeData.a/10000; //流通股本(万股)
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:   //流通市值
                    var financeData=item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value=financeData.a; //流通股本
                    bMarketValue=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:       //总市值
                    var financeData=item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.total)) continue;
                    indexData.Value=financeData.total; //总股本
                    bMarketValue=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:          //换手率
                    var financeData=item.capital;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.a)) continue;
                    indexData.Value=financeData.a; //流通股本
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.peruprofit)) continue;
                    indexData.Value=financeData.Finance.peruprofit;        //每股未分配利润
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.pernetasset)) continue;
                    indexData.Value=financeData.Finance.pernetasset;       //每股净资产
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.percreserve)) continue;
                    indexData.Value=financeData.Finance.percreserve;       //每股资本公积金
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.persearning)) continue;
                    indexData.Value=financeData.Finance.persearning;       //每股收益
                    bFinanceData=true; 
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.persearning)) continue;
                    indexData.Value=financeData.Finance.persearning/financeData.Announcement.quarter*4;       //每股收益(折算为全年收益)  报告期每股收益/报告期*4
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.nprofit)) continue;
                    indexData.Value=financeData.Finance.nprofit;       //净利润
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.alratio)) continue;
                    indexData.Value=financeData.Finance.alratio;       //资产负债率
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
                    var financeData=this.JsonDataToFinance(item);
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.Finance.profityoy)) continue;
                    indexData.Value=financeData.Finance.profityoy;       //净利润同比增长率
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
                    var financeData=item.execdividend;
                    if (!financeData) continue;
                    if (!this.IsNumber(financeData.quarter4)) continue;
                    indexData.Value=financeData.quarter4;       //过去4个季度现金分红总额
                    bFinanceData=true;
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:   //股东人数
                    var shareHolder=item.shareholder;
                    if (!shareHolder || !this.IsNumber(shareHolder.count)) continue;
                    indexData.Value=shareHolder.count;
                    bFinanceData=true;
                    break;
                default:
                    continue;
            }

            aryData.push(indexData);
        }

        let aryFixedData;
        if (bFinanceData) 
        {
            if (this.SourceData) aryFixedData=this.SourceData.GetFittingFinanceData(aryData);
            else aryFixedData=this.Data.GetFittingFinanceData(aryData);
        }
        else if (bMarketValue) 
        {
            if (this.SourceData) aryFixedData=this.SourceData.GetFittingMarketValueData(aryData);   //总市值用不复权的价格计算
            else aryFixedData=this.Data.GetFittingMarketValueData(aryData);

            if (jobID==JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA) this.MarketValue=aryFixedData;  //总市值保存下 算其他数据可能要用
        }
        else if (jobID==JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA)          //股息率TTM:过去4个季度现金分红总额/总市值 * 100%
        {
            aryFixedData=this.CalculateDividendYield(aryData);
        }
        else 
        {
            aryFixedData=this.SourceData.GetFittingData(aryData);
        }

        

        //JSConsole.Complier.Log('[JSSymbolData::RecvStockDayData] jobID=' + jobID, aryFixedData)

        var bindData=new ChartData();
        bindData.Data=aryFixedData;
        bindData.Period=this.Period;    //周期

        if (bindData.Period>0)          //周期数据
        {
            var periodData=bindData.GetPeriodSingleData(bindData.Period);
            bindData.Data=periodData;
        }

        if (jobID==JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA)   //计算换手率 成交量/流通股本*100
        {
            for(let i in this.Data.Data)
            {
                if (!bindData.Data[i]) continue;
                
                if (this.IsNumber(bindData.Data[i].Value) && bindData.Data[i].Value!=0) 
                    bindData.Data[i].Value=this.Data.Data[i].Vol/bindData.Data[i].Value*100;
                else 
                    bindData.Data[i].Value=1;
            }
        }

        let data=bindData.GetValue();
        this.FinanceData.set(jobID,data);
    }

    //计算股息率 股息率TTM:过去4个季度现金分红总额/总市值 * 100%
    this.CalculateDividendYield=function(cashData)
    {
        var dividendYield=[];
        if (!this.MarketValue) return dividendYield;

        for(let i=0,j=0;i<this.Data.Data.length;++i)
        {
            var day=this.Data.Data[i];
            var market=this.MarketValue[i];
            if (!day || !market) continue;

            let item=new SingleData();
            item.Date=day.Date;
            item.Value=0;

            if (j+1<cashData.length)
            {
                if (cashData[j].Date<day.Date && cashData[j+1].Date<=day.Date)
                {
                    ++j;
                    --i;
                    continue;
                }
            }

            if (j<cashData.length)
            {
                var cash=cashData[j];
                var endDate=cash.Date+10000;    //1年有效

                if (day.Date>=cash.Date && day.Date<=endDate && this.IsDivideNumber(market.Value) && this.IsNumber(cash.Value))
                {
                    item.Value=cash.Value/market.Value*100;
                }
            }
           
            dividendYield.push(item);
        }

        return dividendYield;
    }

    //融资融券函数
    this.GetMarginCacheData=function(id, node)
    {
        let jobID=JS_EXECUTE_JOB_ID.GetMarginJobID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node,'不支持MARGIN('+id+')');
        if(this.MarginData.has(jobID)) return this.MarginData.get(jobID);

        return [];
    }

    //下融资融券
    this.GetMarginData=function(jobID)
    {
        if (this.MarginData.has(jobID)) return this.Execute.RunNextJob();

        JSConsole.Complier.Log('[JSSymbolData::GetMarginData] jobID=', jobID);
        var self=this;
        let fieldList=["name","date","symbol"];
        
        switch(jobID)
        {
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:           //融资融券余额
            fieldList.push("margin.balance");
            break;
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:              //融资占比
            fieldList.push("margin.rate");
            break;
    
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE:       //买入信息-融资余额
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT:        //买入信息-买入额
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY:         //买入信息-偿还额
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET:           //买入信息-融资净买入
            fieldList.push("margin.buy");
            break;
    
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE:      //卖出信息-融券余量
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME:       //卖出信息-卖出量
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY:        //卖出信息-偿还量
        case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET:          //卖出信息-融券净卖出
            fieldList.push("margin.sell");
            break;
        }

        //请求数据
        JSNetwork.HttpRequest({
            url: this.StockHistoryDayApiUrl,
            data:
            {
                "field": fieldList,
                "symbol": [this.Symbol],
                "orderfield":"date"
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvMarginData(recvData,jobID);
                self.Execute.RunNextJob();
            }
        });
    }

    this.RecvMarginData=function(data,jobID)
    {
        JSConsole.Complier.Log(data);
        if (!data.stock || data.stock.length!=1) return;

        let stock=data.stock[0];
        var aryData=new Array();
        var aryData2=[], aryData3=[], aryData4=[];  //其他3个数据
        for(let i in stock.stockday)    
        {
            var item=stock.stockday[i];
            var marginData=item.margin;
            if (!marginData) continue;

            let indexData=new SingleData();
            indexData.Date=item.date;

            switch(jobID)
            {
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
                    if (!this.IsNumber(marginData.balance)) continue;
                    indexData.Value=marginData.balance; //融资融券余额
                    aryData.push(indexData);
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
                    if (!this.IsNumber(marginData.rate)) continue;
                    indexData.Value=marginData.rate;    //融资占比
                    aryData.push(indexData);
                    break;

                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE:       //买入信息-融资余额
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT:        //买入信息-买入额
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY:         //买入信息-偿还额
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET:           //买入信息-融资净买入
                    var buyData=marginData.buy;
                    if (!buyData) continue;
                    if (!this.IsNumber(buyData.balance) || !this.IsNumber(buyData.amount) || !this.IsNumber(buyData.repay) || !this.IsNumber(buyData.net)) continue;

                    indexData.Value=buyData.balance;
                    var indexData2=new SingleData();
                    indexData2.Date=item.date;
                    indexData2.Value=buyData.amount;
                    var indexData3=new SingleData();
                    indexData3.Date=item.date;
                    indexData3.Value=buyData.repay;
                    var indexData4=new SingleData();
                    indexData4.Date=item.date;
                    indexData4.Value=buyData.net;

                    aryData.push(indexData);
                    aryData2.push(indexData2);
                    aryData3.push(indexData3);
                    aryData4.push(indexData4);
                    break;
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE:      //卖出信息-融券余量
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME:       //卖出信息-卖出量
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY:        //卖出信息-偿还量
                case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET:          //卖出信息-融券净卖出
                    var sellData=marginData.sell;
                    if (!sellData) continue;
                    if (!this.IsNumber(sellData.balance) || !this.IsNumber(sellData.volume) || !this.IsNumber(sellData.repay) || !this.IsNumber(sellData.net)) continue;

                    indexData.Value=buyData.balance;
                    var indexData2=new SingleData();
                    indexData2.Date=item.date;
                    indexData2.Value=buyData.volume;
                    var indexData3=new SingleData();
                    indexData3.Date=item.date;
                    indexData3.Value=buyData.repay;
                    var indexData4=new SingleData();
                    indexData4.Date=item.date;
                    indexData4.Value=buyData.net;

                    aryData.push(indexData);
                    aryData2.push(indexData2);
                    aryData3.push(indexData3);
                    aryData4.push(indexData4);
                    break;
                default:
                    continue;
            }
        }

        var allData=[];
        if (jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE || jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE)
        {
            allData.push({JobID:jobID,Data:aryData});
        }
        else if (jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE || jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT ||
            jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY || jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET)
        {
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE,Data:aryData});
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT,Data:aryData2});
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY,Data:aryData3});
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET,Data:aryData4});
        }
        else if (jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE || jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME ||
            jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY || jobID===JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET)
        {
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE,Data:aryData});
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME,Data:aryData2});
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY,Data:aryData3});
            allData.push({JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET,Data:aryData4});
        }

        for(let i in allData)
        {
            let aryFixedData=this.SourceData.GetFittingData(allData[i].Data);

            var bindData=new ChartData();
            bindData.Data=aryFixedData;
            bindData.Period=this.Period;    //周期

            if (bindData.Period>0)          //周期数据
            {
                var periodData=bindData.GetPeriodSingleData(bindData.Period);
                bindData.Data=periodData;
            }

            let data=bindData.GetValue();
            this.MarginData.set(allData[i].JobID,data);
        }
    }

    this.GetNewsAnalysisCacheData=function(id,node)
    {

        let jobID=JS_EXECUTE_JOB_ID.GetNewsAnalysisID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node,'不支持NEWS('+id+')');
        if(this.NewsAnalysisData.has(jobID)) return this.NewsAnalysisData.get(jobID);

        return [];
    }

    this.GetHKToSHSZData=function(jobID)
    {
        if (this.HKToSHSZData.has(jobID)) return this.Execute.RunNextJob();

        var url, dataType=0;
        if (this.DataType===HQ_DATA_TYPE.MINUTE_ID) dataType=1;
        else if (this.DataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) dataType=2;
        else if (this.Period<=3) dataType=0;

        url=this.HKToSHSZApiUrl[dataType];
        var self=this;
        JSConsole.Complier.Log(`[JSSymbolData::GetHKToSHSZData] jobID=${jobID}, url=${url}, dataType=${dataType}`);

        if (dataType===2)
        {
            //请求数据
            JSNetwork.HttpRequest({
                url: url,
                data:
                {
                    "symbol": this.Symbol,
                    'daycount': this.MaxRequestMinuteDayCount
                },
                type:"post",
                dataType: "json",
                async:true,
                success: function (recvData)
                {
                    self.RecvMulitMinuteHKToSHSZData(recvData,jobID);
                    self.Execute.RunNextJob();
                }
            });
        }
        else
        {
            //请求数据
            JSNetwork.HttpRequest({
                url: url,
                type:"get",
                dataType: "json",
                async:true,
                success: function (recvData)
                {
                    if (dataType==0) self.RecvHKToSHSZData(recvData,jobID);
                    else if (dataType==1) self.RecvMinuteHKToSHSZData(recvData,jobID);
                    self.Execute.RunNextJob();
                }
            });
        }
    }

    this.RecvMinuteHKToSHSZData=function(data,jobID)
    {
        var arySHSZData=[], arySHData=[], arySZData=[];
        if (this.IsBeforeData)
        {
            for( i in this.SourceData.Data)
            {
                var item=this.SourceData.Data[i];
                if (item.Before)
                {
                    arySHSZData.push(null);
                    arySHData.push(null);
                    arySZData.push(null);
                }
                else
                {
                    break;
                }
            }

            for(var i=0;i<data.time.length;++i)
            {
                var time=data.time[i];
                if (time===925) continue;

                var SHValue=data.hk2sh[i];
                var SZValue=data.hk2sz[i];
                var total=SHValue+SZValue;
    
                arySHSZData.push(total);
                arySHData.push(SHValue);
                arySZData.push(SZValue);
            }
        }
        else
        {
            for(var i=0;i<data.time.length;++i)
            {
                var time=data.time[i];
                var SHValue=data.hk2sh[i];
                var SZValue=data.hk2sz[i];
                var total=SHValue+SZValue;
    
                arySHSZData.push(total);
                arySHData.push(SHValue);
                arySZData.push(SZValue);
            }
        }

        var allData=
        [
            {Data:arySHSZData, JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ}, 
            {Data:arySHData,JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH}, 
            {Data:arySZData,JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ}
        ];

        for(var i in allData)
        {
            var item=allData[i];
            this.HKToSHSZData.set(item.JobID,item.Data);
        }
    }

    this.RecvHKToSHSZData=function(data,jobID)
    {
        var arySHSZData=[], arySHData=[], arySZData=[];
        for(var i=0;i<data.date.length;++i)
        {
            var date=data.date[i];
            var SHValue=data.hk2sh[i]*1000000;  //单位是百万
            var SZValue=data.hk2sz[i]*1000000;  //单位是百万
            var total=SHValue+SZValue;

            let itemSHSZData=new SingleData();
            itemSHSZData.Date=date;
            itemSHSZData.Value=total;

            let itemSHData=new SingleData();
            itemSHData.Date=date;
            itemSHData.Value=SHValue;

            let itemSZData=new SingleData();
            itemSZData.Date=date;
            itemSZData.Value=SZValue;

            arySHSZData.push(itemSHSZData);
            arySHData.push(itemSHData);
            arySZData.push(itemSZData);
        }

        var allData=
        [
            {Data:arySHSZData, JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ}, 
            {Data:arySHData,JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH}, 
            {Data:arySZData,JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ}
        ];

        for(let i in allData)
        {
            let aryFixedData=this.Data.GetFittingData(allData[i].Data);

            var bindData=new ChartData();
            bindData.Data=aryFixedData;
            bindData.Period=this.Period;    //周期

            if (bindData.Period>0)          //周期数据
            {
                var periodData=bindData.GetPeriodSingleData(bindData.Period);
                bindData.Data=periodData;
            }

            let data=bindData.GetValue();
            this.HKToSHSZData.set(allData[i].JobID,data);
        }
    }

    this.RecvMulitMinuteHKToSHSZData=function(data,jobID)   //多日分时图北上资金
    {
        if (!data.data || data.data.length<=0) return;

        var arySHSZData=[], arySHData=[], arySZData=[];
        for(var i=0 ,j=0;i<this.Data.Data.length && j<data.data.length; )
        {
            arySHSZData[i]=null;
            arySHData[i]=null;
            arySZData[i]=null;
            var item=this.Data.Data[i];
            var dateTime=item.DateTime; //日期加时间
            if (!dateTime) 
            {
                ++i;
                continue;
            }
            var aryValue=dateTime.split(' ');
            if (aryValue.length!=2) 
            {
                ++i;
                continue;
            }
            var date=parseInt(aryValue[0]);
            var day=data.data[j];
            if (!day.minute || day.minute.length<=0)
            {
                ++j;
                continue;
            }

            if (day.date>date)
            {
                ++i;
                continue;
            }
            else if (day.date<date)
            {
                ++j;
                continue;
            }

            for(var k in day.minute)
            {
                var timeItem=day.minute[k];

                var SHValue=timeItem[1];
                var SZValue=timeItem[2];
                var total=SHValue+SZValue;

                arySHSZData[i]=total;
                arySHData[i]=SHValue;
                arySZData[i]=SZValue;
                ++i;
            }

            ++j;
        }

        var allData=
        [
            {Data:arySHSZData, JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ}, 
            {Data:arySHData,JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH}, 
            {Data:arySZData,JobID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ}
        ];

        for(var i in allData)
        {
            var item=allData[i];
            this.HKToSHSZData.set(item.JobID,item.Data);
        }
        
    }

    //北上资金函数
    this.GetHKToSHSZCacheData=function(id, node)
    {
        let jobID=JS_EXECUTE_JOB_ID.GetHK2SHSZJobID(id);
        if (!jobID) this.Execute.ThrowUnexpectedNode(node,'不支持HK2SHSZ('+id+')');
        if(this.HKToSHSZData.has(jobID)) return this.HKToSHSZData.get(jobID);

        return [];
    }

    //下载新闻统计
    this.GetNewsAnalysisData=function(jobID)
    {
        if (this.NewsAnalysisData.has(jobID)) return this.Execute.RunNextJob();

        var self=this;
        var mapFolder=new Map([
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE,     "negative"],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH,     'research'],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT,     'interact'],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE, 'holderchange'],      //NEWS(4)   股东增持
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2,'holderchange'],      //NEWS(5)   股东减持
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER,  'trustholder'],       //NEWS(6)   信托持股
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING, 'Blocktrading'],      //NEWS(7)   大宗交易
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS,  'companynews'],       //NEWS(8)   官网新闻
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS,  'topmanagers'],       //NEWS(9)   高管要闻
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE,       'Pledge'],            //NEWS(10)  股权质押
        ]);

        if (!mapFolder.has(jobID))
        {
            this.Execute.RunNextJob();
            return;
        }
        var folderName=mapFolder.get(jobID);
        var url=this.StockNewsAnalysisApiUrl+'/'+folderName+'/'+this.Symbol+'.json';

        //请求数据
        JSNetwork.HttpRequest({
            url: url,
            type:"get",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvNewsAnalysisData(recvData,jobID);
                self.Execute.RunNextJob();
            },
            error:function(request, textStatus)
            {
                self.RecvNewsAnalysisDataError(request, textStatus,jobID);
                self.Execute.RunNextJob();
            }
        });
    }

    this.RecvNewsAnalysisDataError=function(request, textStatus,jobID)
    {
        JSConsole.Complier.Log('[JSSymbolData::RecvNewsAnalysisDataError] ajax error.',request.status);

        //没有新闻使用0数据填充
        var aryData=[];
        for(var i=0;i<this.Data.Data.length;++i)
        {
            var item=new SingleData();
            item.Date=this.Data.Data[i].Date;
            item.Value=0
            aryData.push(item);
        }

        var bindData=new ChartData();
        bindData.Data=aryData;
        this.NewsAnalysisData.set(jobID,bindData.GetValue());
    }

    this.RecvNewsAnalysisData=function(data,jobID)
    {
        if (!data.data || !data.date) return;
        if (data.data.length<=0 || data.data.length!=data.date.length) return;
        
        JSConsole.Complier.Log('[JSSymbolData::RecvNewsAnalysisData] jobID',jobID, data.update);
        if (jobID==JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE|| jobID==JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2)
        {
            var aryData=[], aryData2=[];
            for(var i=0;i<data.data.length;++i)
            {
                var item=new SingleData();
                item.Date=data.date[i];
                item.Value=data.data[i];
                if (this.IsNumber(item.Value)) aryData.push(item);

                if (i<data.data2.length)
                {
                    item=new SingleData();
                    item.Date=data.date[i];
                    item.Value=data.data2[i];
                    if (this.IsNumber(item.Value)) aryData2.push(item);
                }
            }

            let aryFixedData=this.Data.GetFittingData2(aryData,0);
            var bindData=new ChartData();
            bindData.Data=aryFixedData;
            this.NewsAnalysisData.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE,bindData.GetValue());

            aryFixedData=this.Data.GetFittingData2(aryData2,0);
            bindData=new ChartData();
            bindData.Data=aryFixedData;
            this.NewsAnalysisData.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2,bindData.GetValue());
        }
        else
        {
            var aryData=[];
            for(var i=0;i<data.data.length;++i)
            {
                var item=new SingleData();
                item.Date=data.date[i];
                item.Value=data.data[i];
                aryData.push(item);
            }

            let aryFixedData=this.Data.GetFittingData2(aryData,0);
            var bindData=new ChartData();
            bindData.Data=aryFixedData;
            this.NewsAnalysisData.set(jobID,bindData.GetValue());
        }
    }

    this.GetSectionFinanceData=function(job)
    {
        var sfKey=job.SF[0];
        var period=job.SF[1].Period;
        if (this.SectionFinanceData.has(sfKey)) return this.Execute.RunNextJob();

        JSConsole.Complier.Log(`[JSSymbolData::GetSectionFinanceData] ${period.Year}-${period.Quarter}`);
        var fieldName='announcement'+period.Quarter;
        var self=this;
        var fieldList=["name","date","symbol", fieldName,'finance'+period.Quarter];
        var cond=[fieldName+'.year', "int32", "eq", period.Year.toString() ];

        //请求数据
        JSNetwork.HttpRequest({
            url: this.StockHistoryDayApiUrl,
            data:
            {
                "field": fieldList,
                "symbol": [this.Symbol],
                "condition":[{item:cond}]
            },
            type:"post",
            dataType: "json",
            async:true,
            success: function (recvData)
            {
                self.RecvSectionFinanceData(recvData,job);
                self.Execute.RunNextJob();
            }
        });
    }

    this.RecvSectionFinanceData=function(data,job)
    {
        if (!data.stock || data.stock.length!=1) return;
        var stockItem=data.stock[0];
        if (!stockItem.stockday || stockItem.stockday.length<=0) return;
        var dayItem=stockItem.stockday[0];
        var period=job.SF[1].Period;
        var finance=null;
        if (period.Quarter===1) finance=dayItem.finance1;
        else if (period.Quarter===2) finance=dayItem.finance2;
        else if (period.Quarter===3) finance=dayItem.finance3;
        else if (period.Quarter===4) finance=dayItem.finance4;
        if (!finance) return;
        
        var data=new Map([
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_01,finance.currentassets],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_02,finance.monetaryfunds],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_03,finance.inventory],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_04,finance.currentliabilities],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_05,finance.ncurrentliabilities],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_06,finance.expenses3],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_07,finance.investmentincome],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_08,finance.pcnprofit],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_09,finance.nnetprofit],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_10,finance.npersearning],

            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_11,finance.woewa],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_12,finance.inprocess],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_13,finance.accdepreciation],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_14,finance.mholderprofit],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_15,finance.lossexchange],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_16,finance.baddebts],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_17,finance.fixedassets],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_18,finance.curdepreciation],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_19,finance.orevenues],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_20,finance.moprofit],

            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_21,finance.oprofit],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_22,finance.nprofit],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_23,finance.areceivable],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_24,finance.financialcost],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_25,finance.ccfo],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_26,finance.totalassets],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_27,finance.totalliabilities],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_28,finance.totalownersequity],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_29,finance.grossmargin],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_30,finance.percreserve],

            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_31,finance.peruprofit],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_32,finance.persearning],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_33,finance.pernetasset],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_34,finance.perccfo],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_38,finance.alratio],
            [JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_39,finance.profityoy],
        ]);

        if (period.Quarter===4) //年报才有的数据
        {
            data.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_35,finance.nnprofitincrease);
            data.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_36,finance.nnprofitspeed);
            data.set(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_37,finance.nprofitincrease);
        }

        var sfKey=job.SF[0];
        this.SectionFinanceData.set(sfKey,data);
    }

    this.GetSectionFinanceCacheData=function(year,quarter,fieldName,node)
    {
        var period=JS_EXECUTE_JOB_ID.GetSectionReportPeriod(year,quarter);
        if (!period) this.Execute.ThrowUnexpectedNode(node,`不支持FS(${year}, ${quarter}, '${fieldName}') 报告期错误`);
        var id=JS_EXECUTE_JOB_ID.GetSectionFinanceID(fieldName);
        if (!id) this.Execute.ThrowUnexpectedNode(node,`不支持FS(${year}, ${quarter},'${fieldName}') 财务数据字段名称错误`);

        var sfKey=period.Year+'-'+period.Quarter;
        if (!this.SectionFinanceData.has(sfKey) && !this.ThrowSFPeirod.has(sfKey)) //动态下载的数据, 抛异常以后重新下载执行
        {
            this.ThrowSFPeirod.add(sfKey);  //抛过的异常就不抛了
            var job={ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_SF, SF:[sfKey, {Period:period, Fields:new Set([id])}]};
            this.Execute.ThrowDownloadSF(node, job, `FS(${year}, ${quarter}, '${fieldName}') 动态下载`);
        }

        if (!this.SectionFinanceData.has(sfKey)) 
            return this.Execute.ThrowUnexpectedNode(node,`不支持FS(${year}, ${quarter}, '${fieldName}') 没有这期财务数据`);

        var financeData=this.SectionFinanceData.get(sfKey);
        if (!financeData.has(id)) this.Execute.ThrowUnexpectedNode(node,`不支持FS(${year}, ${quarter}, '${fieldName}') 没有这期财务数据字段`);

        return financeData.get(id);
    }

    this.DownloadCustomVariantData=function(job)
    {
        var item=g_JSComplierResource.CustomVariant.Data.get(job.Name);
        if (!item || !item.Download) return this.Execute.RunNextJob();

        var self=this;
        var obj={ Name:job.Name, Symbol:this.Symbol, KData:this.Data, Period:this.Period, Right:this.Right, 
            Success: function() { self.Execute.RunNextJob() } };

        item.Download(obj);
    }

    this.DownloadCustomFunctionData=function(job)
    {
        var item=g_JSComplierResource.CustomFunction.Data.get(job.Name);
        if (!item || !item.Download) return this.Execute.RunNextJob();

        var self=this;
        var obj={ Symbol:this.Symbol, KData:this.Data, Period:this.Period, Right:this.Right, Args:job.Args, Name:job.Name,
            Success: function() { self.Execute.RunNextJob() } };

        item.Download(obj);
    }

    this.JsonDataToHistoryData=function(data)
    {
        var list = data.data;
        var aryDayData=new Array();
        var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7;
        var up=8,down=9,stop=10,unchanged=11;
        for (var i = 0; i < list.length; ++i)
        {
            var item = new HistoryData();

            item.Date = list[i][date];
            item.Open = list[i][open];
            item.YClose = list[i][yclose];
            item.Close = list[i][close];
            item.High = list[i][high];
            item.Low = list[i][low];
            item.Vol = list[i][vol];    //原始单位股
            item.Amount = list[i][amount];
            //上涨 下跌家数
            if (list[i].length>up) item.Up=list[i][up];
            if (list[i].length>down) item.Down=list[i][down];
            if (list[i].length>stop) item.Stop=list[i][stop];
            if (list[i].length>unchanged) item.Unchanged=list[i][unchanged];

            if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除

            aryDayData.push(item);
        }

        return aryDayData;
    }

    this.JsonDataToMinuteHistoryData=function(data)
    {
        var list = data.data;
        var aryDayData=new Array();
        var date = 0, yclose = 1, open = 2, high = 3, low = 4, close = 5, vol = 6, amount = 7, time = 8;
        for (var i = 0; i < list.length; ++i)
        {
            let item = new HistoryData();

            item.Date = list[i][date];
            item.Open = list[i][open];
            item.YClose = list[i][yclose];
            item.Close = list[i][close];
            item.High = list[i][high];
            item.Low = list[i][low];
            item.Vol = list[i][vol];    //原始单位股
            item.Amount = list[i][amount];
            item.Time=list[i][time];

        // if (isNaN(item.Open) || item.Open<=0) continue; //停牌的数据剔除
            aryDayData.push(item);
        }

        // 无效数据处理
        for(let i = 0; i < aryDayData.length; ++i)
        {
            var minData = aryDayData[i];
            if (minData == null) coninue;
            if (isNaN(minData.Open) || minData.Open <= 0 || isNaN(minData.High) || minData.High <= 0 || isNaN(minData.Low) || minData.Low <= 0 
                || isNaN(minData.Close) || minData.Close <= 0 || isNaN(minData.YClose) || minData.YClose <= 0)
            {
                if (i == 0)
                {
                    if (minData.YClose > 0)
                    {
                        minData.Open = minData.YClose;
                        minData.High = minData.YClose;
                        minData.Low = minData.YClose;
                        minData.Close = minData.YClose;
                    }
                }
                else // 用前一个有效数据填充
                {
                    for(let j = i-1; j >= 0; --j)
                    {
                        var minData2 = aryDayData[j];
                        if (minData2 == null) coninue;
                        if (minData2.Open > 0 && minData2.High > 0 && minData2.Low > 0 && minData2.Close > 0)
                        {
                            if (minData.YClose <= 0) minData.YClose = minData2.Close;
                            minData.Open = minData2.Open;
                            minData.High = minData2.High;
                            minData.Low = minData2.Low;
                            minData.Close = minData2.Close;
                            break;
                        }
                    }
                }    
            }
        }
        return aryDayData;
    }

    //API 返回数据 转化为array[]
    this.JsonDataToMinuteData=function(data)
    {
        var aryMinuteData=new Array();
        for(var i in data.stock[0].minute)
        {
            var jsData=data.stock[0].minute[i];
            var item=new MinuteData();

            item.Close=jsData.price;
            item.Open=jsData.open;
            item.High=jsData.high;
            item.Low=jsData.low;
            item.Vol=jsData.vol; //股
            item.Amount=jsData.amount;
            if (i==0)      //第1个数据 写死9:25
                item.DateTime=data.stock[0].date.toString()+" 0925";
            else
                item.DateTime=data.stock[0].date.toString()+" "+jsData.time.toString();
            item.Date=data.stock[0].date;
            item.Time=jsData.time;
            item.Increase=jsData.increase;
            item.Risefall=jsData.risefall;
            item.AvPrice=jsData.avprice;

            aryMinuteData[i]=item;
        }

        return aryMinuteData;
    }

    //多日日线数据API 转化成array[];
    this.JsonDataToMultiDayMinuteData=function(data)
    {
        var symbol=data.symbol;
        var upperSymbol=symbol.toUpperCase();
        var isSHSZ=MARKET_SUFFIX_NAME.IsSHSZ(upperSymbol);
        var isFutures=MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol);
        var result=[];
        for(var i in data.data)
        {
            var minuteData=[];
            var dayData=data.data[i];
            var date=dayData.date;
            var yClose=dayData.yclose;  //前收盘 计算涨幅
            var preClose=yClose;        //前一个数据价格
            var preAvPrice=null;           //上一个均价
            //var preAvPrice=data.stock[0].yclose;    //前一个均价
            for(var j in dayData.minute)
            {
                var jsData=dayData.minute[j];
                if (jsData[2]) preClose=jsData[2];  //保存上一个收盘数据
                var item=new MinuteData();
                item.Close=jsData[2];
                item.Open=jsData[1];
                item.High=jsData[3];
                item.Low=jsData[4];
                item.Vol=jsData[5]/100; //原始单位股
                item.Amount=jsData[6];
                if (7<jsData.length && jsData[7]>0) 
                {
                    item.AvPrice=jsData[7];    //均价
                    preAvPrice=jsData[7];
                }
                item.DateTime=date.toString()+" "+jsData[0].toString();
                item.Date=date
                item.Time=jsData[0];
                
                if (!item.Close)    //当前没有价格 使用上一个价格填充
                {
                    item.Close=preClose;   
                    item.Open=item.High=item.Low=item.Close;
                }

                if (!item.AvPrice && preAvPrice) item.AvPrice=preAvPrice;

                if (item.Close && yClose) item.Increase = (item.Close - yClose)/yClose*100;
                else item.Increase=null;
                if (j==0)      //第1个数据 写死9:25
                {
                    if (isSHSZ) item.DateTime=date.toString()+" 0925";
                    item.IsFristData=true;
                }

                //价格是0的 都用空
                if (item.Open<=0) item.Open=null;
                if (item.Close<=0) item.Close=null;
                if (item.AvPrice<=0) item.AvPrice=null;
                if (item.High<=0) item.High=null;
                if (item.Low<=0) item.Low=null;
                if (item.AvPrice<=0) item.AvPrice=null;

                minuteData[j]=item;
            }

            var newData=new ChartData();
            newData.Data=minuteData;
            newData.YClose=yClose;
            newData.Close=dayData.close;
            newData.Date=date;

            result.push(newData);
        }

        var minuteData=[];
        for(var i=result.length-1; i>=0;--i)
        {
            var item=result[i];
            for(var j in item.Data)
            {
                minuteData.push(item.Data[j]);
            }
        }
        
        return minuteData;
    }

    //CODELIKE 模糊股票代码
    this.CODELIKE=function(value)
    {
        if (this.Symbol && this.Symbol.indexOf(value)==0) return 1;
        return 0;
    }

    this.NAMELIKE=function(value)
    {
        if (this.Name && this.Name.indexOf(value)==0) return 1;
        return 0;
    }

    /*
    SETCODE 市场类型
    0:深圳 1:上海,47:中金所期货 28:郑州商品 29:大连商品 30:上海商品,27:香港指数 31:香港主板,48:香港创业板... 
    */
    this.SETCODE=function()
    {
        if (this.Symbol.indexOf('.sh')) return 1;
        if (this.Symbol.indexOf('.sz')) return 0;

        return 0;
    }

    this.GetSymbol=function()
    {
        return this.Symbol;
    }

    this.GetName=function()
    {
        return this.Name;
    }

    this.GetIndustry=function()
    {
        var key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GROUP_DATA.toString()+'-Group-'+this.Symbol;
        if (!this.ExtendData.has(key)) return '';

        var group=this.ExtendData.get(key);
        if (!group.Industry || group.Industry.length<=0) return '';

        var result='';
        for(var i in group.Industry)
        {
            var item=group.Industry[i];
            if (result.length>0) result+=' ';
            result+=item.Name;
        }

        return result;
    }
    
    this.GetRegion=function()
    {
        var key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GROUP_DATA.toString()+'-Group-'+this.Symbol;
        if (!this.ExtendData.has(key)) return '';

        var group=this.ExtendData.get(key);
        if (!group.Region || group.Region.length<=0) return '';

        var result='';
        for(var i in group.Region)
        {
            var item=group.Region[i];
            if (result.length>0) result+=' ';
            result+=item.Name;
        }

        return result;
    }

    this.GetConcept=function()
    {
        var key=JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GROUP_DATA.toString()+'-Group-'+this.Symbol;
        if (!this.ExtendData.has(key)) return '';

        var group=this.ExtendData.get(key);
        if (!group.Concept || group.Concept.length<=0) return '';

        var result='';
        for(var i in group.Concept)
        {
            var item=group.Concept[i];
            if (result.length>0) result+=' ';
            result+=item.Name;
        }

        return result;
    }
   
    this.DATE=function()
    {
        var result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for(let i in this.Data.Data)
        {
            var item=this.Data.Data[i];
            result[i]=item.Date;
        }

        return result;
    }

    this.YEAR=function()
    {
        var result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for(let i in this.Data.Data)
        {
            var item=this.Data.Data[i];
            if (this.IsNumber(item.Date))
                result[i]=parseInt(item.Date/10000);
            else
                result[i]=null;
        }

        return result;
    }

    this.MONTH=function()
    {
        var result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for(let i in this.Data.Data)
        {
            var item=this.Data.Data[i];
            if (this.IsNumber(item.Date))
                result[i]=parseInt(item.Date%10000/100);
            else
                result[i]=null;
        }

        return result;
    }

    //星期 1-7
    this.WEEK=function()
    {
        var result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        var tempDate=new Date();
        for(let i in this.Data.Data)
        {
            var item=this.Data.Data[i];
            result[i]=null;
            if (!this.IsNumber(item.Date)) continue;

            var year=parseInt(item.Date/10000);
            var month=parseInt(item.Date%10000/100);
            var day=item.Date%100;
            
            tempDate.setFullYear(year);
            tempDate.setMonth(month-1);
            tempDate.setDate(day);

            result[i]=tempDate.getDay();
        }

        return result;
    }

    this.REFDATE=function(data,date)
    {
        var result=null;
        var index=null;
        for(let i in this.Data.Data)   //查找日期对应的索引
        {
            if (this.Data.Data[i].Date==date) 
            {
                index=parseInt(i);
                break;
            }
        }

        if (index==null || index>=data.length) return null;

        return data[index];
    }

    //用法:结果从0到11,依次分别是1/5/15/30/60分钟,日/周/月,多分钟,多日,季,年
    this.PERIOD=function()
    {
        //Period周期 0=日线 1=周线 2=月线 3=年线 9=季线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟
        const PERIOD_MAP=[5,6,7,11, 0,1,2,3,4,5, 9];
        if (this.Period>=0 && this.Period<=PERIOD_MAP.length-1)
            return PERIOD_MAP[this.Period];
        
        return this.Period;
    } 

    this.GetDrawNull=function()
    {
        var result=[];
        if (!this.Data || !this.Data.Data || !this.Data.Data.length) return result;

        for(let i in this.Data.Data)
        {
            result[i]=null;
        }

        return result;
    }

}

//是否有是有效的数字
JSSymbolData.prototype.IsNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;

    return true;
}

JSSymbolData.prototype.IsDivideNumber=function(value)
{
    if (value==null) return false;
    if (isNaN(value)) return false;
    if (value==0) return false;

    return true;
}

JSSymbolData.prototype.JsonDataToFinance=function(data)
{
    var financeData;

    for(let i=1;i<=4;++i)
    {
        switch(i)
        {
            case 1:
                var finance=data.finance1;
                var announcement=data.announcement1;
                break;
            case 2:
                var finance=data.finance2;
                var announcement=data.announcement2;
                break;
            case 3:
                var finance=data.finance3;
                var announcement=data.announcement3;
                break;
            case 4:
                var finance=data.finance4;
                var announcement=data.announcement4;
                break;
            default:
                break;
        }

        if (!finance || !announcement || !this.IsNumber(announcement.year) || !this.IsNumber(announcement.quarter)) continue;
        if (financeData)    //如果存在1天公布多个报告期数据 只取最新的一个公告期数据
        {
            if (financeData.Announcement.year<announcement.year)
                financeData={Date:data.date, Finance:finance, Announcement:announcement};
        }
        else
        {
            financeData={Date:data.date, Finance:finance, Announcement:announcement};
        }

    }

    return financeData;
}

var JS_EXECUTE_DEBUG_LOG=false;

var JS_EXECUTE_JOB_ID=
{
    JOB_DOWNLOAD_SYMBOL_DATA:1, //下载股票的K线数据
    JOB_DOWNLOAD_INDEX_DATA:2,  //下载大盘的K线数据
    JOB_DOWNLOAD_SYMBOL_LATEST_DATA:3,  //最新的股票行情数据
    JOB_DOWNLOAD_INDEX_INCREASE_DATA:4, //涨跌股票个数统计数据
    JOB_DOWNLOAD_VOLR_DATA:5,           //5日量比均量下载量比数据
    JOB_DOWNLOAD_GROUP_DATA:6,          //所属行业|地区|概念
    JOB_DOWNLOAD_SYMBOL_DAY_DATA:7,     //股票日线数据
    JOB_DOWNLOAD_LATEST_INDEX_DATA:8,   //下载最新大盘数据

    //财务函数
    JOB_DOWNLOAD_TOTAL_EQUITY_DATA:100,          //总股本(万股)
    JOB_DOWNLOAD_FLOW_EQUITY_DATA:101,           //流通股本(万股)
    JOB_DOWNLOAD_PER_U_PROFIT_DATA:102,          //每股未分配利润
    JOB_DOWNLOAD_PER_NETASSET_DATA:103,          //每股净资产
    JOB_DOWNLOAD_PER_C_RESERVE_DATA:104,         //每股资本公积金
    JOB_DOWNLOAD_PER_S_EARNING_DATA:105,         //每股收益 
    JOB_DOWNLOAD_PER_S_EARNING2_DATA:106,        //每股收益(折算为全年收益),对于沪深品种有效
    JOB_DOWNLOAD_RELEASE_DATE_DATA:107,          //上市的天数
    JOB_DOWNLOAD_N_PROFIT_DATA:108,              //净利润
    JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:109,      //流通市值
    JOB_DOWNLOAD_MARKETVALUE_DATA:110,           //总市值
    JOB_DOWNLOAD_PROFIT_YOY_DATA:111,            //利润同比 (Profit year on year)
    JOB_DOWNLOAD_AL_RATIO_DATA:112,              //资产负债率 (asset-liability ratio)
    JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:113,        //股息率
    JOB_DOWNLOAD_SHAREHOLDER_DATA:114,           //股东人数    


    JOB_DOWNLOAD_CAPITAL_DATA:200,               //流通股本(手)
    JOB_DOWNLOAD_EXCHANGE_DATA:201,              //换手率 成交量/流通股本*100

   
    JOB_DOWNLOAD_MARGIN_BALANCE:1000,           //融资融券余额
    JOB_DOWNLOAD_MARGIN_RATE:1001,              //融资占比

    JOB_DOWNLOAD_MARGIN_BUY_BALANCE:1010,       //买入信息-融资余额
    JOB_DOWNLOAD_MARGIN_BUY_AMOUNT:1011,        //买入信息-买入额
    JOB_DOWNLOAD_MARGIN_BUY_REPAY:1012,         //买入信息-偿还额
    JOB_DOWNLOAD_MARGIN_BUY_NET:1013,           //买入信息-融资净买入

    JOB_DOWNLOAD_MARGIN_SELL_BALANCE:1020,      //卖出信息-融券余量
    JOB_DOWNLOAD_MARGIN_SELL_VOLUME:1021,       //卖出信息-卖出量
    JOB_DOWNLOAD_MARGIN_SELL_REPAY:1022,        //卖出信息-偿还量
    JOB_DOWNLOAD_MARGIN_SELL_NET:1023,          //卖出信息-融券净卖出

    JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE:2000,             //负面新闻统计
    JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH:2001,             //机构调研
    JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT:2002,             //互动易
    JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE:2003,         //股东增持
    JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2:2004,        //股东减持
    JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER:2005,          //信托持股
    JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING:2006,         //大宗交易
    JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS:2007,          //官网新闻
    JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS:2008,          //高管要闻
    JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE:2009,               //股权质押

    JOB_DOWNLOAD_HK_TO_SH:2050,      //北上流入上证
    JOB_DOWNLOAD_HK_TO_SZ:2051,      //北上流入深证
    JOB_DOWNLOAD_HK_TO_SH_SZ:2052,   //北上流总的

    JOB_CUSTOM_FUNCTION_DATA:6000,       //自定义函数
    JOB_CUSTOM_VARIANT_DATA:6001,        //自定义变量
    //截面数据
    //财务数据 SF(公告期,数据名称)   如: SF(201901,"流动资产");
    JOB_DOWNLOAD_SECTION_SF:20000,

    JOB_DOWNLOAD_SECTION_F_01:20001,    //currentassets 流动资产
    JOB_DOWNLOAD_SECTION_F_02:20002,    //monetaryfunds 货币资金
    JOB_DOWNLOAD_SECTION_F_03:20003,    //inventory 存货
    JOB_DOWNLOAD_SECTION_F_04:20004,    //currentliabilities 流动负债
    JOB_DOWNLOAD_SECTION_F_05:20005,    //ncurrentliabilities 非流动负债
    JOB_DOWNLOAD_SECTION_F_06:20006,    //3expenses 三项费用
    JOB_DOWNLOAD_SECTION_F_07:20007,    //investmentincome 投资收益
    JOB_DOWNLOAD_SECTION_F_08:20008,    //pcnprofit 归母净利润
    JOB_DOWNLOAD_SECTION_F_09:20009,    //nnetprofit 扣非净利润
    JOB_DOWNLOAD_SECTION_F_10:20010,    //npersearning 扣非每股收益
    JOB_DOWNLOAD_SECTION_F_11:20011,    //woewa 加权平均净资产收益
    JOB_DOWNLOAD_SECTION_F_12:20012,    //inprocess 在建工程
    JOB_DOWNLOAD_SECTION_F_13:20013,    //accdepreciation 累计折旧
    JOB_DOWNLOAD_SECTION_F_14:20014,    //mholderprofit 少数股东利润
    JOB_DOWNLOAD_SECTION_F_15:20015,    //lossexchange 汇兑损益
    JOB_DOWNLOAD_SECTION_F_16:20016,    //baddebts 坏账计提
    JOB_DOWNLOAD_SECTION_F_17:20017,    //fixedassets 固定资产
    JOB_DOWNLOAD_SECTION_F_18:20018,    //curdepreciation 当期折旧
    JOB_DOWNLOAD_SECTION_F_19:20019,    //orevenues 营业总收入
    JOB_DOWNLOAD_SECTION_F_20:20020,    //moprofit 主营业务利润
    JOB_DOWNLOAD_SECTION_F_21:20021,    //oprofit 营业利润
    JOB_DOWNLOAD_SECTION_F_22:20022,    //nprofit 净利润
    JOB_DOWNLOAD_SECTION_F_23:20023,    //areceivable 应收账款
    JOB_DOWNLOAD_SECTION_F_24:20024,    //financialcost 财务费用
    JOB_DOWNLOAD_SECTION_F_25:20025,    //ccfo 经营性现金流
    JOB_DOWNLOAD_SECTION_F_26:20026,    //totalassets 资产总计
    JOB_DOWNLOAD_SECTION_F_27:20027,    //totalliabilities 负债总计
    JOB_DOWNLOAD_SECTION_F_28:20028,    //totalownersequity 所有者权益总计
    JOB_DOWNLOAD_SECTION_F_29:20029,    //grossmargin 毛利率
    JOB_DOWNLOAD_SECTION_F_30:20030,    //percreserve 每股资本公积金
    JOB_DOWNLOAD_SECTION_F_31:20031,    //peruprofit 每股未分配利润
    JOB_DOWNLOAD_SECTION_F_32:20032,    //persearning 每股收益
    JOB_DOWNLOAD_SECTION_F_33:20033,    //pernetasset 每股净资产
    JOB_DOWNLOAD_SECTION_F_34:20034,    //perccfo 每股经营性现金流
    JOB_DOWNLOAD_SECTION_F_35:20035,    //nnprofitincrease finance4特有,扣非净利润涨幅
    JOB_DOWNLOAD_SECTION_F_36:20036,    //nnprofitspeed finance4特有,扣非净利润涨速
    JOB_DOWNLOAD_SECTION_F_37:20037,    //nprofitincrease finance4特有,净利润涨幅
    JOB_DOWNLOAD_SECTION_F_38:20038,    //alratio 资产负债率(数值乘以100)
    JOB_DOWNLOAD_SECTION_F_39:20039,    //profityoy 利润同比%(数值乘以100)

    

    JOB_RUN_SCRIPT:10000, //执行脚本

    GetFinnanceJobID:function(value)
    {
        let dataMap=new Map([
            [1,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA],       //FINANCE(1)   总股本(万股)
            [7,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA],        //FINANCE(7)   流通股本(万股)
            [9,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA],           //FINANCE(9)   资产负债率 (asset-liability ratio)
            [18,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA],     //FINANCE(18)  每股公积金
            [30,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA],          //FINANCE(30)  净利润
            [32,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA],      //FINANCE(32)  每股未分配利润
            [33,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA],    //FINANCE(33)  每股收益(折算为全年收益),对于沪深品种有效
            [34,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA],      //FINANCE(34)  每股净资产
            [38,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA],     //FINANCE(38)  每股收益(最近一期季报)
            [40,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA],  //FINANCE(40)  流通市值 
            [41,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA],       //FINANCE(41)  总市值
            [42,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_RELEASE_DATE_DATA],      //FINANCE(42)  上市的天数
            [43,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA],        //FINANCE(43)  利润同比 (Profit year on year)
            [45,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA],    //FINANCE(45)  股息率

            [100,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA],      //FINANCE(100) 股东人数

            [200,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA],          //流通股本(手)
            [201,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA]          //换手率 成交量/流通股本
           
        ]);
    
        if (dataMap.has(value)) return dataMap.get(value);
    
        return null;
    },

    //融资融券
    GetMarginJobID:function(value)
    {
        let dataMap=new Map([
            [1,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE],          //MARGIN(1)   融资融券余额
            [2,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE],             //MARGIN(2)   融资占比

            [3,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE],       //MARGIN(3)   买入信息-融资余额
            [4,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT],        //MARGIN(4)   买入信息-买入额
            [5,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY],         //MARGIN(5)   买入信息-偿还额
            [6,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET],           //MARGIN(6)   买入信息-融资净买入

            [7,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE],      //MARGIN(7)   卖出信息-融券余量
            [8,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME],       //MARGIN(8)   卖出信息-卖出量
            [9,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY],        //MARGIN(9)   卖出信息-偿还量
            [10,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET],         //MARGIN(10)  卖出信息-融券净卖出 
        ]);
    
        if (dataMap.has(value)) return dataMap.get(value);
    
        return null;
    },

    //北上资金
    GetHK2SHSZJobID:function(value)
    {
        let dataMap=new Map([
            [1,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ],          //HK2SHSZ(1)   北上流总的
            [2,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH],             //HK2SHSZ(2)   北上流入上证
            [3,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ],             //HK2SHSZ(3)   北上流入深证
        ]);
    
        if (dataMap.has(value)) return dataMap.get(value);
    
        return null;
    },

    GetNewsAnalysisID:function(value)
    {
        let dataMap=new Map([
            [1,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE],          //NEWS(1)   负面新闻统计
            [2,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH],          //NEWS(2)   机构调研统计
            [3,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT],          //NEWS(3)   互动易
            [4,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE],      //NEWS(4)   股东增持
            [5,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2],     //NEWS(5)   股东减持
            [6,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER],       //NEWS(6)   信托持股
            [7,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING],      //NEWS(7)   大宗交易
            [8,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS],       //NEWS(8)   官网新闻
            [9,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS],       //NEWS(9)   高管要闻
            [10,JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE],           //NEWS(10)  股权质押    
        ]);

        if (dataMap.has(value)) return dataMap.get(value);
    
        return null;
    },

    //财务截面数据 分报告期
    GetSectionFinanceID:function(value)
    {
        let dataMap=new Map([
            ['流动资产', JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_01],
            ['货币资金', JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_02],
            ['存货', JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_03],
            ['流动负债', JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_04],
            ['非流动负债',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_05],
            ['三项费用',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_06],
            ['投资收益',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_07],
            ['归母净利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_08],
            ['扣非净利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_09],
            ['扣非每股收益',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_10],
            ['加权平均净资产收益',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_11],
            ['在建工程',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_12],
            ['累计折旧',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_13],
            ['少数股东利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_14],
            ['汇兑损益',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_15],
            ['坏账计提',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_16],
            ['固定资产',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_17],
            ['当期折旧',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_18],
            ['营业总收入',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_19],
            ['主营业务利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_20],
            ['营业利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_21],
            ['净利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_22],
            ['应收账款',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_23],
            ['财务费用',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_24],
            ['经营性现金流',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_25],
            ['资产总计',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_26],
            ['负债总计',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_27],
            ['所有者权益总计',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_28],
            ['毛利率',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_29],
            ['每股资本公积金',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_30],
            ['每股未分配利润',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_31],
            ['每股收益',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_32],
            ['每股净资产',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_33],
            ['每股经营性现金流',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_34],
            ['扣非净利润涨幅',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_35],
            ['扣非净利润涨速',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_36],
            ['净利润涨幅',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_37],
            ['资产负债率',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_38],
            ['利润同比',JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_F_39],
        ]);

        if (dataMap.has(value)) return dataMap.get(value);

        return null;
    },

    //获取报告期 2018, 1 
    GetSectionReportPeriod:function(year,quarter)  
    {
        if (year>=2000 && quarter>=1 && quarter<=4) 
            return { Year:year, Quarter:quarter };
        return null;
    }

};

function JSExecute(ast,option)
{
    this.AST=ast;   //语法树

    this.ErrorHandler=new ErrorHandler();
    this.VarTable=new Map();        //变量表
    this.OutVarTable=[];   //输出变量
    this.Arguments=[];
    this.ErrorCallback;             //执行错误回调

    //脚本自动变量表, 只读
    this.ConstVarTable=new Map([
        //个股数据
        ['CLOSE',null],['VOL',null],['OPEN',null],['HIGH',null],['LOW',null],['AMOUNT',null],
        ['C',null],['V',null],['O',null],['H',null],['L',null],['VOLR',null],

        //日期类
        ['DATE',null],['YEAR',null],['MONTH',null],['PERIOD', null],['WEEK',null],

        //大盘数据
        ['INDEXA',null],['INDEXC',null],['INDEXH',null],['INDEXL',null],['INDEXO',null],['INDEXV',null],
        ['INDEXADV',null],['INDEXDEC',null],

        ['FROMOPEN',null],  //已开盘有多长分钟

        ['CURRBARSCOUNT',null], //到最后交易日的周期数
        ['ISLASTBAR',null],     //判断是否为最后一个周期

        ['CAPITAL',null],   //流通股本(手)
        ['EXCHANGE',null],   //换手率
        ['SETCODE', null],  //市场类型
        ['CODE',null],      //品种代码
        ['STKNAME',null],   //品种名称

        ['HYBLOCK',null],   //所属行业板块
        ['DYBLOCK',null],   //所属地域板块
        ['GNBLOCK',null],    //所属概念

        ['DRAWNULL',null]

    ]);   

    this.SymbolData=new JSSymbolData(this.AST,option,this);
    this.Algorithm=new JSAlgorithm(this.ErrorHandler,this.SymbolData);
    this.Draw=new JSDraw(this.ErrorHandler,this.SymbolData);
    
    this.JobList=[];            //执行的任务队列

    this.UpdateUICallback=null; //回调
    this.CallbackParam=null;
    this.IsSectionMode=false;

    if (option)
    {
        if (option.Callback) this.UpdateUICallback=option.Callback;
        if (option.CallbackParam) this.CallbackParam=option.CallbackParam;
        if (option.Arguments) this.Arguments=option.Arguments;
        if (option.IsSectionMode) this.IsSectionMode=option.IsSectionMode;
    }

    this.Execute=function()
    {
        JSConsole.Complier.Log('[JSExecute::Execute] JobList', this.JobList);
        this.OutVarTable=[];
        this.VarTable=new Map();
        this.RunNextJob();
    }

    this.RunNextJob=function()
    {
        if (this.JobList.length<=0) return;

        var jobItem=this.JobList.shift();

        switch(jobItem.ID)
        {
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA:
                return this.SymbolData.GetSymbolData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DAY_DATA:
                return this.SymbolData.GetSymbolDayData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_DATA:
                return this.SymbolData.GetIndexData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_LATEST_INDEX_DATA:
                return this.SymbolData.GetLatestIndexData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_INDEX_INCREASE_DATA:
                return this.SymbolData.GetIndexIncreaseData(jobItem);
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_LATEST_DATA:
                return this.SymbolData.GetLatestData();
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_VOLR_DATA:  //量比
                return this.SymbolData.GetVolRateData(jobItem);
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_GROUP_DATA:
                return this.SymbolData.GetGroupData(jobItem);   //行业|概念|地区

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_TOTAL_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_EQUITY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_NETASSET_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_U_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_C_RESERVE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PER_S_EARNING2_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_N_PROFIT_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_FLOW_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARKETVALUE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_AL_RATIO_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_PROFIT_YOY_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_DIVIDEND_YIELD_DATA:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SHAREHOLDER_DATA:
                return this.SymbolData.GetFinanceData(jobItem.ID);
            
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_RELEASE_DATE_DATA:
                return this.SymbolData.GetCompanyReleaseDate(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BALANCE:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_RATE:
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_BALANCE:       //买入信息-融资余额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_AMOUNT:        //买入信息-买入额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_REPAY:         //买入信息-偿还额
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_BUY_NET:           //买入信息-融资净买入
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_BALANCE:      //卖出信息-融券余量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_VOLUME:       //卖出信息-卖出量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_REPAY:        //卖出信息-偿还量
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_MARGIN_SELL_NET:          //卖出信息-融券净卖出
                return this.SymbolData.GetMarginData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_NEGATIVE:             //负面新闻
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_RESEARCH:             //机构调研
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_INTERACT:             //互动易
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE:         //股东增持
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_HOLDERCHANGE2:        //股东减持
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TRUSTHOLDER:          //信托持股
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_BLOCKTRADING:         //大宗交易
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_COMPANYNEWS:          //官网新闻
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_TOPMANAGERS:          //高管要闻
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_NEWS_ANALYSIS_PLEDGE:               //股权质押
                return this.SymbolData.GetNewsAnalysisData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH_SZ:        //北上总的
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SH:           //北上上证
            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_HK_TO_SZ:           //北上深证
                return this.SymbolData.GetHKToSHSZData(jobItem.ID);

            case JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SECTION_SF:
                return this.SymbolData.GetSectionFinanceData(jobItem);   //财务截面报告数据

            case JS_EXECUTE_JOB_ID.JOB_CUSTOM_VARIANT_DATA:
                return this.SymbolData.DownloadCustomVariantData(jobItem);
            case JS_EXECUTE_JOB_ID.JOB_CUSTOM_FUNCTION_DATA:
                return this.SymbolData.DownloadCustomFunctionData(jobItem);

            case JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT:
                return this.Run();
        }
    }

    this.ReadSymbolData=function(name,node)
    {
        switch(name)
        {
            case 'CLOSE':
            case 'C':
            case 'VOL':
            case 'V':
            case 'OPEN':
            case 'O':
            case 'HIGH':
            case 'H':
            case 'LOW':
            case 'L':
            case 'AMOUNT':
                return this.SymbolData.GetSymbolCacheData(name);
            case 'VOLR':
                return this.SymbolData.GetVolRateCacheData(node);

            //大盘数据
            case 'INDEXA':
            case 'INDEXC':
            case 'INDEXH':
            case 'INDEXH':
            case 'INDEXO':
            case 'INDEXV':
            case 'INDEXL':
            case 'INDEXADV':
            case 'INDEXDEC':
                return this.SymbolData.GetIndexCacheData(name);
            case 'CURRBARSCOUNT':
                return this.SymbolData.GetCurrBarsCount();
            case 'ISLASTBAR':
                return this.SymbolData.GetIsLastBar();
            case 'CAPITAL':
                return this.SymbolData.GetFinanceCacheData(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_CAPITAL_DATA);
            case 'EXCHANGE':
                return this.SymbolData.GetFinanceCacheData(JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_EXCHANGE_DATA);
            case 'SETCODE':
                return this.SymbolData.SETCODE();
            case 'CODE':
                return this.SymbolData.GetSymbol();
            case 'STKNAME':
                return this.SymbolData.GetName();
            case 'HYBLOCK':
                return this.SymbolData.GetIndustry();
            case 'DYBLOCK':
                return this.SymbolData.GetRegion();
            case 'GNBLOCK':
                return this.SymbolData.GetConcept();

            case 'DATE':
                return this.SymbolData.DATE();
            case 'YEAR':
                return this.SymbolData.YEAR();
            case 'MONTH':
                return this.SymbolData.MONTH();
            case 'WEEK':
                return this.SymbolData.WEEK();
            case 'PERIOD':
                return this.SymbolData.PERIOD();
            case 'FROMOPEN':
                return this.SymbolData.GetLatestIndexCacheData('FROMOPEN');

            case 'DRAWNULL':
                return this.SymbolData.GetDrawNull();
        }
    }

    this.ReadCustomVariant=function(name,node)
    {
        var item=g_JSComplierResource.CustomVariant.Data.get(name);
        if (!item || !item.Read) return [];

        var obj={ Name:name, Symbol:this.SymbolData.Symbol, KData: this.SymbolData.Data, Period:this.SymbolData.Period, Right:this.SymbolData.Right };
        return item.Read(obj);
    }

    //读取变量
    this.ReadVariable=function(name,node)
    {
        if (this.ConstVarTable.has(name)) 
        {
            let data=this.ConstVarTable.get(name);

            if (data==null) //动态加载,用到再加载
            {
                data=this.ReadSymbolData(name,node);
                this.ConstVarTable.set(name,data);
            }

            return data;
        }

        if (g_JSComplierResource.IsCustomVariant(name)) return this.ReadCustomVariant(name,node); //读取自定义变量

        if (this.VarTable.has(name)) return this.VarTable.get(name);

        this.ThrowUnexpectedNode(node, '变量'+name+'不存在');
        return null;
    }

    //单数据转成数组 个数和历史数据一致
    this.SingleDataToArrayData=function(value)
    {
        let count=this.SymbolData.Data.Data.length;
        let result=[];
        for(let i=0;i<count;++i)
        {
            result[i]=value;
        }

        return result;
    }

    this.RunAST=function()
    {
        //预定义的变量
        for(let i in this.Arguments)
        {
            let item =this.Arguments[i];
            this.VarTable.set(item.Name,item.Value);
        }

        if (!this.AST) this.ThrowError();
        if (!this.AST.Body) this.ThrowError();

        for(let i in this.AST.Body)
        {
            let item =this.AST.Body[i];
            this.VisitNode(item);

            //输出变量
            if (item.Type==Syntax.ExpressionStatement && item.Expression)
            {
                if (item.Expression.Type==Syntax.AssignmentExpression && item.Expression.Operator==':' && item.Expression.Left)
                {
                    let assignmentItem=item.Expression;
                    let varName=assignmentItem.Left.Name;
                    let outVar=this.VarTable.get(varName);
                    if (!this.IsSectionMode && !Array.isArray(outVar)) 
                    {
                        if (typeof(outVar)=='string') outVar=this.SingleDataToArrayData(parseFloat(outVar));
                        else outVar=this.SingleDataToArrayData(outVar);
                    }

                    this.OutVarTable.push({Name:varName, Data:outVar,Type:0});
                }
                else if (item.Expression.Type==Syntax.CallExpression)
                {
                    let callItem=item.Expression;
                    if (this.Draw.IsDrawFunction(callItem.Callee.Name))
                    {
                        let draw=callItem.Draw;
                        draw.Name=callItem.Callee.Name;
                        this.OutVarTable.push({Name:draw.Name, Draw:draw, Type:1});
                    }
                }
                else if (item.Expression.Type==Syntax.SequenceExpression)
                {
                    let varName;
                    let draw;
                    let color;
                    let lineWidth;
                    let colorStick=false;
                    let pointDot=false;
                    let circleDot=false;
                    let lineStick=false;
                    let stick=false;
                    let volStick=false;
                    let isShow=true;
                    let isExData=false;
                    let isDotLine=false;
                    for(let j in item.Expression.Expression)
                    {
                        let itemExpression=item.Expression.Expression[j];
                        if (itemExpression.Type==Syntax.AssignmentExpression && itemExpression.Operator==':' && itemExpression.Left)
                        {
                            varName=itemExpression.Left.Name;
                            let varValue=this.VarTable.get(varName);
                            if (!Array.isArray(varValue)) 
                            {
                                varValue=this.SingleDataToArrayData(varValue); 
                                this.VarTable.set(varName,varValue);            //把常量放到变量表里
                            } 
                        }
                        else if (itemExpression.Type==Syntax.Identifier)
                        {
                            let value=itemExpression.Name;
                            if (value==='COLORSTICK') colorStick=true;
                            else if (value==='POINTDOT') pointDot=true;
                            else if (value==='CIRCLEDOT') circleDot=true;
                            else if (value=='DOTLINE') isDotLine=true;
                            else if (value==='LINESTICK') lineStick=true;
                            else if (value==='STICK') stick=true;
                            else if (value==='VOLSTICK') volStick=true;
                            else if (value.indexOf('COLOR')==0) color=value;
                            else if (value.indexOf('LINETHICK')==0) lineWidth=value;
                            else if (value.indexOf('NODRAW')==0) isShow=false;
                            else if (value.indexOf('EXDATA')==0) isExData=true; //扩展数据, 不显示再图形里面
                        }
                        else if(itemExpression.Type==Syntax.Literal)    //常量
                        {
                            let aryValue=this.SingleDataToArrayData(itemExpression.Value);
                            varName=itemExpression.Value.toString();
                            this.VarTable.set(varName,aryValue);    //把常量放到变量表里
                        }
                        else if (itemExpression.Type==Syntax.CallExpression && this.Draw.IsDrawFunction(itemExpression.Callee.Name))
                        {
                            draw=itemExpression.Draw;
                            draw.Name=itemExpression.Callee.Name;
                        }
                    }

                    if (pointDot && varName)   //圆点
                    {
                        let outVar=this.VarTable.get(varName);
                        if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar);
                        let value={Name:varName, Data:outVar, Radius:2, Type:3};
                        if (color) value.Color=color;
                        if (lineWidth) value.LineWidth=lineWidth;
                        this.OutVarTable.push(value);
                    }
                    else if (circleDot && varName)  //圆点
                    {
                        let outVar=this.VarTable.get(varName);
                        if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar);
                        let value={Name:varName, Data:outVar, Radius:1.3, Type:3};
                        if (color) value.Color=color;
                        if (lineWidth) value.LineWidth=lineWidth;
                        this.OutVarTable.push(value);
                    }
                    else if (lineStick && varName)  //LINESTICK  同时画出柱状线和指标线
                    {
                        let outVar=this.VarTable.get(varName);
                        let value={Name:varName, Data:outVar, Type:4};
                        if (color) value.Color=color;
                        if (lineWidth) value.LineWidth=lineWidth;
                        this.OutVarTable.push(value);
                    }
                    else if (stick && varName)  //STICK 画柱状线
                    {
                        let outVar=this.VarTable.get(varName);
                        let value={Name:varName, Data:outVar, Type:5};
                        if (color) value.Color=color;
                        if (lineWidth) value.LineWidth=lineWidth;
                        this.OutVarTable.push(value);
                    }
                    else if (volStick && varName)   //VOLSTICK   画彩色柱状线
                    {
                        let outVar=this.VarTable.get(varName);
                        let value={Name:varName, Data:outVar, Type:6};
                        if (color) value.Color=color;
                        this.OutVarTable.push(value);
                    }
                    else if (varName && color) 
                    {
                        let outVar=this.VarTable.get(varName);
                        if (!Array.isArray(outVar)) outVar=this.SingleDataToArrayData(outVar);
                        let value={Name:varName, Data:outVar, Color:color, Type:0};
                        if (lineWidth) value.LineWidth=lineWidth;
                        if (isShow == false) value.IsShow = false;
                        if (isExData==true) value.IsExData = true;
                        if (isDotLine==true) value.IsDotLine=true;
                        this.OutVarTable.push(value);
                    }
                    else if (draw)  //画图函数
                    {
                        var outVar={Name:draw.Name, Draw:draw, Type:1};
                        if (color) outVar.Color=color;
                        if (lineWidth) outVar.LineWidth=lineWidth;
                        this.OutVarTable.push(outVar);
                    }
                    else if (colorStick && varName)  //CYW: SUM(VAR4,10)/10000, COLORSTICK; 画上下柱子
                    {
                        let outVar=this.VarTable.get(varName);
                        let value={Name:varName, Data:outVar, Color:color, Type:2};
                        this.OutVarTable.push(value);
                    }
                    else if (varName)
                    {
                        let outVar=this.VarTable.get(varName);
                        let value={Name:varName, Data:outVar,Type:0};
                        if (color) value.Color=color;
                        if (lineWidth) value.LineWidth=lineWidth;
                        if (isShow==false) value.IsShow=false;
                        if (isExData==true) value.IsExData = true;
                        if (isDotLine==true) value.IsDotLine=true;
                        this.OutVarTable.push(value);
                    }
                }
            }
        }

        JSConsole.Complier.Log('[JSExecute::Run]', this.VarTable);

        return this.OutVarTable;
    }

    this.Run=function()
    { 
        try
        {                       
            let data=this.RunAST();//执行脚本
            JSConsole.Complier.Log('[JSComplier.Run] execute finish', data);
        
            if (this.UpdateUICallback) 
            {
                JSConsole.Complier.Log('[JSComplier.Run] invoke UpdateUICallback.');
                if (this.CallbackParam && this.CallbackParam.Self && this.CallbackParam.Self.ClassName==='ScriptIndexConsole') this.CallbackParam.JSExecute=this;
                this.UpdateUICallback(data,this.CallbackParam);
            }
        }
        catch(error)
        {
            JSConsole.Complier.Log(error);
            if (error.Job)
            {
                JSConsole.Complier.Log('[JSComplier.Run] download job and reexectue', error.Job);
                this.JobList.push(error.Job);
                this.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT});
                this.Execute();
            }
            else if (this.ErrorCallback) 
            {
                this.ErrorCallback(error);
            }
        }
    }

    this.VisitNode=function(node)
    {
        switch(node.Type)
        {
            case Syntax.SequenceExpression:
                this.VisitSequenceExpression(node);
                break;
            case Syntax.ExpressionStatement:
                this.VisitNode(node.Expression);
                break;
            case Syntax.AssignmentExpression:
                this.VisitAssignmentExpression(node);
                break;
            case Syntax.BinaryExpression:
            case Syntax.LogicalExpression:
                this.VisitBinaryExpression(node);
                break;
            case Syntax.CallExpression:
                this.VisitCallExpression(node);
                break;
        }
    }

    this.VisitSequenceExpression=function(node)
    {
        for(let i in node.Expression)
        {
            let item =node.Expression[i];
            this.VisitNode(item);
        }
    }

    //函数调用
    this.VisitCallExpression=function(node)
    {
        let funcName=node.Callee.Name;
        let args=[];
        for(let i in node.Arguments)
        {
            let item=node.Arguments[i];
            let value;
            if (item.Type==Syntax.BinaryExpression || item.Type==Syntax.LogicalExpression) 
                value=this.VisitBinaryExpression(item);
            else if (item.Type==Syntax.CallExpression)
                value=this.VisitCallExpression(item);
            else
                value=this.GetNodeValue(item);
            args.push(value);
        }

        if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitCallExpression]' , funcName, '(', args.toString() ,')');

        switch(funcName)
        {
            case 'DYNAINFO':    //行情最新数据
                node.Out=this.SymbolData.GetLatestCacheData(args[0]);
                break;
            case 'STICKLINE':
                node.Draw=this.Draw.STICKLINE(args[0],args[1],args[2],args[3],args[4]);
                node.Out=[];
                break;
            case 'DRAWTEXT':
                node.Draw=this.Draw.DRAWTEXT(args[0],args[1],args[2]);
                node.Out=[];
                break;
            case 'DRAWTEXT_FIX':
                node.Draw=this.Draw.DRAWTEXT_FIX(args[0],args[1],args[2],args[3],args[4]);
                node.Out=[];
                break;
            case 'SUPERDRAWTEXT':
                node.Draw=this.Draw.SUPERDRAWTEXT(args[0],args[1],args[2],args[3],args[4]);
                node.Out=[];
                break;
            case 'DRAWICON':
                node.Draw=this.Draw.DRAWICON(args[0],args[1],args[2]);
                node.Out=[];
                break;
            case 'DRAWLINE':
                node.Draw=this.Draw.DRAWLINE(args[0],args[1],args[2],args[3],args[4]);
                node.Out=node.Draw.DrawData;
                break;
            case 'DRAWBAND':
                node.Draw=this.Draw.DRAWBAND(args[0],args[1],args[2],args[3]);
                node.Out=[];
                break;
            case 'DRAWKLINE':
                node.Draw=this.Draw.DRAWKLINE(args[0],args[1],args[2],args[3]);
                node.Out=[];
                break;
            case 'DRAWKLINE_IF':
                node.Draw=this.Draw.DRAWKLINE_IF(args[0],args[1],args[2],args[3],args[4]);
                node.Out=[];
                break;
            case 'PLOYLINE':
            case 'POLYLINE':
                node.Draw=this.Draw.POLYLINE(args[0],args[1]);
                node.Out=node.Draw.DrawData;
                break;
            case 'DRAWNUMBER':
                node.Draw=this.Draw.DRAWNUMBER(args[0],args[1],args[2]);
                node.Out=node.Draw.DrawData.Value;
                break;
            case "DRAWCHANNEL":
                node.Draw=this.Draw.DRAWCHANNEL(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
                node.Out=[];
                break;
            case 'RGB':
                node.Out=this.Draw.RGB(args[0],args[1],args[2]);
                break;
            case 'PARTLINE':
                node.Draw=this.Draw.PARTLINE(args);
                node.Out=[];
                break;
            case 'DRAWGBK':
                node.Draw=this.Draw.DRAWGBK(args[0],args[1],args[2],args[3]);
                node.Out=[];
                break;
            case 'DRAWTEXT_LINE':
                node.Draw=this.Draw.DRAWTEXT_LINE(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
                node.Out=[];
                break;
            case 'DRAWRECTREL':
                node.Draw=this.Draw.DRAWRECTREL(args[0],args[1],args[2],args[3],args[4]);
                node.Out=[];
                break;
            case 'CODELIKE':
                node.Out=this.SymbolData.CODELIKE(args[0]);
                break;
            case 'NAMELIKE':
                node.Out=this.SymbolData.NAMELIKE(args[1]);
                break;
            case 'REFDATE':
                node.Out=this.SymbolData.REFDATE(args[0],args[1]);
                break;
            case 'FINANCE':
                node.Out=this.SymbolData.GetFinanceCacheData(args[0],node);
                break;
            case "MARGIN":
                node.Out=this.SymbolData.GetMarginCacheData(args[0],node);
                break;
            case "HK2SHSZ":
                node.Out=this.SymbolData.GetHKToSHSZCacheData(args[0],node);
                break;
            case "NEWS":
                node.Out=this.SymbolData.GetNewsAnalysisCacheData(args[0],node);
                break;
            case 'UPCOUNT':
            case 'DOWNCOUNT':
                node.Out=this.SymbolData.GetIndexIncreaseCacheData(funcName,args[0],node);
                break;
            case 'SF':
                node.Out=this.SymbolData.GetSectionFinanceCacheData(args[0],args[1],args[2],node);
                break;
            default:
                node.Out=this.Algorithm.CallFunction(funcName, args, node, this.SymbolData);
                break;
        }

        return node.Out;
    }

    //赋值
    this.VisitAssignmentExpression=function(node)
    {
        let left=node.Left;
        if (left.Type!=Syntax.Identifier) this.ThrowUnexpectedNode(node);

        let varName=left.Name;

        let right=node.Right;
        let value=null;
        if (right.Type==Syntax.BinaryExpression || right.Type==Syntax.LogicalExpression)
            value=this.VisitBinaryExpression(right);
        else if (right.Type==Syntax.CallExpression)
            value=this.VisitCallExpression(right);
        else if (right.Type==Syntax.Literal)
            value=right.Value;
        else if (right.Type==Syntax.Identifier) //右值是变量
            value=this.ReadVariable(right.Name,right);

        if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitAssignmentExpression]' , varName, ' = ',value);
        this.VarTable.set(varName,value);
    }

    //逻辑运算
    this.VisitBinaryExpression=function(node)
    {
        let stack=[];
        stack.push(node);
        let temp=null;

        while(stack.length!=0)
        {
            temp=stack[stack.length-1];
            if (temp.Left && node!=temp.Left && node!=temp.Right)
            {
                stack.push(temp.Left);
            }
            else if (temp.Right && node!=temp.Right)
            {
                stack.push(temp.Right);
            }
            else
            {
                let value=stack.pop();
                if (value.Type==Syntax.BinaryExpression)    //只遍历操作符就可以
                {
                    let leftValue=this.GetNodeValue(value.Left);
                    let rightValue=this.GetNodeValue(value.Right);

                    if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value , leftValue, rightValue);
                    value.Out=null; //保存中间值

                    switch(value.Operator)
                    {
                        case '-':
                            value.Out=this.Algorithm.Subtract(leftValue,rightValue);
                            break;
                        case '*':
                            value.Out=this.Algorithm.Multiply(leftValue,rightValue);
                            break;
                        case '/':
                            value.Out=this.Algorithm.Divide(leftValue,rightValue)
                            break;
                        case '+':
                            value.Out=this.Algorithm.Add(leftValue,rightValue);
                            break;
                        case '>':
                            value.Out=this.Algorithm.GT(leftValue,rightValue);
                            break;
                        case '>=':
                            value.Out=this.Algorithm.GTE(leftValue,rightValue);
                            break;
                        case '<':
                            value.Out=this.Algorithm.LT(leftValue,rightValue);
                            break;
                        case '<=':
                            value.Out=this.Algorithm.LTE(leftValue,rightValue);
                            break;
                        case '==':
                        case '=':   //= 比较
                            value.Out=this.Algorithm.EQ(leftValue,rightValue);
                            break;
                        case '!=':
                        case '<>':
                            value.Out=this.Algorithm.NEQ(leftValue,rightValue);
                            break;
                    }

                    if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] BinaryExpression',value);
                }
                else if (value.Type==Syntax.LogicalExpression)
                {
                    let leftValue=this.GetNodeValue(value.Left);
                    let rightValue=this.GetNodeValue(value.Right);

                    if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value , leftValue, rightValue);
                    value.Out=null; //保存中间值

                    switch(value.Operator)
                    {
                        case '&&':
                        case 'AND':
                            value.Out=this.Algorithm.And(leftValue,rightValue);
                            break;
                        case '||':
                        case 'OR':
                            value.Out=this.Algorithm.Or(leftValue,rightValue);
                            break;
                    }

                    if (JS_EXECUTE_DEBUG_LOG) JSConsole.Complier.Log('[JSExecute::VisitBinaryExpression] LogicalExpression',value);
                }
                
                node=temp;
            }
        }

        return node.Out;

    }

    this.GetNodeValue=function(node)
    {
        switch(node.Type)
        {
            case Syntax.Literal:    //数字
                return node.Value;
            case Syntax.UnaryExpression:
                if (node.Operator=='-') 
                {
                    let value=this.GetNodeValue(node.Argument);
                    return this.Algorithm.Subtract(0,value);
                }
                return node.Argument.Value;
            case Syntax.Identifier:
                let value=this.ReadVariable(node.Name,node);
                return value;
            case Syntax.BinaryExpression:
            case Syntax.LogicalExpression:
                return node.Out;
            case Syntax.CallExpression:
                return this.VisitCallExpression(node);
            default:
                this.ThrowUnexpectedNode(node);
        }
    }

    this.ThrowUnexpectedNode=function(node,message)
    {
        let marker=node.Marker;
        let msg=message || "执行异常";
       
        return this.ErrorHandler.ThrowError(marker.Index,marker.Line,marker.Column,msg);
       
    }

    this.ThrowDownloadSF=function(node,job,message)
    {
        let marker=node.Marker;
        let msg=message;

        return this.ErrorHandler.ThrowDownloadJob(marker.Index,marker.Line,marker.Column,msg,job);
    }

    this.ThrowError=function()
    {

    }
}

//对外导出类
function JSComplier()
{
    
}


//词法分析
JSComplier.Tokenize=function(code)
{
    JSConsole.Complier.Log('[JSComplier.Tokenize]', code);
    let tokenizer=new Tokenizer(code);
    let tokens=[];
    try
    {
        while(true)
        {
            let token=tokenizer.GetNextToken();
            if (!token) break;

            tokens.push(token);
        }
    }
    catch(e)
    {

    }

    return tokens;
}

//语法解析 生成抽象语法树(Abstract Syntax Tree)
JSComplier.Parse=function(code)
{
    JSConsole.Complier.Log('[JSComplier.Parse]',code);

    let parser=new JSParser(code);
    parser.Initialize();
    let program=parser.ParseScript();
    let ast=program;
    return ast;
}

/*
    执行
    option.Symbol=股票代码
    option.Name=股票名称
    option.Data=这个股票的ChartData
    option.Right=复权
    option.MaxReqeustDataCount=请求数据的最大个数
*/

function timeout(ms) {
    return new Promise((resolve) => {
      setTimeout(resolve, ms);
    });
  }

  
JSComplier.Execute=function(code,option,errorCallback)
{
    //异步调用
    //var asyncExecute= async function() es5不能执行 去掉异步
    var asyncExecute= function()
    {
        try
        {
            JSConsole.Complier.Log('[JSComplier.Execute]',code,option);

            JSConsole.Complier.Log('[JSComplier.Execute] parser .....');
            let parser=new JSParser(code);
            parser.Initialize();
            let program=parser.ParseScript(); 
            
            let ast=program;
            JSConsole.Complier.Log('[JSComplier.Execute] parser finish.', ast);

            JSConsole.Complier.Log('[JSComplier.Execute] execute .....');
            let execute=new JSExecute(ast,option);
            execute.ErrorCallback=errorCallback;        //执行错误回调
            execute.JobList=parser.Node.GetDataJobList();
            if (option.ClassName=='ScriptIndexConsole') execute.JobList.unshift({ID:JS_EXECUTE_JOB_ID.JOB_DOWNLOAD_SYMBOL_DATA});
            execute.JobList.push({ID:JS_EXECUTE_JOB_ID.JOB_RUN_SCRIPT});

            let result=execute.Execute();

        }catch(error)
        {
            JSConsole.Complier.Log(error);

            if (errorCallback) errorCallback(error);
        }
    }

    asyncExecute();

    JSConsole.Complier.Log('[JSComplier.Execute] async execute.');
}


JSComplier.SetDomain = function (domain, cacheDomain)   //修改API地址
{
    if (domain) g_JSComplierResource.Domain = domain;
    if (cacheDomain) g_JSComplierResource.CacheDomain = cacheDomain;
}


JSComplier.AddIcon=function(obj)    //添加一个obj={ID:, Text:, Color, Family: }
{
    g_JSComplierResource.CustomDrawIcon.Data.set(obj.ID, obj);
}

JSComplier.AddFunction=function(obj)    //添加函数
{
    if (!obj || !obj) return;

    var ID=obj.Name.toUpperCase();
    g_JSComplierResource.CustomFunction.Data.set(ID, obj);
}

JSComplier.AddVariant=function(obj)
{
    if (!obj || !obj) return;

    var ID=obj.Name.toUpperCase();
    g_JSComplierResource.CustomVariant.Data.set(ID, obj);
}

var HQ_DATA_TYPE=
{
    KLINE_ID:0,         //K线
    MINUTE_ID:2,        //当日走势图
    HISTORY_MINUTE_ID:3,//历史分钟走势图
    MULTIDAY_MINUTE_ID:4,//多日走势图
};


//脚本指标
//name=指标名字 args=参数名字 参数值
function ScriptIndex(name,script,args,option)
{
    this.newMethod=BaseIndex;   //派生
    this.newMethod(name);
    delete this.newMethod;

    this.Script=script;
    this.Arguments=[];
    this.OutVar=[];
    this.ID;                //指标ID
    this.FloatPrecision=2;  //小数位数
    this.StringFormat;
    this.KLineType=null;    //K线显示类型
    this.InstructionType;   //五彩K线, 交易指标
    this.YSpecificMaxMin=null;  //最大最小值
    this.YSplitScale=null;      //固定刻度
    this.Condition=null;        //限制条件

    //指标上锁配置信息
    this.IsLocked=false;    //是否锁住指标
    this.LockCallback=null;
    this.LockID=null;
    this.LockBG=null;       //锁背景色
    this.LockTextColor=null;
    this.LockText=null;
    this.LockFont=null;
    this.LockCount=20;
    this.LockMinWidth=null;
    this.TitleFont=g_JSChartResource.TitleFont;     //标题字体
    this.IsShortTitle=false;                        //是否显示指标参数

    if (option)
    {
        if (option.FloatPrecision>=0) this.FloatPrecision=option.FloatPrecision;
        if (option.StringFormat>0) this.StringFormat=option.StringFormat;
        if (option.ID) this.ID=option.ID;
        if (option.KLineType>=0 || option.KLineType===-1) this.KLineType=option.KLineType;
        if (option.InstructionType) this.InstructionType=option.InstructionType;
        if (option.YSpecificMaxMin) this.YSpecificMaxMin=option.YSpecificMaxMin;
        if (option.YSplitScale) this.YSplitScale=option.YSplitScale;
        if (option.Condition) this.Condition=option.Condition;
        if (option.TitleFont) this.TitleFont=option.TitleFont;
        if (option.IsShortTitle) this.IsShortTitle=option.IsShortTitle;
    }

    if (option && option.Lock) 
    {
        if (option.Lock.IsLocked==true) this.IsLocked=true;  //指标上锁
        if (option.Lock.Callback) this.LockCallback=option.Lock.Callback;    //锁回调
        if (option.Lock.ID) this.LockID=option.Lock.ID;                      //锁ID
        if (option.Lock.BG) this.LockBG=option.Lock.BG;
        if (option.Lock.TextColor) this.LockTextColor=option.Lock.TextColor;
        if (option.Lock.Text) this.LockText=option.Lock.Text;
        if (option.Lock.Font) this.LockFont=option.Lock.Font;
        if (option.Lock.Count) this.LockCount=option.Lock.Count;
        if (option.Lock.MinWidth) this.LockMinWidth=option.Lock.MinWidth*GetDevicePixelRatio();
    }

    if (args) this.Arguments=args;

    this.SetLock=function(lockData)
    {
        if (lockData.IsLocked==true) 
        {
            this.IsLocked=true;  //指标上锁
            if (lockData.Callback) this.LockCallback=lockData.Callback;    //锁回调
            if (lockData.ID) this.LockID=lockData.ID;                      //锁ID
            if (lockData.BG) this.LockBG=lockData.BG;
            if (lockData.TextColor) this.LockTextColor=lockData.TextColor;
            if (lockData.Text) this.LockText=lockData.Text;
            if (lockData.Font) this.LockFont=lockData.Font;
            if (lockData.Count) this.LockCount=lockData.Count;
            if (lockData.MinWidth) this.LockMinWidth=lockData.MinWidth*GetDevicePixelRatio();
        }
        else
        {   //清空锁配置信息
            this.IsLocked=false;    //是否锁住指标
            this.LockCallback=null;
            this.LockID=null;
            this.LockBG=null;       //锁背景色
            this.LockTextColor=null;
            this.LockText=null;
            this.LockFont=null;
            this.LockCount=20;
        }
    }

    this.ExecuteScript=function(hqChart,windowIndex,hisData)
    {
        this.OutVar=[];
        let self = this;
        let param=
        {
            HQChart:hqChart,
            WindowIndex:windowIndex,
            HistoryData:hisData,
            Self:this
        };

        //数据类型
        let hqDataType=HQ_DATA_TYPE.KLINE_ID;   //默认K线
        if (hqChart.ClassName==='MinuteChartContainer' || hqChart.ClassName==='MinuteChartHScreenContainer') 
        {
            if (hqChart.DayCount>1) hqDataType=HQ_DATA_TYPE.MULTIDAY_MINUTE_ID; //多日分钟
            else hqDataType=HQ_DATA_TYPE.MINUTE_ID;                             //分钟数据
        }
        else if (hqChart.ClassName==='HistoryMinuteChartContainer') 
        {
            hqDataType=HQ_DATA_TYPE.HISTORY_MINUTE_ID;   //历史分钟
        }
        let option=
        {
            HQDataType:hqDataType,
            Symbol:hqChart.Symbol, 
            Name:hqChart.Name,
            Data:hisData,
            SourceData:hqChart.SourceData,
            Callback:this.RecvResultData, CallbackParam:param,
            Async:true,
            MaxRequestDataCount:hqChart.MaxReqeustDataCount,
            MaxRequestMinuteDayCount:hqChart.MaxRequestMinuteDayCount,
            Arguments:this.Arguments,
            Condition:this.Condition,
            IsBeforeData:hqChart.IsBeforeData
        };

        if (hqDataType===HQ_DATA_TYPE.HISTORY_MINUTE_ID) option.TrateDate=hqChart.TradeDate;
        if (hqDataType===HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) option.DayCount=hqChart.DayCount;
        if (hqChart.NetworkFilter) option.NetworkFilter=hqChart.NetworkFilter;

        if (this.Condition && !this.IsMeetCondition(param,option))
        {
            this.ShowConditionError(param);
            return;
        }

        let code=this.Script;
        let run=JSComplier.Execute(code,option,hqChart.ScriptErrorCallback);
    }

    //是否符合限制条件
    this.IsMeetCondition=function(param,option)
    {
        JSConsole.Complier.Log('[ScriptIndex::IsMeetCondition] ', this.Condition);
        if (this.Condition.Period)      //周期是否满足
        {
            if (!this.IsMeetPeriodCondition(param,option)) return false;
        }

        return true;
    }

    //周期是否满足条件
    this.IsMeetPeriodCondition=function(param,option)
    {
        if (!this.Condition.Period) return true;

        for(var i in this.Condition.Period)
        {
            var item=this.Condition.Period[i];
            switch(item)
            {
                case CONDITION_PERIOD.MINUTE_ID:
                    if (option.HQDataType==HQ_DATA_TYPE.MINUTE_ID) return true;
                    break;
                case CONDITION_PERIOD.MULTIDAY_MINUTE_ID:
                    if (option.HQDataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) return true;
                    break;
                case CONDITION_PERIOD.KLINE_DAY_ID:
                case CONDITION_PERIOD.KLINE_WEEK_ID:
                case CONDITION_PERIOD.KLINE_MONTH_ID:
                case CONDITION_PERIOD.KLINE_YEAR_ID:

                case CONDITION_PERIOD.KLINE_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_5_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_15_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_30_MINUTE_ID:
                case CONDITION_PERIOD.KLINE_60_MINUTE_ID:
                    if (param.HQChart.Period==item) return true;
                    break;
            }
        }

        return false;
    }

    //显示指标不符合条件
    this.ShowConditionError=function(param)
    {
        var hqChart=param.HQChart;
        var windowIndex=param.WindowIndex;

        hqChart.DeleteIndexPaint(windowIndex);
        if (windowIndex==0) hqChart.ShowKLine(true);

        var message='指标不支持当前品种或周期';

        let line=new ChartLine();
        line.Canvas=hqChart.Canvas;
        line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        line.NotSupportMessage=message;
        hqChart.ChartPaint.push(line);
        hqChart.Draw();
    }

    this.RecvResultData=function(outVar,param)
    {
        let hqChart=param.HQChart;
        let windowIndex=param.WindowIndex;
        let hisData=param.HistoryData;
        param.Self.OutVar=outVar;
        param.Self.BindData(hqChart,windowIndex,hisData);

        if (param.Self.IsLocked==false) //不上锁
        {
            param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(null);
        }
        else    //上锁
        {
            let lockData={ IsLocked:true,Callback:param.Self.LockCallback,IndexName:param.Self.Name ,ID:param.Self.LockID,
                BG:param.Self.LockBG,Text:param.Self.LockText,TextColor:param.Self.LockTextColor, Font:param.Self.LockFont, Count:param.Self.LockCount, MinWidth:param.Self.LockMinWidth };
            param.HQChart.Frame.SubFrame[windowIndex].Frame.SetLock(lockData);
        }

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();

        if (hqChart.GetIndexEvent)
        {
            var event=hqChart.GetIndexEvent();  //指标计算完成回调
            if (event)
            {
                var self=param.Self;
                var data={ OutVar:self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData, 
                        Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} };
                event.Callback(event,data,self);
            }
        }
    }

    this.CreateLine=function(hqChart,windowIndex,varItem,id)
    {
        let line=new ChartLine();
        line.Canvas=hqChart.Canvas;
        line.DrawType=1;
        line.Name=varItem.Name;
        line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color=this.GetColor(varItem.Color);
        else line.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) line.LineWidth=width;
        }

        if (varItem.IsDotLine) line.IsDotLine=true; //虚线
        if (varItem.IsShow==false) line.IsShow=false;
        
        let titleIndex=windowIndex+1;
        line.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color);

        hqChart.ChartPaint.push(line);
    }

    //创建柱子
    this.CreateBar=function(hqChart,windowIndex,varItem,id)
    {
        let bar=new ChartStickLine();
        bar.Canvas=hqChart.Canvas;
        if (varItem.Draw.Width>0) bar.LineWidth=varItem.Draw.Width;
        else bar.LineWidth=1;

        bar.Name=varItem.Name;
        bar.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        bar.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) bar.Color=this.GetColor(varItem.Color);
        else bar.Color=this.GetDefaultColor(id);
        
        let titleIndex=windowIndex+1;
        bar.Data.Data=varItem.Draw.DrawData;
        bar.BarType=varItem.Draw.Type;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(bar);
    }

    //创建文本
    this.CreateText=function(hqChart,windowIndex,varItem,id)
    {
        let chartText=new ChartSingleText();
        chartText.Canvas=hqChart.Canvas;

        chartText.Name=varItem.Name;
        chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chartText.Color=this.GetColor(varItem.Color);
        else chartText.Color=this.GetDefaultColor(id);

        let titleIndex=windowIndex+1;
        if (varItem.Draw.Position) chartText.Position=varItem.Draw.Position;    //赋值坐标
        if (varItem.Draw.DrawData) chartText.Data.Data=varItem.Draw.DrawData;
        chartText.Text=varItem.Draw.Text;
        if (varItem.Draw.Direction>0) chartText.Direction=varItem.Draw.Direction;
        if (varItem.Draw.YOffset>0) chartText.YOffset=varItem.Draw.YOffset;
        if (varItem.Draw.TextAlign) chartText.TextAlign=varItem.Draw.TextAlign;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(chartText);
    }

    //COLORSTICK 
    this.CreateMACD=function(hqChart,windowIndex,varItem,id)
    {
        let chartMACD=new ChartMACD();
        chartMACD.Canvas=hqChart.Canvas;

        chartMACD.Name=varItem.Name;
        chartMACD.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartMACD.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        let titleIndex=windowIndex+1;
        chartMACD.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chartMACD.Data,varItem.Name,this.GetDefaultColor(id));

        hqChart.ChartPaint.push(chartMACD);
    }

    this.CreatePointDot=function(hqChart,windowIndex,varItem,id)
    {
        let pointDot=new ChartPointDot();
        pointDot.Canvas=hqChart.Canvas;
        pointDot.Name=varItem.Name;
        pointDot.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        pointDot.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) pointDot.Color=this.GetColor(varItem.Color);
        else pointDot.Color=this.GetDefaultColor(id);

        if (varItem.Radius) pointDot.Radius=varItem.Radius;

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) pointDot.Radius=width;
        }

        let titleIndex=windowIndex+1;
        pointDot.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(pointDot.Data,varItem.Name,pointDot.Color);

        hqChart.ChartPaint.push(pointDot);
    }

    this.CreateStick=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartStick();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.LineWidth=width;
        }

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        hqChart.ChartPaint.push(chart);
    }

    this.CreateLineStick=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartLineStick();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.LineWidth=width;
        }

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        hqChart.ChartPaint.push(chart);
    }

    this.CreateStraightLine=function(hqChart,windowIndex,varItem,id)
    {
        let line=new ChartLine();
        line.DrawType=1;
        line.Canvas=hqChart.Canvas;
        line.Name=varItem.Name;
        line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color=this.GetColor(varItem.Color);
        else line.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) line.LineWidth=width;
        }
        
        let titleIndex=windowIndex+1;
        line.Data.Data=varItem.Draw.DrawData;
        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color);

        hqChart.ChartPaint.push(line);
    }

    this.CreateVolStick=function(hqChart,windowIndex,varItem,id,hisData)
    {
        let chart=new ChartVolStick();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        chart.KLineDrawType=hqChart.KLineDrawType;  //设置K线显示类型
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        chart.HistoryData=hisData;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        hqChart.ChartPaint.push(chart);
    }

    this.CreateBand=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartBand();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.FirstColor = varItem.Draw.Color[0];
        chart.SecondColor = varItem.Draw.Color[1];
        chart.Data.Data=varItem.Draw.DrawData;

        hqChart.ChartPaint.push(chart);
    }

    //创建K线图
    this.CreateKLine=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartKLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data.Data=varItem.Draw.DrawData;
        chart.IsShowMaxMinPrice=false;
        chart.IsShowKTooltip=false;

        if (varItem.Color)  //如果设置了颜色,使用外面设置的颜色
            chart.UnchagneColor=chart.DownColor=chart.UpColor=this.GetColor(varItem.Color);

        hqChart.ChartPaint.push(chart);
    }

    this.CreatePolyLine=function(hqChart,windowIndex,varItem,id)
    {
        let line=new ChartLine();
        line.Canvas=hqChart.Canvas;
        line.Name=varItem.Name;
        line.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        line.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) line.Color=this.GetColor(varItem.Color);
        else line.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) line.LineWidth=width;
        }
        
        let titleIndex=windowIndex+1;
        line.Data.Data=varItem.Draw.DrawData;
        hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,' ',line.Color); //给一个空的标题

        hqChart.ChartPaint.push(line);
    }

    this.CreateBackgroud=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartBackground();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Draw && varItem.Draw.DrawData)
        {
            var drawData=varItem.Draw.DrawData;
            chart.Color=drawData.Color;
            chart.ColorAngle=drawData.Angle;
        }

        hqChart.ChartPaint.push(chart);
    }

    this.CreateTextLine=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartTextLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Draw && varItem.Draw.DrawData)
        {
            var drawData=varItem.Draw.DrawData;
            chart.Text=drawData.Text;
            chart.Line=drawData.Line;
            chart.Price=drawData.Price;
        }

        hqChart.ChartPaint.push(chart);
    }

    this.CreateNumberText=function(hqChart,windowIndex,varItem,id)
    {
        let chartText=new ChartSingleText();
        chartText.Canvas=hqChart.Canvas;

        chartText.Name=varItem.Name;
        chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;
        if (varItem.Color) chartText.Color=this.GetColor(varItem.Color);
        else chartText.Color=this.GetDefaultColor(id);

        let titleIndex=windowIndex+1;
        chartText.Data.Data=varItem.Draw.DrawData.Value;
        chartText.Text=varItem.Draw.DrawData.Text;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(chartText);
    }

    //创建图标
    this.CreateIcon=function(hqChart,windowIndex,varItem,id)
    {
        let chartText=new ChartSingleText();
        chartText.Canvas=hqChart.Canvas;
        chartText.TextAlign='center';

        chartText.Name=varItem.Name;
        chartText.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chartText.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        let titleIndex=windowIndex+1;
        chartText.Data.Data=varItem.Draw.DrawData;
        var icon=varItem.Draw.Icon;
        if (icon.IconFont==true)
        {
            chartText.IconFont={ Family:icon.Family, Text:icon.Symbol, Color:icon.Color };
        }
        else
        {
            chartText.Text=icon.Symbol;
            if (varItem.Color) chartText.Color=this.GetColor(varItem.Color);
            else if (icon.Color) chartText.Color=icon.Color;
            else chartText.Color='rgb(0,0,0)';
        }
        
        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        hqChart.ChartPaint.push(chartText);
    }

    //创建通道
    this.CreateChannel=function(hqChart,windowIndex,varItem,id)
    {
        let chart=new ChartChannel();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        if(varItem.Draw.AreaColor) chart.AreaColor=varItem.Draw.AreaColor;
        else if (varItem.Color) chart.AreaColor=this.GetColor(varItem.Color);
        else chart.AreaColor=this.GetDefaultColor(id);

        if (varItem.Draw.Border.Color) chart.LineColor=varItem.Draw.Border.Color;
        else chart.LineColor=null;

        if (varItem.Draw.Border.Dotted) chart.LineDotted=varItem.Draw.Border.Dotted;
        if (varItem.Draw.Border.Width>0) chart.LineWidth=varItem.Draw.Border.Width;

        //let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData;
        hqChart.ChartPaint.push(chart);
    }

    this.CreatePartLine=function(hqChart,windowIndex,varItem,i)
    {
        let chart=new ChartPartLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data.Data=varItem.Draw.DrawData;
        hqChart.ChartPaint.push(chart);
    }

    this.CreateMultiLine=function(hqChart,windowIndex,varItem,i)
    {
        let chart=new ChartMultiLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data=hqChart.ChartPaint[0].Data;//绑定K线
        chart.Lines=varItem.Draw.DrawData; 
        hqChart.ChartPaint.push(chart);
    }

    this.CreateMultiText=function(hqChart,windowIndex,varItem,i)
    {
        let chart=new ChartMultiText();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data=hqChart.ChartPaint[0].Data;//绑定K线
        chart.Texts=varItem.Draw.DrawData; 
        hqChart.ChartPaint.push(chart);
    }

    this.CreateMultiSVGIcon=function(hqChart,windowIndex,varItem,i)
    {
        let chart=new ChartMultiSVGIcon();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data=hqChart.ChartPaint[0].Data;//绑定K线
        chart.Family=varItem.Draw.DrawData.Family;
        chart.Icon= varItem.Draw.DrawData.Icon;
        hqChart.ChartPaint.push(chart);
    }

    this.CreateRectangle=function(hqChart,windowIndex,varItem,i)
    {
        let chart=new ChartRectangle();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Color=[varItem.Draw.DrawData.Color];
        chart.Rect=varItem.Draw.DrawData.Rect;
        if (varItem.Color) chart.BorderColor=this.GetColor(varItem.Color);
        hqChart.ChartPaint.push(chart);
    }

    //创建K线
    this.CreateSelfKLine=function(hqChart,windowIndex,hisData)
    {
        let chart=new ChartKLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name="Self Kline"
        chart.ChartBorder=hqChart.Frame.SubFrame[windowIndex].Frame.ChartBorder;
        chart.ChartFrame=hqChart.Frame.SubFrame[windowIndex].Frame;

        chart.Data=hisData
        chart.IsShowMaxMinPrice=false;
        chart.IsShowKTooltip=false;
        chart.DrawType=this.KLineType;

        hqChart.ChartPaint.push(chart);
    }

    this.BindInstructionData=function(hqChart,windowIndex,hisData)  //绑定指示指标
    {
        if (this.OutVar==null || this.OutVar.length<0) return;
        if (this.InstructionType==2)        //五彩K线
        {
            let varItem=this.OutVar[this.OutVar.length-1]; //取最后一组数据作为指示数据
            hqChart.SetInstructionData(this.InstructionType, {Data:varItem.Data, Name:this.Name, ID:this.ID });       //设置指示数据
            return true;
        }
        else if (this.InstructionType==1)   //交易系统
        {
            var buyData, sellData;
            for(var i in this.OutVar)
            {
                let item=this.OutVar[i];
                if (item.Name=='ENTERLONG') buyData=item.Data;
                else if (item.Name=='EXITLONG') sellData=item.Data;
            }

            hqChart.SetInstructionData(this.InstructionType, {Buy:buyData, Sell:sellData, Name:this.Name, ID:this.ID});       //设置指示数据
            return true;
        }
    }

    this.BindData=function(hqChart,windowIndex,hisData)
    {
        if (windowIndex==0 && this.InstructionType)
        {
            this.BindInstructionData(hqChart,windowIndex,hisData);
            return;
        }

        //清空主指标图形
        hqChart.DeleteIndexPaint(windowIndex);
        if (windowIndex==0) hqChart.ShowKLine(true);

        if (this.OutVar==null || this.OutVar.length<0) return;

        //叠加一个K线背景
        if (this.KLineType!=null)
        {
            if (this.KLineType===0 || this.KLineType===1 || this.KLineType===2) this.CreateSelfKLine(hqChart,windowIndex,hisData);
            else if (this.KLineType===-1 && windowIndex==0) hqChart.ShowKLine(false);
        }

        if (windowIndex>=1 && hqChart.Frame)
        {
            hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision=this.FloatPrecision;
            if (this.YSpecificMaxMin)  hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=this.YSpecificMaxMin;  //最大最小值
            if (this.YSplitScale)   hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale=this.YSplitScale;             //固定刻度
        }

        /*
        if (this.Name=='MA')    //测试多线段
        {
            var point1={Point:[{Index:300, Value:15.5}, {Index:301, Value:14.2} , {Index:304, Value:14.05}], Color:'rgb(244,55,50)'};
            var point2={Point:[{Index:307, Value:14.5}, {Index:308, Value:14.2} , {Index:309, Value:14.15}], Color:'rgb(0,55,50)'};
            var testData={ Name:'MULTI_LINE', Type:1,Draw:{ DrawType:'MULTI_LINE', DrawData:[point1,point2] } };
            this.OutVar.push(testData);
        }
        */
        
        for(let i in this.OutVar)
        {
            let item=this.OutVar[i];
            if (item.IsExData===true) continue; //扩展数据不显示图形

            if (item.Type==0)  
            {
                this.CreateLine(hqChart,windowIndex,item,i);
            }
            else if (item.Type==1)
            {
                switch(item.Draw.DrawType)
                {
                    case 'STICKLINE':
                        this.CreateBar(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWTEXT':
                    case 'SUPERDRAWTEXT':
                    case 'DRAWTEXT_FIX':
                        this.CreateText(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWLINE':
                        this.CreateStraightLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWBAND':
                        this.CreateBand(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWKLINE':
                        this.CreateKLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWKLINE_IF':
                        this.CreateKLine(hqChart,windowIndex,item,i);
                        break;
                    case 'POLYLINE':
                        this.CreatePolyLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWGBK':
                        this.CreateBackgroud(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWTEXT_LINE':
                        this.CreateTextLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWNUMBER':
                        this.CreateNumberText(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWICON':
                        this.CreateIcon(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWCHANNEL':
                        this.CreateChannel(hqChart,windowIndex,item,i);
                        break;
                    case 'PARTLINE':
                        this.CreatePartLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWRECTREL':
                        this.CreateRectangle(hqChart,windowIndex,item,i);
                        break;
                    case 'MULTI_LINE':
                        this.CreateMultiLine(hqChart,windowIndex,item,i);
                        break;
                    case 'MULTI_TEXT':
                        this.CreateMultiText(hqChart,windowIndex,item,i);
                        break;
                    case 'MULTI_SVGICON':
                        this.CreateMultiSVGIcon(hqChart,windowIndex,item,i);
                        break;
                }
            }
            else if (item.Type==2)
            {
                this.CreateMACD(hqChart,windowIndex,item,i);
            }
            else if (item.Type==3)
            {
                this.CreatePointDot(hqChart,windowIndex,item,i);
            }
            else if (item.Type==4)
            {
                this.CreateLineStick(hqChart,windowIndex,item,i);
            }
            else if (item.Type==5)
            {
                this.CreateStick(hqChart,windowIndex,item,i);
            }
            else if (item.Type==6)
            {
                this.CreateVolStick(hqChart,windowIndex,item,i,hisData);
            }

            var titlePaint=hqChart.TitlePaint[windowIndex+1];
            if (titlePaint &&  titlePaint.Data && i<titlePaint.Data.length) //设置标题数值 小数位数和格式
            {
                if (this.StringFormat>0) titlePaint.Data[i].StringFormat=this.StringFormat;
                if (this.FloatPrecision>=0) titlePaint.Data[i].FloatPrecision=this.FloatPrecision;
            }
        }

        let titleIndex=windowIndex+1;
        hqChart.TitlePaint[titleIndex].Title=this.Name;

        if (!this.IsShortTitle)
        {
            let indexParam='';
            for(let i in this.Arguments)
            {
                let item=this.Arguments[i];
                if (indexParam.length>0) indexParam+=',';
                indexParam+=item.Value.toString();
            }

            if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')';
        }

        if (this.TitleFont) hqChart.TitlePaint[titleIndex].Font=this.TitleFont;

        return true;
    }


    //给一个默认的颜色
    this.GetDefaultColor=function(id)
    {
        let COLOR_ARRAY=null;
        //使用全局线段配置
        if (g_JSChartResource && g_JSChartResource.Index && g_JSChartResource.Index.LineColor)
        {
            COLOR_ARRAY=g_JSChartResource.Index.LineColor;
        }

        if (!COLOR_ARRAY || !Array.isArray(COLOR_ARRAY))
        {
            COLOR_ARRAY=
            [
                "rgb(255,174,0)",
                "rgb(25,199,255)",
                "rgb(175,95,162)",
                "rgb(236,105,65)",
                "rgb(68,114,196)",
                "rgb(229,0,79)",
                "rgb(0,128,255)",
                "rgb(252,96,154)",
                "rgb(42,230,215)",
                "rgb(24,71,178)",
            ];
        }

        let number=parseInt(id);
        return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)];
    }

    //获取颜色
    this.GetColor=function(colorName)
    {
        let COLOR_MAP=new Map([
            ['COLORBLACK','rgb(0,0,0)'],
            ['COLORBLUE','rgb(18,95,216)'],
            ['COLORGREEN','rgb(25,158,0)'],
            ['COLORCYAN','rgb(0,255,198)'],
            ['COLORRED','rgb(238,21,21)'],
            ['COLORMAGENTA','rgb(255,0,222)'],
            ['COLORBROWN','rgb(149,94,15)'],
            ['COLORLIGRAY','rgb(218,218,218)'],      //画淡灰色
            ['COLORGRAY','rgb(133,133,133)'],        //画深灰色
            ['COLORLIBLUE','rgb(94,204,255)'],       //淡蓝色
            ['COLORLIGREEN','rgb(183,255,190)'],      //淡绿色
            ['COLORLICYAN','rgb(154,255,242)'],      //淡青色
            ['COLORLIRED','rgb(255,172,172)'],       //淡红色
            ['COLORLIMAGENTA','rgb(255,145,241)'],   //淡洋红色
            ['COLORWHITE','rgb(255,255,255)'],       //白色
            ['COLORYELLOW','rgb(255,198,0)']
        ]);

        if (COLOR_MAP.has(colorName)) return COLOR_MAP.get(colorName);

        //COLOR 自定义色
        //格式为COLOR+“RRGGBB”:RR、GG、BB表示红色、绿色和蓝色的分量,每种颜色的取值范围是00-FF,采用了16进制。
        //例如:MA5:MA(CLOSE,5),COLOR00FFFF 表示纯红色与纯绿色的混合色:COLOR808000表示淡蓝色和淡绿色的混合色。
        if (colorName.indexOf('COLOR')==0) return '#'+colorName.substr(5);

        return 'rgb(30,144,255)';
    }
}

function OverlayScriptIndex(name,script,args,option)
{
    this.newMethod=ScriptIndex;   //派生
    this.newMethod(name,script,args,option);
    delete this.newMethod;

    //叠加指标
    this.OverlayIndex=null; // { IsOverlay:true, Identify:overlayFrame.Identify, WindowIndex:windowIndex, Frame:overlayFrame }

    this.BindData=function(hqChart,windowIndex,hisData)
    {
        if (!this.OverlayIndex || this.OverlayIndex.IsOverlay!=true) return;

        this.OverlayIndex.Frame.ChartPaint=[];
        if (this.OutVar==null || this.OutVar.length<0) return;

        /*叠加一个K线背景
        if (this.KLineType!=null)
        {
            if (this.KLineType===0 || this.KLineType===1 || this.KLineType===2) this.CreateSelfKLine(hqChart,windowIndex,hisData);
            else if (this.KLineType===-1 && windowIndex==0) hqChart.ShowKLine(false);
        }

        if (windowIndex>=1 && hqChart.Frame)
        {
            hqChart.Frame.SubFrame[windowIndex].Frame.YSplitOperator.FloatPrecision=this.FloatPrecision;
            if (this.YSpecificMaxMin)  hqChart.Frame.SubFrame[windowIndex].Frame.YSpecificMaxMin=this.YSpecificMaxMin;  //最大最小值
            if (this.YSplitScale)   hqChart.Frame.SubFrame[windowIndex].Frame.YSplitScale=this.YSplitScale;             //固定刻度
        }
        */
        
        //指标名字
        var titleInfo={ Data:[], Title:this.Name };
        let indexParam='';
        for(var i in this.Arguments)
        {
            let item=this.Arguments[i];
            if (indexParam.length>0) indexParam+=',';
            indexParam+=item.Value.toString();
        }
        if (indexParam.length>0) titleInfo.Title=this.Name+'('+indexParam+')';

        var titleIndex=windowIndex+1;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.set(this.OverlayIndex.Identify,titleInfo);
        this.OverlayIndex.Frame.Frame.Title=titleInfo.Title;    //给子框架设置标题

        for(var i in this.OutVar)
        {
            let item=this.OutVar[i];
            if (item.IsExData===true) continue; //扩展数据不显示图形

            if (item.Type==0)  
            {
                this.CreateLine(hqChart,windowIndex,item,i);
            }
            else if (item.Type==1)
            {
                switch(item.Draw.DrawType)
                {
                    case 'STICKLINE':
                        this.CreateBar(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWTEXT':
                    case 'SUPERDRAWTEXT':
                    case 'DRAWTEXT_FIX':
                        this.CreateText(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWLINE':
                        this.CreateStraightLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWBAND':
                        this.CreateBand(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWKLINE':
                        this.CreateKLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWKLINE_IF':
                        this.CreateKLine(hqChart,windowIndex,item,i);
                        break;
                    case 'POLYLINE':
                        this.CreatePolyLine(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWNUMBER':
                        this.CreateNumberText(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWICON':
                        this.CreateIcon(hqChart,windowIndex,item,i);
                        break;
                    case 'DRAWCHANNEL':
                        this.CreateChannel(hqChart,windowIndex,item,i);
                        break;
                }
            }
            else if (item.Type==2)
            {
                this.CreateMACD(hqChart,windowIndex,item,i);
            }
            else if (item.Type==3)
            {
                this.CreatePointDot(hqChart,windowIndex,item,i);
            }
            else if (item.Type==4)
            {
                this.CreateLineStick(hqChart,windowIndex,item,i);
            }
            else if (item.Type==5)
            {
                this.CreateStick(hqChart,windowIndex,item,i);
            }
            else if (item.Type==6)
            {
                this.CreateVolStick(hqChart,windowIndex,item,i,hisData);
            }
        }

        
        /*
        hqChart.TitlePaint[titleIndex].Title=this.Name;

        let indexParam='';
        for(let i in this.Arguments)
        {
            let item=this.Arguments[i];
            if (indexParam.length>0) indexParam+=',';
            indexParam+=item.Value.toString();
        }

        if (indexParam.length>0) hqChart.TitlePaint[titleIndex].Title=this.Name+'('+indexParam+')';
        */

        return true;
    }

    //指标执行完成
    this.RecvResultData=function(outVar,param)
    {
        let hqChart=param.HQChart;
        let windowIndex=param.WindowIndex;
        let hisData=param.HistoryData;
        param.Self.OutVar=outVar;
        param.Self.BindData(hqChart,windowIndex,hisData);

        param.HQChart.UpdataDataoffset();           //更新数据偏移
        param.HQChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        param.HQChart.Draw();

        var event=hqChart.GetOverlayIndexEvent();  //指标计算完成回调
        if (event)
        {
            var self=param.Self;
            var data={ OutVar:self.OutVar, WindowIndex: windowIndex, Name: self.Name, Arguments: self.Arguments, HistoryData: hisData, 
                    Identify:self.OverlayIndex.Identify,
                    Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} };
            event.Callback(event,data,self);
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////
    //  图形创建
    /////////////////////////////////////////////////////////////////////////////////////

    this.CreateLine=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartLine();
        chart.Canvas=hqChart.Canvas;
        chart.DrawType=1;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) line.LineWidth=width;
        }

        if (varItem.IsShow==false) line.IsShow=false;
        
        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        frame.ChartPaint.push(chart);
    }

    //创建柱子
    this.CreateBar=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartStickLine();
        chart.Canvas=hqChart.Canvas;
        if (varItem.Draw.Width>0) chart.LineWidth=varItem.Draw.Width;
        else chart.LineWidth=1;

        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);
        
        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    }

    //创建文本
    this.CreateText=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartSingleText();
        chart.Canvas=hqChart.Canvas;

        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        let titleIndex=windowIndex+1;
        if (varItem.Draw.Position) chart.Position=varItem.Draw.Position;    //赋值坐标
        if (varItem.Draw.DrawData) chart.Data.Data=varItem.Draw.DrawData;
        chart.Text=varItem.Draw.Text;
        if (varItem.Draw.Direction>0) chart.Direction=varItem.Draw.Direction;
        if (varItem.Draw.YOffset>0) chart.YOffset=varItem.Draw.YOffset;
        if (varItem.Draw.TextAlign) chart.TextAlign=varItem.Draw.TextAlign;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    }

    //COLORSTICK 
    this.CreateMACD=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartMACD();
        chart.Canvas=hqChart.Canvas;

        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,this.GetDefaultColor(id));

        frame.ChartPaint.push(chart);
    }

    this.CreatePointDot=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartPointDot();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.Radius) chart.Radius=varItem.Radius;

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.Radius=width;
        }

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        frame.ChartPaint.push(chart);
    }

    this.CreateStick=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartStick();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.LineWidth=width;
        }

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        frame.ChartPaint.push(chart);
    }

    this.CreateLineStick=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartLineStick();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.LineWidth=width;
        }

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        frame.ChartPaint.push(chart);
    }

    this.CreateStraightLine=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartLine();
        chart.DrawType=1;
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.LineWidth=width;
        }
        
        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData;
        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(line.Data,varItem.Name,line.Color);

        frame.ChartPaint.push(chart);
    }

    this.CreateVolStick=function(hqChart,windowIndex,varItem,id,hisData)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartVolStick();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        chart.KLineDrawType=hqChart.KLineDrawType;  //设置K线显示类型
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Data;
        chart.HistoryData=hisData;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(chart.Data,varItem.Name,chart.Color);

        frame.ChartPaint.push(chart);
    }

    this.CreateBand=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartBand();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        chart.FirstColor = varItem.Draw.Color[0];
        chart.SecondColor = varItem.Draw.Color[1];
        chart.Data.Data=varItem.Draw.DrawData;

        frame.ChartPaint.push(chart);
    }

    //创建K线图
    this.CreateKLine=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartKLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        chart.Data.Data=varItem.Draw.DrawData;
        chart.IsShowMaxMinPrice=false;
        chart.IsShowKTooltip=false;

        if (varItem.Color)  //如果设置了颜色,使用外面设置的颜色
            chart.UnchagneColor=chart.DownColor=chart.UpColor=this.GetColor(varItem.Color);

        frame.ChartPaint.push(chart);
    }

    this.CreatePolyLine=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        if (varItem.LineWidth) 
        {
            let width=parseInt(varItem.LineWidth.replace("LINETHICK",""));
            if (!isNaN(width) && width>0) chart.LineWidth=width;
        }
        
        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData;
        var titlePaint=hqChart.TitlePaint[titleIndex];
        titlePaint.OverlayIndex.get(overlayIndex.Identify).Data[id]=new DynamicTitleData(line.Data,' ',line.Color); //给一个空的标题

        frame.ChartPaint.push(chart);
    }

    this.CreateNumberText=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartSingleText();
        chart.Canvas=hqChart.Canvas;

        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;
        if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
        else chart.Color=this.GetDefaultColor(id);

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData.Value;
        chart.Text=varItem.Draw.DrawData.Text;

        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    }

    //创建图标
    this.CreateIcon=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartSingleText();
        chart.Canvas=hqChart.Canvas;
        chart.TextAlign='center';

        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData;
        var icon=varItem.Draw.Icon;
        if (icon.IconFont==true)
        {
            chart.IconFont={ Family:icon.Family, Text:icon.Symbol, Color:icon.Color };
        }
        else
        {
            chart.Text=icon.Symbol;
            if (varItem.Color) chart.Color=this.GetColor(varItem.Color);
            else if (icon.Color) chart.Color=icon.Color;
            else chart.Color='rgb(0,0,0)';
        }
        
        //hqChart.TitlePaint[titleIndex].Data[id]=new DynamicTitleData(bar.Data,varItem.Name,bar.Color);

        frame.ChartPaint.push(chart);
    }

    //创建通道
    this.CreateChannel=function(hqChart,windowIndex,varItem,id)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartChannel();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        if(varItem.Draw.AreaColor) chart.AreaColor=varItem.Draw.AreaColor;
        else if (varItem.Color) chart.AreaColor=this.GetColor(varItem.Color);
        else chart.AreaColor=this.GetDefaultColor(id);

        if (varItem.Draw.Border.Color) chart.LineColor=varItem.Draw.Border.Color;
        else chart.LineColor=null;

        if (varItem.Draw.Border.Dotted) chart.LineDotted=varItem.Draw.Border.Dotted;
        if (varItem.Draw.Border.Width>0) chart.LineWidth=varItem.Draw.Border.Width;

        //let titleIndex=windowIndex+1;
        chart.Data.Data=varItem.Draw.DrawData;
        frame.ChartPaint.push(chart);
    }

    //
    this.CreatePartLine=function(hqChart,windowIndex,varItem,i)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartPartLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name=varItem.Name;
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        chart.Data.Data=varItem.Draw.DrawData;
        frame.ChartPaint.push(chart);
    }

    //创建K线
    this.CreateSelfKLine=function(hqChart,windowIndex,hisData)
    {
        var overlayIndex=this.OverlayIndex;
        var frame=overlayIndex.Frame;
        let chart=new ChartKLine();
        chart.Canvas=hqChart.Canvas;
        chart.Name="Self Kline"
        chart.ChartBorder=frame.Frame.ChartBorder;
        chart.ChartFrame=frame.Frame;
        chart.Identify=overlayIndex.Identify;

        chart.Data=hisData
        chart.IsShowMaxMinPrice=false;
        chart.IsShowKTooltip=false;
        chart.DrawType=this.KLineType;

        frame.ChartPaint.push(chart);
    }

    //给一个默认的颜色
    this.GetDefaultColor=function(id)
    {
        let COLOR_ARRAY=
        [
            "rgb(24,71,178)",
            "rgb(42,230,215)",
            "rgb(252,96,154)",
            "rgb(0,128,255)",
            "rgb(229,0,79)",
            "rgb(68,114,196)",
            "rgb(255,174,0)",
            "rgb(25,199,255)",
            "rgb(175,95,162)",
            "rgb(236,105,65)",
        ];

        let number=parseInt(id);
        return COLOR_ARRAY[number%(COLOR_ARRAY.length-1)];
    }
}

//后台执行指标
function APIScriptIndex(name,script,args,option)
{
    this.newMethod=ScriptIndex;   //派生
    this.newMethod(name,script,args,option);
    delete this.newMethod;

    this.ApiUrl;    //指标执行api地址
    this.HQDataType;

    if (option.API) 
    {
        if (option.API.Url) this.ApiUrl=option.API.Url;
        if (option.API.Name) this.Name=this.ID=option.API.Name;
        if (option.API.ID) this.ID=option.API.ID;
    }

    this.ExecuteScript=function(hqChart,windowIndex,hisData)
    {
        JSConsole.Complier.Log('[APIScriptIndex::ExecuteScript] name, Arguments ', this.Name,this.Arguments );

        //数据类型
        let hqDataType=HQ_DATA_TYPE.KLINE_ID;   //默认K线
        if (hqChart.ClassName==='MinuteChartContainer' || hqChart.ClassName==='MinuteChartHScreenContainer') 
        {
            if (hqChart.DayCount>1) hqDataType=HQ_DATA_TYPE.MULTIDAY_MINUTE_ID; //多日分钟
            else hqDataType=HQ_DATA_TYPE.MINUTE_ID;                             //分钟数据
        }
        else if (hqChart.ClassName==='HistoryMinuteChartContainer') 
        {
            hqDataType=HQ_DATA_TYPE.HISTORY_MINUTE_ID;   //历史分钟
        }

        var args=[];
        if (this.Arguments)
        {
            for(var i in this.Arguments)
            {
                var item=this.Arguments[i];
                args.push({name:item.Name, value:item.Value});
            }
        }

        var requestCount=hqChart.GetRequestDataCount();
        var self=this;
        var postData =
        { 
            indexname:this.ID,  symbol: hqChart.Symbol, script:this.Script, args:args,
            period:hqChart.Period, right:hqChart.Right, maxdatacount:requestCount.MaxRequestDataCount, maxminutedaycount:requestCount.MaxRequestMinuteDayCount, hqdatatype: hqDataType
        };
        if (hqDataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID || hqDataType==HQ_DATA_TYPE.MINUTE_ID) postData.daycount=hqChart.DayCount;
        this.HQDataType=hqDataType;

        if (hqChart.NetworkFilter)
        {
            var obj=
            {
                Name:'APIScriptIndex::ExecuteScript', //类名::
                Explain:'指标计算',
                Request:{ Url:self.ApiUrl,  Type:'POST', Data: postData }, 
                Self:this,
                HQChart:hqChart,
                PreventDefault:false
            };
            
            hqChart.NetworkFilter(obj, function(data) 
            { 
                self.RecvAPIData(data,hqChart,windowIndex,hisData);
            });

            if (obj.PreventDefault==true) return;   //已被上层替换,不调用默认的网络请求
        }

        JSNetwork.HttpRequest({
            url: self.ApiUrl,
            data: postData, 
            type:"post",
            dataType: "json",
            async:true, 
            success: function (recvData)
            {
                self.RecvAPIData(recvData,hqChart,windowIndex,hisData);
            },
            error: function(request)
            {
                self.RecvError(request);
            }
        });
    }

    this.RecvAPIData=function(data,hqChart,windowIndex,hisData)
    {
        JSConsole.Complier.Log('[APIScriptIndex::RecvAPIData] recv data ', this.Name,data );
        if (data.code!=0) return;

        if (data.outdata && data.outdata.name) this.Name=data.outdata.name;

        this.Arguments=[];
        if (data.outdata.args)
        {
            for(var i in data.outdata.args)
            {
                var item= data.outdata.args[i];
                this.Arguments.push({Name:item.name, Value:item.value});
            }
        }

        if (this.HQDataType==HQ_DATA_TYPE.KLINE_ID)
        {
            this.OutVar=this.FittingData(data.outdata,hqChart);
            JSConsole.Complier.Log('[APIScriptIndex::RecvAPIData] conver to OutVar ', this.OutVar);
        }
        else
        {
            this.OutVar=this.FittingMinuteData(data.outdata,hqChart);   //走势图数据
        }
        this.BindData(hqChart,windowIndex,hisData);

        if (this.IsLocked==false) //不上锁
        {
            hqChart.Frame.SubFrame[windowIndex].Frame.SetLock(null);
        }
        else    //上锁
        {
            let lockData={ IsLocked:true,Callback:this.LockCallback,IndexName:this.Name ,ID:this.LockID,
                BG:this.LockBG,Text:this.LockText,TextColor:this.LockTextColor, Font:this.LockFont, Count:this.LockCount, MinWidth:this.LockMinWidth };
            hqChart.Frame.SubFrame[windowIndex].Frame.SetLock(lockData);
        }

        hqChart.UpdataDataoffset();           //更新数据偏移
        hqChart.UpdateFrameMaxMin();          //调整坐标最大 最小值
        hqChart.Draw();

        if (hqChart.GetIndexEvent)
        {
            var event=hqChart.GetIndexEvent();  //指标计算完成回调
            if (event)
            {
                var self=param.Self;
                var data={ OutVar:self.OutVar, WindowIndex: windowIndex, Name: this.Name, Arguments: this.Arguments, HistoryData: hisData, 
                        Stock: {Symbol:hqChart.Symbol,Name:hqChart.Name} };
                event.Callback(event,data,this);
            }
        }
    }

    this.FittingArray=function(sourceData,date,time,hqChart,arrayType)  //arrayType 0=单值数组 1=结构体
    {
        var kdata=hqChart.ChartPaint[0].Data;   //K线

        var arySingleData=[];
        for(var i in sourceData)
        {
            var value=sourceData[i];
            var indexItem=new SingleData(); //单列指标数据
            indexItem.Date=date[i];
            if (time && i<time.length) indexItem.Time=time[i];
            indexItem.Value=value;
            arySingleData.push(indexItem);
        }

        var aryFittingData;
        if (ChartData.IsDayPeriod(hqChart.Period))
            aryFittingData=kdata.GetFittingData(arySingleData);        //数据和主图K线拟合
        else
            aryFittingData=kdata.GetMinuteFittingData(arySingleData);  //数据和主图K线拟合

        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        var result;
        if (arrayType==1) result=bindData.GetObject();
        else result=bindData.GetValue();
        return result;
    }

    this.FittingMultiLine=function(sourceData,date,time,hqChart)
    {
        var kdata=hqChart.ChartPaint[0].Data;   //K线

        if (ChartData.IsDayPeriod(hqChart.Period,true))  //日线
        {
            var aryPoint=[];
            for(var i in sourceData)
            {
                var item=sourceData[i];
                for(var j in item.Point)
                {
                    var point=item.Point[j];
                    aryPoint.push(point);
                }
            }

            aryPoint.sort(function(a,b) { return a.Date-b.Date; });
            kdata.GetDateIndex(aryPoint);
            return sourceData;
        }
        else if (ChartData.IsMinutePeriod(hqChart.Period,true)) //分钟线
        {
            var aryPoint=[];
            for(var i in sourceData)
            {
                var item=sourceData[i];
                for(var j in item.Point)
                {
                    var point=item.Point[j];
                    aryPoint.push(point);
                }
            }

            aryPoint.sort(function(a,b) 
                { 
                    if (a.Date==b.Date) return a.Time-b.Time;
                    return a.Date-b.Date; 
                }
            );

            kdata.GetDateTimeIndex(aryPoint);
            return sourceData;
        }
        
        return null;
    }

    this.FittingMultiText=function(sourceData,date,time,hqChart)
    {
        var kdata=hqChart.ChartPaint[0].Data;   //K线

        if (ChartData.IsDayPeriod(hqChart.Period,true))  //日线
        {
            sourceData.sort(function(a,b) { return a.Date-b.Date; });
            kdata.GetDateIndex(sourceData);
            return sourceData;
        }
        else if (ChartData.IsMinutePeriod(hqChart.Period,true)) //分钟线
        {
            sourceData.sort(function(a,b) 
                { 
                    if (a.Date==b.Date) return a.Time-b.Time;
                    return a.Date-b.Date; 
                }
            );

            kdata.GetDateTimeIndex(sourceData);
            return sourceData;
        }
        
        return null;
    }

    this.FittingData=function(jsonData, hqChart)
    {
        var outVar=jsonData.outvar;
        var date=jsonData.date;
        var time=jsonData.time;
        var kdata=hqChart.ChartPaint[0].Data;

        //把数据拟合到kdata上
        var result=[];
        
        for(var i in outVar)
        {
            var item=outVar[i];
            var indexData=[];
            var outVarItem={Name:item.name,Type:item.type};
            if (item.color) outVarItem.Color=item.color;
            if (item.data)
            {
                outVarItem.Data=this.FittingArray(item.data,date,time,hqChart);

                if (item.color) outVarItem.Color=item.color;
                if (item.linewidth>=1) outVarItem.LineWidth=item.linewidth; 
                if (item.isshow==false) outVarItem.IsShow = false;
                if (item.isexdata==true) outVarItem.IsExData = true;

                result.push(outVarItem);
            }
            else if (item.Draw)
            {
                var draw=item.Draw;
                var drawItem={};
                if (draw.DrawType=='DRAWICON')  //图标
                {
                    drawItem.Icon=draw.Icon;
                    drawItem.Name=draw.Name;
                    drawItem.DrawType=draw.DrawType;
                    drawItem.DrawData=this.FittingArray(draw.DrawData,date,time,hqChart);
                    outVarItem.Draw=drawItem;

                    result.push(outVarItem);
                }
                else if (draw.DrawType=='DRAWTEXT') //文本
                {
                    drawItem.Text=draw.Text;
                    drawItem.Name=draw.Name;
                    drawItem.DrawType=draw.DrawType;
                    drawItem.DrawData=this.FittingArray(draw.DrawData,date,time,hqChart);
                    outVarItem.Draw=drawItem;

                    result.push(outVarItem);
                }
                else if (draw.DrawType=='STICKLINE')    //柱子
                {
                    drawItem.Name=draw.Name;
                    drawItem.Type=draw.Type;
                    drawItem.Width=draw.Width;
                    drawItem.DrawType=draw.DrawType;
                    drawItem.DrawData=this.FittingArray(draw.DrawData,date,time,hqChart,1);
                    outVarItem.Draw=drawItem;

                    result.push(outVarItem);
                }
                else if (draw.DrawType=='MULTI_LINE')
                {
                    drawItem.Text=draw.Text;
                    drawItem.Name=draw.Name;
                    drawItem.DrawType=draw.DrawType;
                    drawItem.DrawData=this.FittingMultiLine(draw.DrawData,date,time,hqChart);
                    outVarItem.Draw=drawItem;

                    result.push(outVarItem);
                }
                else if (draw.DrawType=='MULTI_TEXT')
                {
                    drawItem.Text=draw.Text;
                    drawItem.Name=draw.Name;
                    drawItem.DrawType=draw.DrawType;
                    drawItem.DrawData=this.FittingMultiText(draw.DrawData,date,time,hqChart);
                    outVarItem.Draw=drawItem;

                    result.push(outVarItem);
                }
                else if (draw.DrawType=='MULTI_SVGICON')
                {
                    drawItem.Text=draw.Text;
                    drawItem.Name=draw.Name;
                    drawItem.DrawType=draw.DrawType;
                    drawItem.DrawData={ Icon:this.FittingMultiText(draw.DrawData.Icon,date,time,hqChart), Family:draw.DrawData.Family };
                    outVarItem.Draw=drawItem;

                    result.push(outVarItem);
                }
            }
        }

        return result;
    }

    this.FittingMinuteData=function(jsonData, hqChart)
    {
        var outVar=jsonData.outvar;
        var date=jsonData.date;
        var time=jsonData.time;
        var result=[];
        
        for(var i in outVar)
        {
            item=outVar[i];
            var outVarItem={Name:item.name,Type:item.type}
            if (item.data)
            {
                outVarItem.Data=this.FittingMinuteArray(item.data,date,time,hqChart);
                if (item.color) outVarItem.Color=item.color;
                if (item.linewidth>=1) outVarItem.LineWidth=item.linewidth;
                if (item.isshow==false) outVarItem.IsShow = false;
                if (item.isexdata==true) outVarItem.IsExData = true;

                result.push(outVarItem);
            }
        }

        return result;
    }

    this.FittingMinuteArray=function(sourceData,date,time,hqChart)
    {
        var minutedata=hqChart.SourceData;;   //分钟线

        var arySingleData=[];
        for(var i in sourceData)
        {
            var value=sourceData[i];
            var indexItem=new SingleData(); //单列指标数据
            indexItem.Date=date[i];
            if (time && i<time.length) indexItem.Time=time[i];
            indexItem.Value=value;
            arySingleData.push(indexItem);
        }

        var aryFittingData=minutedata.GetMinuteFittingData(arySingleData);  //数据和主图K线拟合
        var bindData=new ChartData();
        bindData.Data=aryFittingData;
        var result=bindData.GetValue();
        return result;
    }

}

// 本地json数据指标
function LocalJsonDataIndex(name,args,option)
{
    this.newMethod=ScriptIndex;   //派生
    this.newMethod(name,null,args,null);
    delete this.newMethod;

    this.JsonData;  //json格式数据
    if (option.JsonData) this.JsonData=option.JsonData;

    this.RequestData=function(hqChart,windowIndex,hisData)
    {
        if (!this.JsonData)
        {
            console.warn("[LocalJsonDataIndex::RequestData] JsonData is null");
            if (param.HQChart.ScriptErrorCallback) param.HQChart.ScriptErrorCallback('json 数据不能为空');
            return;
        }

        this.OutVar=this.FittingData(this.JsonData,hisData);
        this.BindData(hqChart,windowIndex,hisData);
    }

    this.FittingData=function(jsonData, hisData)
    {
        outVar=jsonData.OutVar;
        date=jsonData.Date;
        time=jsonData.Time;

        result=[];

        for(i in outVar)
        {
            item=outVar[i];
            JSConsole.Complier.Log('[LocalJsonDataIndex::FittingData] item', item);
            if (item.Type!=0 && item.Type!=3)   //复杂的图形不支持
            {
                console.warn("[LocalJsonDataIndex::FittingData] can't support ", item.Name, item.Type);
                continue; 
            }
            
            var indexData=[];
            outVarItem={Name:item.Name,Type:item.Type}
            for(j in item.Data)
            {
                var indexItem=new SingleData(); //单列指标数据
                indexItem.Date=date[j];
                if (time && j<time.length) indexItem.Time=time[j];
                indexItem.Value=item.Data[j];
                indexData.push(indexItem);
            }

            if (hisData.Period<4)
                var aryFittingData=hisData.GetFittingData(indexData); //数据和主图K线拟合
            else
                var aryFittingData=hisData.GetMinuteFittingData(indexData); //数据和主图K线拟合
            var bindData=new ChartData();
            bindData.Data=aryFittingData;
            outVarItem.Data=bindData.GetValue();
            result.push(outVarItem)
        }

        return result;
    }
}


////////////////////////////////////////////////////////////////////////
//  无UI指标执行
// obj: { Name:指标名字 , ID:指标ID , Script:指标脚本, Args:指标参数, ErrorCallback:错误回调 }
//
///////////////////////////////////////////////////////////////////////
function ScriptIndexConsole(obj)    
{
    this.ID;                //指标ID
    this.Name;              //指标名字
    this.Script;            //脚本
    this.Arguments=[];      //参数
    this.ClassName='ScriptIndexConsole';
    this.ErrorCallback;     //执行错误回调
    this.FinishCallback;    //执行完成回调
    this.IsSectionMode=false;   //截面报表模式

    if (obj)
    {
        if (obj.Name) this.Name=obj.Name;
        if (obj.Script) this.Script=obj.Script;
        if (obj.ID) this.ID=obj.ID;
        if (obj.Args) this.Arguments=obj.Args;
        if (obj.ErrorCallback) this.ErrorCallback=obj.ErrorCallback;
        if (obj.FinishCallback) this.FinishCallback=obj.FinishCallback;
        if (obj.IsSectionMode) this.IsSectionMode=obj.IsSectionMode;
    }

    //执行脚本
    //obj:
    //     HQDataType:(HQ_DATA_TYPE), Period:周期, Right:复权
    //     Stock: {Name:, Symbol:}, Request: { MaxDataCount:请求K线数据个数, MaxMinuteDayCount:请求分钟数据天数, TradeDate: 历史走势图才用}, :
    //     Data: 当前计算数据(周期|复权以后), Source:  原始股票数据 
    this.ExecuteScript=function(obj)
    {
        this.OutVar=[];
        let param= { Self:this };

        let option=
        {
            HQDataType:obj.HQDataType,
            Symbol:obj.Stock.Symbol, 
            Name:obj.Stock.Name,
            Period:obj.Period,
            Right:obj.Right,
            Callback:this.RecvResultData, CallbackParam:param,
            Async:true,
            MaxRequestDataCount:obj.Request.MaxDataCount,
            MaxRequestMinuteDayCount:obj.Request.MaxMinuteDayCount,
            Arguments:this.Arguments,
            IsSectionMode:this.IsSectionMode,
            ClassName:'ScriptIndexConsole',
        };

        if (obj.HQDataType===HQ_DATA_TYPE.HISTORY_MINUTE_ID) option.TrateDate=obj.Request.TradeDate;
        if (obj.HQDataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID) option.DayCount=obj.DayCount;
        
        let code=this.Script;
        let run=JSComplier.Execute(code,option,this.ErrorCallback);
    }

    this.RecvResultData=function(outVar,param)
    {
        var self=param.Self;
        var jsExec=param.JSExecute;

        var date=null;
        if (jsExec.SymbolData.Data)
            date=jsExec.SymbolData.Data.GetDate();
        
        var result=
        { 
            Out:outVar, 
            Date:date,  //日期对应 上面的数据
            Stock:{ Name:jsExec.SymbolData.Name, Symbol:jsExec.SymbolData.Symbol }, //股票信息
            Index: { Name:self.Name, ID:self.ID }   //指标信息
        };

        if (jsExec.SymbolData.DataType==HQ_DATA_TYPE.MULTIDAY_MINUTE_ID || jsExec.SymbolData.DataType==HQ_DATA_TYPE.MINUTE_ID) result.Time=jsExec.SymbolData.Data.GetTime();
        else if (jsExec.SymbolData.DataType==HQ_DATA_TYPE.KLINE_ID && ChartData.IsMinutePeriod(jsExec.SymbolData.Period,true)) result.Time=jsExec.SymbolData.Data.GetTime();
        //JSConsole.Complier.Log('[ScriptIndexConsole::RecvResultData] outVar ', outVar);
        if (self.FinishCallback) self.FinishCallback(result, param.JSExecute);
    }
}

ScriptIndexConsole.SetDomain = function (domain, cacheDomain)   //修改API地址
{
    JSComplier.SetDomain(domain,cacheDomain);
}


/* 测试例子
var code1='VARHIGH:IF(VAR1<=REF(HH,-1),REF(H,BARSLAST(VAR1>=REF(HH,1))),DRAWNULL),COLORYELLOW;';
var code2='VAR1=((SMA(MAX((CLOSE - LC),0),3,1) / SMA(ABS((CLOSE - LC)),3,1)) * 100);';
var code3='mm1=1-2*-9+20;';

JSConsole.Complier.Log(code1+code2)
var tokens=JSComplier.Tokenize(code1+code2);
var ast=JSComplier.Parse(code2+code1);

JSConsole.Complier.Log(ast);
*//*
   Copyright (c) 2018 jones
 
    http://www.apache.org/licenses/LICENSE-2.0

   开源项目 https://github.com/jones2000/HQChart
 
   jones_2000@163.com

   配色
*/

/*
    不同风格行情配置文件
    !!手机上字体大小需要*分辨率比
*/

/* umychart.js 里面已经有了
function GetDevicePixelRatio()
{
    return window.devicePixelRatio || 1;
}
*/

//黑色风格
var BLACK_STYLE=
{
    BGColor:'rgb(0,0,0)', //背景色
    TooltipBGColor: "rgb(255, 255, 255)", //背景色
    TooltipAlpha: 0.92,                  //透明度

    SelectRectBGColor: "rgba(1,130,212,0.06)", //背景色
    //  SelectRectAlpha: 0.06;                  //透明度

    //K线颜色
    UpBarColor: "rgb(238,21,21)",   //上涨
    DownBarColor: "rgb(25,158,0)",  //下跌
    UnchagneBarColor: "rgb(228,228,228)", //平盘

    Minute: 
    {
        VolBarColor: "rgb(255,236,0)",
        PriceColor: "rgb(25,180,231)",
        AreaPriceColor:"rgba(63,158,255,.3)",
        AvPriceColor: "rgb(255,236,0)",
        PositionColor:'rgb(218,165,32)', 
    },


    DefaultTextColor: "rgb(101,104,112)",
    DefaultTextFont: 14*GetDevicePixelRatio() +'px 微软雅黑',
    TitleFont: 13*GetDevicePixelRatio() +'px 微软雅黑',    //标题字体(动态标题 K线及指标的动态信息字体)

    UpTextColor: "rgb(238,21,21)",
    DownTextColor: "rgb(25,158,0)",
    UnchagneTextColor: "rgb(101,104,112)",
    CloseLineColor: 'rgb(178,34,34)',

    FrameBorderPen: "rgba(236,236,236,0.13)",     //边框
    FrameSplitPen: "rgba(236,236,236,0.13)",          //分割线
    FrameSplitTextColor: "rgb(101,104,112)",     //刻度文字颜色
    FrameSplitTextFont: 12*GetDevicePixelRatio() +"px 微软雅黑",        //坐标刻度文字字体
    FrameTitleBGColor: "rgb(0,0,0)",      //标题栏背景色

    Frame:{ XBottomOffset:1*GetDevicePixelRatio() },   //X轴文字向下偏移

    FrameLatestPrice : {
        TextColor:'rgb(255,255,255)',   //最新价格文字颜色
        UpBarColor:"rgb(238,21,21)",    //上涨
        DownBarColor:"rgb(25,158,0)",   //下跌
        UnchagneBarColor:"rgb(190,190,190)",   //平盘
        BGAlpha:0.6
    },

    CorssCursorBGColor: "rgb(43,54,69)",            //十字光标背景
    CorssCursorTextColor: "rgb(255,255,255)",
    CorssCursorTextFont: 12*GetDevicePixelRatio() +"px 微软雅黑",
    CorssCursorHPenColor: "rgb(130,130,130)",           //十字光标线段颜色
    CorssCursorVPenColor: "rgb(130,130,130)",           //十字光标线段颜色

    KLine:
    {
        MaxMin: { Font: 12*GetDevicePixelRatio() +'px 微软雅黑', Color: 'rgb(111,111,111)' },   //K线最大最小值显示
        Info:  //信息地雷
        {
            Investor:
                {
                    ApiUrl:'https://opensource.zealink.com/API/NewsInteract', //互动易
                    IconFont: { Family:'iconfont', Text:'\ue631' , HScreenText:'\ue684', Color:'#1c65db'} //SVG 文本
                },
                Announcement:                                           //公告
                {
                    ApiUrl:'https://opensource.zealink.com/API/ReportList',
                    IconFont: { Family:'iconfont', Text:'\ue633', HScreenText:'\ue685', Color:'#f5a521' }, //SVG 文本
                    IconFont2: { Family:'iconfont', Text:'\ue634', HScreenText:'\ue686', Color:'#ed7520' } //SVG 文本 //季报
                },
                Pforecast:  //业绩预告
                {
                    ApiUrl:'https://opensource.zealink.com/API/StockHistoryDay',
                    IconFont: { Family:'iconfont', Text:'\ue62e', HScreenText:'\ue687', Color:'#986cad' } //SVG 文本
                },
                Research:   //调研
                {
                    ApiUrl:'https://opensource.zealink.com/API/InvestorRelationsList',
                    IconFont: { Family:'iconfont', Text:'\ue632', HScreenText:'\ue688', Color:'#19b1b7' } //SVG 文本
                },
                BlockTrading:   //大宗交易
                {
                    ApiUrl:'https://opensource.zealink.com/API/StockHistoryDay',
                    IconFont: { Family:'iconfont', Text:'\ue630', HScreenText:'\ue689', Color:'#f39f7c' } //SVG 文本
                },
                TradeDetail:    //龙虎榜
                {
                    ApiUrl:'https://opensource.zealink.com/API/StockHistoryDay',
                    IconFont: { Family:'iconfont', Text:'\ue62f', HScreenText:'\ue68a' ,Color:'#b22626' } //SVG 文本
                }

        },
        NumIcon:
        {
            Color:'rgb(251,80,80)',Family:'iconfont',
            Text:[  '\ue649',
                    '\ue63b','\ue640','\ue63d','\ue63f','\ue645','\ue641','\ue647','\ue648','\ue646','\ue636',
                    '\ue635','\ue637','\ue638','\ue639','\ue63a','\ue63c','\ue63e','\ue642','\ue644','\ue643'
                ]
        },
        TradeIcon:  //交易指标 图标
        {
            Family:'iconfont', 
            Buy: { Color:'rgb(255,15,4)', Text:'\ue683', HScreenText:'\ue682'}, 
            Sell: { Color:'rgb(64,122,22)', Text:'\ue681',HScreenText:'\ue680'},
        }
    },

    Index: 
    {      
        LineColor:  //指标线段颜色
        [
            "rgb(255,189,09)",
            "rgb(22,198,255)",
            "rgb(174,35,161)",
            "rgb(236,105,65)",
            "rgb(68,114,196)",
            "rgb(229,0,79)",
            "rgb(0,128,255)",
            "rgb(252,96,154)",
            "rgb(42,230,215)",
            "rgb(24,71,178)",
        ],
        NotSupport: { Font: "14px 微软雅黑", TextColor: "rgb(52,52,52)" }
    },
      
    ColorArray:       //自定义指标默认颜色
    [
        "rgb(255,174,0)",
        "rgb(25,199,255)",
        "rgb(175,95,162)",
        "rgb(236,105,65)",
        "rgb(68,114,196)",
        "rgb(229,0,79)",
        "rgb(0,128,255)",
        "rgb(252,96,154)",
        "rgb(42,230,215)",
        "rgb(24,71,178)",
    ],

    DrawPicture:  //画图工具
    {
        LineColor: "rgb(30,144,255)",
        PointColor: "rgb(105,105,105)",
    },

    TooltipPaint : 
    {
        BGColor:'rgba(20,20,20,0.8)',    //背景色
        BorderColor:'rgb(210,210,210)',     //边框颜色
        TitleColor:'rgb(210,210,210)',       //标题颜色
        TitleFont:13*GetDevicePixelRatio() +'px 微软雅黑'   //字体
    },

    //走势图 信息地雷
    MinuteInfo:
    {
        TextColor: 'rgb(84,143,255)',
        Font: 14*GetDevicePixelRatio() +'px 微软雅黑',
        PointColor:'rgb(38,113,254)',
        LineColor:'rgb(120,167,255)',
        TextBGColor:'rgba(255,255,255,1)'
    }
    
};

var STYLE_TYPE_ID=
{
    BLACK_ID:1, //黑色风格
}

function HQChartStyle()
{

}

HQChartStyle.GetStyleConfig=function(styleid)    //获取一个风格的配置变量
{
  switch (styleid)
  {
      case STYLE_TYPE_ID.BLACK_ID:
          return BLACK_STYLE;
      default:
          return null;
  }
}





/*
    单独的网络接口
*/
function JSNetwork() { }

JSNetwork.HttpRequest=function(obj) //使用uniapp 网络接口接口  !!每家平台都喜欢自定义接口, 低层不都是一样的嘛
{
	let method=obj.type.toUpperCase();
	uni.request({
		url: obj.url,
		data: obj.data,
		method:method,
		success: (res)=> 
		{ 
			obj.success(res.data); 
		},
		fail: obj.error,
	});
}

//把给外界调用的方法暴露出来
export default  {
   
    //类导出
    JSChart:JSChart,                            //行情图形库
    ChartData:ChartData,
    MARKET_SUFFIX_NAME:MARKET_SUFFIX_NAME,      //判断股票属性
    IFrameSplitOperator:IFrameSplitOperator,    //格式化字符串方法
    JSKLineInfoMap:JSKLineInfoMap,
    JSCHART_EVENT_ID:JSCHART_EVENT_ID,          //可以订阅的事件类型
    JSCHART_OPERATOR_ID:JSCHART_OPERATOR_ID,    //图形控制类型
    JSAlgorithm:JSAlgorithm,                    //算法类
    JSComplier:JSComplier,                      //指标编译器
    JSIndexScript:JSIndexScript,                //系统指标库
    GetDevicePixelRatio,GetDevicePixelRatio,
    
    ScriptIndexConsole:ScriptIndexConsole,      //指标执行 无UI
    //style.js相关
    STYLE_TYPE_ID:STYLE_TYPE_ID,
    HQChartStyle:HQChartStyle,                  //预定义全局的配色 黑

    JSConsole:JSConsole,    //日志输出
}