﻿/*
根据 http://Welkin.ifree.com/test.js 原理重构而成。
AiryAi@163.com, 24 April, 2009
*/
// 测试器主体部分
(function() {
    window.HxTest = {};
    // 创建简-繁映射表
    window.HxTest.Chars = {
        JT: "皑蔼碍爱翱袄奥坝罢摆败颁办绊帮绑镑谤剥饱宝报鲍辈贝钡狈备惫绷笔毕毙币闭边编贬变辩辫标鳖别瘪濒滨宾摈饼并拨钵铂驳卜补财参蚕残惭惨灿苍舱仓沧厕侧册测层诧搀掺蝉馋谗缠铲产产阐颤场尝长偿肠厂畅钞车彻尘陈衬撑称惩诚骋痴迟驰耻齿炽冲虫宠畴踌筹绸丑橱厨锄雏础储触处传疮闯创锤纯绰辞词赐聪葱囱从丛凑蹿窜错达带贷担单郸掸胆惮诞弹当挡党荡档捣岛祷导盗灯邓敌涤递缔颠点垫电淀钓调迭谍叠钉顶锭订丢东动栋冻斗犊独读赌镀锻断缎兑队对吨顿钝夺堕鹅额讹恶饿儿尔饵贰发罚阀珐矾钒烦范贩饭访纺飞诽废费纷坟奋愤粪丰枫锋风疯冯缝讽凤肤辐抚辅赋复复负讣妇缚该钙盖干赶秆赣冈刚钢纲岗皋镐搁鸽阁铬个给龚宫巩贡钩沟构购够蛊顾剐关观馆惯贯广规硅归龟闺轨诡柜贵刽辊滚锅国过骇韩汉号阂鹤贺横轰鸿红后壶护沪户哗华画划话怀坏欢环还缓换唤痪焕涣黄谎挥辉毁贿秽会烩汇讳诲绘荤浑伙获货祸击机积饥讥鸡绩缉极辑级挤迹几蓟剂济计记际继纪夹荚颊贾钾价驾歼监坚笺间艰缄茧检碱硷拣捡简俭减荐槛鉴践贱见键舰剑饯渐溅涧将浆蒋桨奖讲酱胶浇骄娇搅铰矫侥脚饺缴绞轿较秸阶节茎鲸惊经颈静镜径痉竞净纠厩旧驹举据锯惧剧鹃绢杰洁结诫届紧锦仅谨进晋烬尽劲荆九觉决诀绝钧军骏开凯颗壳课垦恳抠库裤夸块侩宽矿旷况亏岿窥馈溃扩阔蜡腊莱来赖蓝栏拦篮阑兰澜谰揽览懒缆烂滥捞劳涝乐镭垒类泪篱离里鲤礼丽厉励砾历沥隶俩联莲连镰怜涟帘敛脸链恋炼练粮凉两辆谅疗辽镣猎临邻鳞凛赁龄铃凌灵岭领馏刘龙聋咙笼垄拢陇楼娄搂篓芦卢颅庐炉掳卤虏鲁赂禄录陆驴吕铝侣屡缕虑滤绿峦挛孪滦乱抡轮伦仑沦纶论萝裸罗逻锣箩骡骆络妈玛码蚂马骂吗买麦卖迈脉瞒馒蛮满谩猫锚铆贸么霉没镁门闷们锰梦谜弥觅幂绵缅庙灭悯闽鸣铭谬谋亩钠纳难挠脑恼闹馁内拟腻撵捻酿鸟聂啮镊镍柠狞宁拧泞钮纽脓浓农疟诺欧鸥殴呕沤盘庞赔喷鹏骗飘频贫苹凭评泼颇扑铺朴谱栖凄脐齐骑岂启气弃讫牵扦钎铅迁签谦钱钳潜浅谴堑枪呛墙蔷强抢锹桥乔侨翘窍窃钦亲寝轻氢倾顷请庆琼穷趋区躯驱龋颧权劝却鹊确让饶扰绕热韧认纫荣绒软锐闰润洒萨鳃赛三伞丧骚扫涩杀纱筛晒删闪陕赡缮伤赏烧绍赊摄慑设绅审婶肾渗声绳胜圣师狮湿诗尸时蚀实识驶势适释饰视试寿兽枢输书赎属术树竖数帅双谁税顺说硕烁丝饲耸怂颂讼诵擞苏诉肃虽随绥岁孙损笋缩琐锁獭挞台抬态摊贪瘫滩坛谭谈叹汤烫涛绦讨腾誊锑题体屉条贴铁厅听烃铜统头秃图涂团颓蜕脱鸵驮驼椭洼袜弯湾顽万网韦违围为伪潍维苇伟伪纬谓卫温闻纹稳问瓮挝蜗涡窝卧呜钨乌污诬无芜吴坞雾务误锡牺袭习铣戏细虾辖峡侠狭厦吓锨鲜纤咸贤衔闲显险现献县馅羡宪线厢镶乡详响项萧嚣销晓啸蝎协挟携胁谐写泻谢锌衅兴汹锈绣虚嘘须许叙绪续轩悬选癣绚学勋询寻驯训讯逊压鸦鸭哑亚讶阉烟盐严颜阎艳厌砚彦谚验鸯杨扬疡阳痒养样瑶摇尧遥窑谣药爷页业叶医铱颐遗仪彝蚁艺亿忆义诣议谊译异绎荫阴银饮隐樱婴鹰应缨莹萤营荧蝇赢颖哟拥佣痈踊咏涌优忧邮铀犹游诱舆鱼渔娱与屿语吁欲御狱誉预驭鸳渊辕园员圆缘远愿约跃钥岳粤悦阅云郧匀陨运蕴酝晕韵杂灾载攒暂赞赃脏凿枣灶责择则泽贼赠扎札轧铡闸栅诈斋债毡盏斩辗崭栈战绽张涨帐账胀赵蛰辙锗这贞针侦诊镇阵挣睁狰争帧郑证织职执纸挚制掷帜质滞冢钟终种肿众众诌轴皱昼骤猪诸诛烛瞩嘱贮铸筑驻专砖转赚桩庄装妆壮状锥赘坠缀谆浊兹资渍踪综总纵邹诅组钻奸0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
        FT: "皚藹礙愛翺襖奧壩罷擺敗頒辦絆幫綁鎊謗剝飽寶報鮑輩貝鋇狽備憊繃筆畢斃幣閉邊編貶變辯辮標鼈別癟瀕濱賓擯餅並撥缽鉑駁蔔補財參蠶殘慚慘燦蒼艙倉滄廁側冊測層詫攙摻蟬饞讒纏鏟産產闡顫場嘗長償腸廠暢鈔車徹塵陳襯撐稱懲誠騁癡遲馳恥齒熾沖蟲寵疇躊籌綢醜櫥廚鋤雛礎儲觸處傳瘡闖創錘純綽辭詞賜聰蔥囪從叢湊躥竄錯達帶貸擔單鄲撣膽憚誕彈當擋黨蕩檔搗島禱導盜燈鄧敵滌遞締顛點墊電澱釣調叠諜疊釘頂錠訂丟東動棟凍鬥犢獨讀賭鍍鍛斷緞兌隊對噸頓鈍奪墮鵝額訛惡餓兒爾餌貳發罰閥琺礬釩煩範販飯訪紡飛誹廢費紛墳奮憤糞豐楓鋒風瘋馮縫諷鳳膚輻撫輔賦複復負訃婦縛該鈣蓋幹趕稈贛岡剛鋼綱崗臯鎬擱鴿閣鉻個給龔宮鞏貢鈎溝構購夠蠱顧剮關觀館慣貫廣規矽歸龜閨軌詭櫃貴劊輥滾鍋國過駭韓漢號閡鶴賀橫轟鴻紅後壺護滬戶嘩華畫劃話懷壞歡環還緩換喚瘓煥渙黃謊揮輝毀賄穢會燴彙諱誨繪葷渾夥獲貨禍擊機積饑譏雞績緝極輯級擠跡幾薊劑濟計記際繼紀夾莢頰賈鉀價駕殲監堅箋間艱緘繭檢堿鹼揀撿簡儉減薦檻鑒踐賤見鍵艦劍餞漸濺澗將漿蔣槳獎講醬膠澆驕嬌攪鉸矯僥腳餃繳絞轎較稭階節莖鯨驚經頸靜鏡徑痙競淨糾廄舊駒舉據鋸懼劇鵑絹傑潔結誡屆緊錦僅謹進晉燼盡勁荊㈨覺決訣絕鈞軍駿開凱顆殼課墾懇摳庫褲誇塊儈寬礦曠況虧巋窺饋潰擴闊蠟臘萊來賴藍欄攔籃闌蘭瀾讕攬覽懶纜爛濫撈勞澇樂鐳壘類淚籬離裏鯉禮麗厲勵礫曆瀝隸倆聯蓮連鐮憐漣簾斂臉鏈戀煉練糧涼兩輛諒療遼鐐獵臨鄰鱗凜賃齡鈴淩靈嶺領餾劉龍聾嚨籠壟攏隴樓婁摟簍蘆盧顱廬爐擄鹵虜魯賂祿錄陸驢呂鋁侶屢縷慮濾綠巒攣孿灤亂掄輪倫侖淪綸論蘿祼羅邏鑼籮騾駱絡媽瑪碼螞馬罵嗎買麥賣邁脈瞞饅蠻滿謾貓錨鉚貿麽黴沒鎂門悶們錳夢謎彌覓冪綿緬廟滅憫閩鳴銘謬謀畝鈉納難撓腦惱鬧餒內擬膩攆撚釀鳥聶齧鑷鎳檸獰甯擰濘鈕紐膿濃農瘧諾歐鷗毆嘔漚盤龐賠噴鵬騙飄頻貧蘋憑評潑頗撲鋪樸譜棲淒臍齊騎豈啓氣棄訖牽扡釺鉛遷簽謙錢鉗潛淺譴塹槍嗆牆薔強搶鍬橋喬僑翹竅竊欽親寢輕氫傾頃請慶瓊窮趨區軀驅齲顴權勸卻鵲確讓饒擾繞熱韌認紉榮絨軟銳閏潤灑薩鰓賽叁傘喪騷掃澀殺紗篩曬刪閃陝贍繕傷賞燒紹賒攝懾設紳審嬸腎滲聲繩勝聖師獅濕詩屍時蝕實識駛勢適釋飾視試壽獸樞輸書贖屬術樹豎數帥雙誰稅順說碩爍絲飼聳慫頌訟誦擻蘇訴肅雖隨綏歲孫損筍縮瑣鎖獺撻檯擡態攤貪癱灘壇譚談歎湯燙濤縧討騰謄銻題體屜條貼鐵廳聽烴銅統頭禿圖塗團頹蛻脫鴕馱駝橢窪襪彎灣頑萬網韋違圍爲偽濰維葦偉僞緯謂衛溫聞紋穩問甕撾蝸渦窩臥嗚鎢烏汙誣無蕪吳塢霧務誤錫犧襲習銑戲細蝦轄峽俠狹廈嚇鍁鮮纖鹹賢銜閑顯險現獻縣餡羨憲線廂鑲鄉詳響項蕭囂銷曉嘯蠍協挾攜脅諧寫瀉謝鋅釁興洶鏽繡虛噓須許敘緒續軒懸選癬絢學勳詢尋馴訓訊遜壓鴉鴨啞亞訝閹煙鹽嚴顔閻豔厭硯彥諺驗鴦楊揚瘍陽癢養樣瑤搖堯遙窯謠藥爺頁業葉醫銥頤遺儀彜蟻藝億憶義詣議誼譯異繹蔭陰銀飲隱櫻嬰鷹應纓瑩螢營熒蠅贏穎喲擁傭癰踴詠湧優憂郵鈾猶遊誘輿魚漁娛與嶼語籲慾禦獄譽預馭鴛淵轅園員圓緣遠願約躍鑰嶽粵悅閱雲鄖勻隕運蘊醞暈韻雜災載攢暫贊贓髒鑿棗竈責擇則澤賊贈紮劄軋鍘閘柵詐齋債氈盞斬輾嶄棧戰綻張漲帳賬脹趙蟄轍鍺這貞針偵診鎮陣掙睜猙爭幀鄭證織職執紙摯製擲幟質滯塚鍾終種腫衆眾謅軸皺晝驟豬諸誅燭矚囑貯鑄築駐專磚轉賺樁莊裝妝壯狀錐贅墜綴諄濁茲資漬蹤綜總縱鄒詛組鑽姦０１２３４５６７８９ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ",
        MapTable: []
    };
    for (var i=0; i<window.HxTest.Chars.FT.length; i++) {
        window.HxTest.Chars.MapTable[
            window.HxTest.Chars.FT.charAt(i)
         ] = window.HxTest.Chars.JT.charAt(i);
    }
    // 高效的字符串拼接类
    window.HxTest.StringBuffer = function() {
        var buf = new Array();
        var count = 0;
        var _self = this;
        
        this.dispose = function() {
            for (var s in _self) {
                delete _self[s];
            }
        };
        
        this.toString = function() {
            return buf.join("");
        };
        
        this.append = function(c) {
            buf[count++] = c;
        };
    };
    // 将繁体中文转化为简体中文
    window.HxTest.Simplify = function(cc) {
        var builder=new window.HxTest.StringBuffer();
        cc = cc || "";
        for (var i=0; i<cc.length; i++) {
            var c = cc.charAt(i);
            builder.append(
                window.HxTest.Chars.MapTable[c] || c
            );
        }
        return builder.toString();
    };
    
    window.HxTest.Keywords = function(dictArray) {
        var keyHash = {};
        var count = 0;
        var _self = this;
        var isReady = false;
        var nextId = 0; // 分配一个关键字唯一的 ID
		var nextDictId = 0;
        
        this.Keywords = keyHash;
        this.MaxSearchLength = 1000;
        
        // 载入关键词列表的某些回调
        this.OnReady = function(sender) {
            
        };
		this.OnDownloaded = function(sender, dictId) {
			
		};
        this.OnError = function(sender) {
            
        };
        this.IsReady = function() {
            return isReady;
        };
        
        // 属性
        this.Count = function() {
            return count;
        };
        
        // 关键词文件列表
        // 这里提供了载入多个词库并合并的解决方案
        var dictionaries = dictArray || [];
        
        if (!(dictionaries instanceof Array))
            dictionaries = new Array(dictionaries);
        
        this.load = function() {
            var req = null;
            var hacked = false;
            
            
            // load next dictionary
            var loadNext = function () {
                if (nextDictId < dictionaries.length) {
                    var nextDictObj = dictionaries[nextDictId];
                    nextDictId++;
                    
                    // create web request
                    if (nextDictObj.url)
                        req = new AjaxRequest(nextDictObj.url, true, nextDictObj.charset);
                    else
                        req = new AjaxRequest(nextDictObj);
                    req.onComplete = onComplete;
                    
                    // load
                    try {
						req.get("hash=" + Math.random());
					} catch (e) {
						_self.OnError(this);
					}
                } else {
                    isReady = true;
                    _self.OnReady(_self);
                }
            };
            
            // when a dictionary loaded
            var onComplete = function (xmlhttp) {
                // process this one
                var _isReady = false;
                if (xmlhttp.responseText) {
                    try {
						var val = eval("(" + xmlhttp.responseText + ")");
						if (val) {
							if (!hacked) {
								// we'd like to add these words in when we load the first dictionary
								_self.hackWelkingObject(val);
								hacked = true;
							}
	
							_self.loadObject(val);
							_self.OnDownloaded(this, nextDictId);
							isReady = _isReady = true;
						}
					} catch(e) {
						_isReady = false;
					}
                }
                
                // verify & load next
                if (!_isReady)
                    _self.OnError(_self);

                loadNext();
            };
            
            loadNext();
        };
        
        
        // 注册关键字类型及处理函数
        this.Types = {
            keywordSets: 1,
            keyCombo1: 2,
            keyCombo2: 3,
            keywords: 4,
            vKeywords: 5,
            pKeywords: 6,
            rKeywords: 7,
            iKeywords: 8,
			socialKeywords: 9,
			myKeywords: 10
        };
        
        var defaultMatcher = function(c, st) {
            var cc = c.toLowerCase();
            var ccd = cc.charCodeAt(0);
            if (st.kwm[this[3]].ki >= this[0].length)
                return 1; //return 2; // Very special at this, persuade caller to move the pointer back one char
            if (this[0].charAt(st.kwm[this[3]].ki) == cc) {
                // 完全匹配
                st.kwm[this[3]].ki++; st.kwm[this[3]].jumped=0;
                if (st.kwm[this[3]].ki == this[0].length)
                    return 1;
                else
                    return 0;
            } else {
                st.kwm[this[3]].jumped++;
                if (st.kwm[this[3]].jumped >= _self.MaxSearchLength) {
                    // 如果超过了最大搜索长度，跳出
                    return -1;
                }
                if ((ccd >= 0x4e00
                    && ccd <= 0x9fa5)) {
                    // 如果遇到中文，不可能继续匹配
                    return -1;
                } else if (/*(ccd >= 65 && ccd <= 90)
                        || Already toLowerCase */ (ccd >= 97 && ccd <= 122)
                        || (ccd >= 48 && ccd <= 57)) {
                    // 如果是字母，分情况考虑
                    if (this[2] == 0)
                        return 0;
                    else
                        return -1; // 字母对字母，不能再匹配。
                } else {
                    return 0;
                }
            }
        };
        
        this.MatchHandler = [
            /*null */ null,
            /*KeywordSets: */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*keyCombo1:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*keyCombo2:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*keywords:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*vKeywords:  */
            function(c, st) {
                var cc = c.toLowerCase();
                var ccd = cc.charCodeAt(0);
                if (st.kwm[this[3]].ki >= this[0].length)
                    return 1;
                if (this[0].charAt(st.kwm[this[3]].ki) == cc) {
                    // 完全匹配
                    st.kwm[this[3]].ki++; st.kwm[this[3]].jumped=0;
                    if (st.kwm[this[3]].ki == this[0].length)
                        return 1;
                    else
                        return 0;
                } else {
                    return -1;
                }
            },
            /*pKeywords:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*rKeywords:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*iKeywords:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*socialKeywords:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            },
            /*myKeywords:  */
            function() {
                return defaultMatcher.apply(this, arguments);
            }
        ];
        
        var englishLevel = function(w) {
            var hasEng = 0, hasChs = 0;
            for (var i=0; i<w.length; i++) {
                var c = w.charCodeAt(i);
                if (c >= 0x4e00 && c <= 0x95af) {
                    // 中文
                    hasChs = 1;
                } else {
                    // 英文
                    hasEng = 1;
                }
            }
            // 空字符串返回-1，纯中文返回 0，纯英文返回 1，中英混合返回 2。
            if (hasEng == 0) {
                return hasChs-1;
            } else {
                return hasEng+hasChs;
            }
        };
        
        /* // 读取 keywords.js
        this.loadURL = function(url) {
            var req = new AjaxRequest(url);
            req.onComplete = function (xmlhttp) {
                isReady = false;
                if (xmlhttp.responseText) {
                    var val = eval("(" + xmlhttp.responseText + ")");
                    if (val) {
                        _self.hackWelkingObject(val);
                        _self.loadObject(val);
                        isReady = true;
                        _self.OnReady(_self);
                    }
                }
                if (!isReady)
                    _self.OnError(_self);
                req.dispose();
            };
            req.get("hash=" + Math.random());
        }; */
        
        this.hackWelkingObject = function(obj) {
            // 为词库添加 Hacking 词组
            // Bug Reported By 崔庚大队长 on May 16, 2009
            with (obj) {
                // hackish. going from '收购' to the end in keyCombo1 for gambling and other illegal stuff
                for(var k = 3;k < keyCombo1.length;k++) {
                    keywordSets.push('下注,' + keyCombo1[k]);
                    keywordSets.push('假币,' + keyCombo1[k]);
                    keywordSets.push('老虎机,' + keyCombo1[k]);
                    keywordSets.push('赌球,' + keyCombo1[k]);
                    keywordSets.push('特码,' + keyCombo1[k]);
                    keywordSets.push('一肖,' + keyCombo1[k]);
                    keywordSets.push('博彩,' + keyCombo1[k]);
                    keywordSets.push('3d轮盘,' + keyCombo1[k]);
                    keywordSets.push('天下彩,' + keyCombo1[k]);
                    keywordSets.push('香港赛马会,' + keyCombo1[k]);
                    keywordSets.push('一码,' + keyCombo1[k]);
                    keywordSets.push('身份证,' + keyCombo1[k]);
                    keywordSets.push('窃听器,' + keyCombo1[k]);
                    keywordSets.push('无线耳机,' + keyCombo1[k]);
                }

                // hackish. going from '电话' to the end in keyCombo1 for '枪手'
                    for(var k = 5;k < keyCombo1.length;k++) {
                    keywordSets.push('枪手,' + keyCombo1[k]);
                }

                // hackish. going from '买' to the end in keyCombo1 for '猎枪'
                for(var k = 10;k < keyCombo1.length;k++) {
                    keywordSets.push('猎枪,' + keyCombo1[k]);
                    keywordSets.push('弓弩,' + keyCombo1[k]);
                }
            }
        };
        
        // 读取合并后的关键字对象
        this.loadObject = function(obj) {
            if (obj.keywordSets) {
                // 载入 keywordSets
                for (var i=0; i<obj.keywordSets.length; i++) {
                    if (!obj.keywordSets[i]) continue; // Detect for May 6, 2009
                    obj.keywordSets[i] = obj.keywordSets[i].toLowerCase();
                    var arr = obj.keywordSets[i].split(",");
                    
                    var c = arr[0].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [arr[0], this.Types.keywordSets, englishLevel(arr[0]), nextId++, arr[1]] // 按照 原词、类型、包含英文的状态、关键字 ID、关联对象 的顺序排列
                    );
                    
                    var c = arr[1].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [arr[1], this.Types.keywordSets, englishLevel(arr[0]), nextId++, arr[0]]
                    );
                }
            }
            
            if (obj.keyCombo1) {
                // 载入 keyCombo1
                for (var i=0; i<obj.keyCombo1.length; i++) {
                    if (!obj.keyCombo1[i]) continue; // Detect for May 6, 2009
                    obj.keyCombo1[i] = obj.keyCombo1[i].toLowerCase();
                    var c = obj.keyCombo1[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.keyCombo1[i], this.Types.keyCombo1, englishLevel(obj.keyCombo1[i]), nextId++]
                    );
                }
            }
            
            if (obj.keyCombo2) {
                // 载入 keyCombo2
                for (var i=0; i<obj.keyCombo2.length; i++) {
                    if (!obj.keyCombo2[i]) continue; // Detect for May 6, 2009
                    obj.keyCombo2[i] = obj.keyCombo2[i].toLowerCase();
                    var c = obj.keyCombo2[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.keyCombo2[i], this.Types.keyCombo2, englishLevel(obj.keyCombo2[i]), nextId++]
                    );
                }
            }
                
            if (obj.keywords) {
                // 载入 keywords
                for (var i=0; i<obj.keywords.length; i++) {
                    if (!obj.keywords[i]) continue; // Detect for May 6, 2009
                    obj.keywords[i] = obj.keywords[i].toLowerCase();
                    var c = obj.keywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.keywords[i], this.Types.keywords, englishLevel(obj.keywords[i]), nextId++]
                    );
                }
            }
                
            if (obj.vKeywords) {
                // 载入 vKeywords
                for (var i=0; i<obj.vKeywords.length; i++) {
                    if (!obj.vKeywords[i]) continue; // Detect for May 6, 2009
                    obj.vKeywords[i] = obj.vKeywords[i].toLowerCase();
                    var c = obj.vKeywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.vKeywords[i], this.Types.vKeywords, englishLevel(obj.vKeywords[i]), nextId++]
                    );
                }
            }

            if (obj.pKeywords) {
                // 载入 pKeywords
                for (var i=0; i<obj.pKeywords.length; i++) {
                    if (!obj.pKeywords[i]) continue; // Detect for May 6, 2009
                    obj.pKeywords[i] = obj.pKeywords[i].toLowerCase();
                    var c = obj.pKeywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.pKeywords[i], this.Types.pKeywords, englishLevel(obj.pKeywords[i]), nextId++]
                    );
                }
            }

            if (obj.rKeywords) {
                // 载入 rKeywords
                for (var i=0; i<obj.rKeywords.length; i++) {
                    if (!obj.rKeywords[i]) continue; // Detect for May 6, 2009
                    obj.rKeywords[i] = obj.rKeywords[i].toLowerCase();
                    var c = obj.rKeywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.rKeywords[i], this.Types.rKeywords, englishLevel(obj.rKeywords[i]), nextId++]
                    );
                }
            }

            if (obj.iKeywords) {
                // 载入 iKeywords
                for (var i=0; i<obj.iKeywords.length; i++) {
                    if (!obj.iKeywords[i]) continue;
                    obj.iKeywords[i] = obj.iKeywords[i].toLowerCase();
                    var c = obj.iKeywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.iKeywords[i], this.Types.iKeywords, englishLevel(obj.iKeywords[i]), nextId++]
                    );
                }
            }
			
			if (obj.socialKeywords) {
                // 载入 socialKeywords
                for (var i=0; i<obj.socialKeywords.length; i++) {
                    if (!obj.socialKeywords[i]) continue;
                    obj.socialKeywords[i] = obj.socialKeywords[i].toLowerCase();
                    var c = obj.socialKeywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.socialKeywords[i], this.Types.socialKeywords, englishLevel(obj.socialKeywords[i]), nextId++]
                    );
                }
            }

            if (obj.myKeywords) {
                // 载入 myKeywords
                for (var i=0; i<obj.myKeywords.length; i++) {
                    if (!obj.myKeywords[i]) continue;
                    obj.myKeywords[i] = obj.myKeywords[i].toLowerCase();
                    var c = obj.myKeywords[i].charAt(0);
                    if (!keyHash[c])
                        keyHash[c] = [];
                    keyHash[c].push(
                        [obj.myKeywords[i], this.Types.myKeywords, englishLevel(obj.myKeywords[i]), nextId++]
                    );
                }
            }

            count = nextId;
        };
    };
    
    // 辅助进行和谐测试的对象
    window.HxTest.TestHelper = {
        copyArray: function(arr, st) {
            /* var arr1 = arr && arr.concat([]);
            if (arr1) {
                for (var i=0; i<arr1.length; i++) {
                    arr1[i].jumped = 0;
                    arr1[i].ki = 1;
                }
            }
            return arr1; */
            if (arr) {
                var arr1 = new Array();
                for (var i=0; i<arr.length; i++) {
                    if (arr[i][0].length > 1) {
                        st.kwm[arr[i][3]] = {};
                        st.kwm[arr[i][3]].jumped = 0;
                        st.kwm[arr[i][3]].ki = 1;
                        arr1.push(arr[i]);
                    } else {
                        // 已经检测到关键字
                        //debugger;
                        st.onHitKeyword(st.innerId, st.innerId, arr[i], st);
                    }
                }
                return arr1;
            } else {
                return null;
            }
        },
        
        testChar: function(st) {
            var ki=0;
            var i=st.innerId;
            var basicSet = window.HxTest.TestHelper.copyArray(
                st.k.Keywords[st.src.charAt(i).toLowerCase()], st
            );
            // 如果这个字符并不出现在关键字列表中，返回
            if (!basicSet || basicSet.length == 0) {
                st.innerId++;
                return;
            }
            var blength = basicSet.length;
            
            // 预先处理单字情况
            /* 已经转移到 copyArray 函数中 */
            
            // 循环测试
            i++; ki++;
            var retInd = -1;
            while (i<=st.endId) {
                // 开始匹配集合
                for (var j=0; j<basicSet.length; j++) {
                    var func = basicSet[j] && st.k.MatchHandler[basicSet[j][1]];
                    if (func) {
                        var ret = func.call(basicSet[j], st.src.charAt(i), st);
                        if (ret == -1) {
                            delete basicSet[j];
                            blength--;
                        }
                        else if (ret >= 1) {
                            st.onHitKeyword(st.innerId, i, basicSet[j], st);
                            delete basicSet[j];
                            blength--;
                        }
                        if (blength <= 0) {
                            st.innerId++;
                            return;
                        }
                    }
                }
                i++;
            }

            st.innerId++;
            return;
        }
    };

    // 和谐测试器主类
    window.HxTest.Tester = function (title, content, keywords, statusObj) {
        // 一些基本的字符串操作函数
        content = content || "";
        var ti = window.HxTest.Simplify(title);
        var cnt = window.HxTest.Simplify(content);
        // var curStr = ""; var innerId = -1; var status = -1;
        var st = statusObj || {};
        var rUpperLimit = 0;

        this.Result = {};
        this.Result.Content = cnt;
        this.Result.Title = ti;
        this.Result.ContentItems = [];
        this.Result.TitleItems = [];
        
        // 初始化一个测试器状态对象
        var newStatus = function() {
            st.tiRUpper = st.tiRUpper || 2;
            st.cntRUpper = st.cntRUpper || 10;
            st.onHitKeyword = onHitKeyword;
            st.k = keywords;
            st.keywords = keywords;
            st.tiRes = [];
            st.cntRes = [];
            st.tiR = {};
            st.cntR = {};
            st.cmb1 = 0;
            st.cmb2 = 0;
            st.sets = [];
            st.kwm = []; // 线程安全的 keywords 匹配状态池
        };
        var assignStatus = function(srcStr, startId, endId, jobId) {
            st.r = null;
            st.res = null;
            st.src = srcStr;
            st.startId = startId || 0;
            st.endId = endId || srcStr.length-1;
            // For getting progress outside
            st.jobId = jobId || 0;
            st.jobCount = 2;
            st.innerId = 0;
            st.strlen = st.endId-st.startId+1;
        };
        
        // 处理匹配成功的函数
        var Handler = [
            /*null */ null,
            /*KeywordSets: */
            function(startId, endId, kword, st) {
                // 加入列表
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 0
                    }
                );
                // 加入待匹配列表
                st.sets[kword[0].toLowerCase()]=1;
            },
            /*keyCombo1:  */
            function(startId, endId, kword, st) {
                // 加入列表
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 0
                    }
                );
                // 加入待匹配列表
                st.cmb1=1;
            },
            /*keyCombo2:  */
            function(startId, endId, kword, st) {
                // 加入列表
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 0
                    }
                );
                // 加入待匹配列表
                st.cmb2=1;
            },
            /*keywords:  */
            function(startId, endId, kword, st) {
                // 加入结果
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 1
                    }
                );
            },
            /*vKeywords:  */
            function(startId, endId, kword, st) {
                // 加入结果
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 1
                    }
                );
            },
            /*pKeywords:  */
            function(startId, endId, kword, st) {
                // 判断&加入结果
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 1
                    }
                );
            },
            /*rKeywords:  */
            function(startId, endId, kword, st) {
                // 加入列表
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 0
                    }
                );
                // 统计数目
                var key = kword[0].toLowerCase()
                if (!st.r[key])
                    st.r[key]=0;
                st.r[key]++;
            },
            /* iKeywords:  */
            function(startId, endId, kword, st) {
                // 判断&加入结果
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 1
                    }
                );
            }
			,
            /* socialKeywords:  */
            function(startId, endId, kword, st) {
                // 判断&加入结果
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 1
                    }
                );
            }
			,
            /* myKeywords:  */
            function(startId, endId, kword, st) {
                // 判断&加入结果
                st.res.push(
                    {
                        startId: startId,
                        endId: endId,
                        phrase: st.src.substr(startId, endId-startId+1),
                        kword: kword,
                        actived: 1
                    }
                );
            }
        ];
        
        var AfterHandler = function(st) {
            // 更新标题结果
            for (var i=0; i<st.tiRes.length; i++) {
                // 更新 keyCombo1
                if (st.tiRes[i].kword[1] == st.k.Types.keyCombo1
                        && st.cmb2 == 1) {
                    st.tiRes[i].actived = 1;
                }
                // 更新 keyCombo2
                if (st.tiRes[i].kword[1] == st.k.Types.keyCombo2
                        && st.cmb1 == 1) {
                    st.tiRes[i].actived = 1;
                }
                // 更新 KeywordSets 状态
                if (st.tiRes[i].kword[1] == st.k.Types.keywordSets
                        && st.sets[st.tiRes[i].kword[4].toLowerCase()] == 1) {
                    st.tiRes[i].actived = 1;
                }
                // 更新 rKeywords 状态
                if (st.tiRes[i].kword[1] == st.k.Types.rKeywords
                        && st.r[st.tiRes[i].kword[0].toLowerCase()] >= st.tiRUpper) {
                    st.tiRes[i].actived = 1;
                }
            }
            
            // 更新内容结果
            for (var i=0; i<st.cntRes.length; i++) {
                // 更新 keyCombo1
                if (st.cntRes[i].kword[1] == st.k.Types.keyCombo1
                        && st.cmb2 == 1) {
                    st.cntRes[i].actived = 1;
                }
                // 更新 keyCombo2
                if (st.cntRes[i].kword[1] == st.k.Types.keyCombo2
                        && st.cmb1 == 1) {
                    st.cntRes[i].actived = 1;
                }
                // 更新 KeywordSets 状态
                if (st.cntRes[i].kword[1] == st.k.Types.keywordSets
                        && st.sets[st.cntRes[i].kword[4].toLowerCase()] == 1) {
                    st.cntRes[i].actived = 1;
                }
                // 更新 rKeywords 状态
                if (st.cntRes[i].kword[1] == st.k.Types.rKeywords
                        && st.r[st.cntRes[i].kword[0].toLowerCase()] >= st.cntRUpper) {
                    st.cntRes[i].actived = 1;
                }
            }
        };
        
        var onHitKeyword = function(startId, endId, kword, st) {
            return Handler[kword[1]] && Handler[kword[1]](startId, endId, kword, st);
        };
        
        // 一些接口
        this.GetStatus = function() {
            return status;
        };
        
        this.GetProgress = function() {
            return innerId;
        };
        this.GetMaxProgress = function() {
            return curStr.length;
        };
        
        /* var genResStr = function(res, curStr) {
            var ss = "";
            for (var i=0; i<res.length; i++) {
                ss += curStr.substr(res[i].startId, res[i].endId - res[i].startId + 1) + "\n";
            }
            return ss;
        }; */
        
        var MergeResults = function(res) {
            var nextInd = 0;
            //debugger;
            // 取得下一个可用项的闭包
            var nextItem = function() {
                while (nextInd < res.length
                        && res[nextInd].actived != 1) {
                    nextInd++;
                };
                return res[nextInd++];
            };
            
            var ret = [];
            var newGroup = null;
            // 添加第一项
            var firstItem = nextItem();
            if (firstItem) {
                newGroup = {
                    startId: firstItem.startId,
                    endId: firstItem.endId,
                    phrases: [ firstItem ],
                    type: firstItem.kword[1]
                };
            }
            
            // 遍历所有后续项
            var nxt = null;
            while (nxt = nextItem()) {
                if (nxt.startId > newGroup.endId) {
                    ret.push(newGroup);
                    newGroup = {
                        startId: nxt.startId,
                        endId: nxt.endId,
                        phrases: [ nxt ],
                        type: nxt.kword[1]
                    };
                } else {
                    newGroup.phrases.push(nxt);
                    if (nxt.endId>newGroup.endId) newGroup.endId = nxt.endId;
                    if (nxt.kword[1] != newGroup.type) newGroup.type = 0;
                }
            }
            
            // 添加最后一项
            if (newGroup)
                ret.push(newGroup);
                
            
            return ret;
        };
        
        // 测试字符串        
        this.Exec = function() {
        
            var incStep = st.strlen / 100;
            if (incStep < 200) incStep = 200;
            newStatus();
        
            // 测试标题
            assignStatus(ti, 0, ti.length-1, 0);
            st.res = st.tiRes; st.r = st.tiR;
            
            while (st.innerId <= st.endId) {
                window.HxTest.TestHelper.testChar(st);
                /*if (st.setProgress && st.innerId % incStep == 0) {
                    st.setProgress(st.innerId / st.strlen);
                }*/
            }
            
            // 测试内容
            assignStatus(cnt, 0, cnt.length-1, 0);
            st.res = st.cntRes; st.r = st.cntR;
            
            while (st.innerId <= st.endId) {
                window.HxTest.TestHelper.testChar(st);
                /*if (st.setProgress && st.innerId % incStep == 0) {
                    st.setProgress(st.innerId / st.strlen);
                }*/
            }
            
            // 合并结果
            AfterHandler(st);
            this.Result.TitleItems = MergeResults(st.tiRes);
            this.Result.ContentItems = MergeResults(st.cntRes);
        };
    };
    
    // 和谐测试器智能替换器
    window.HxTest.Replacer = function(cnt, res, k, spChar) {
        var _self = this;
        spChar = spChar || String.fromCharCode(0x4E28);
        
        this.setSpChar = function (ch) {
            spChar = ch || spChar;
        }
    
        this.UndealtItems = [];
        this.outputPart = [];
		this.ItemTypes = [];
    
        var strRev = function(str) {
            return str.split("").reverse().join("");
        };
        
        
        var htmlEncode = function(str) {
            return str.replace(/\&/g, "&amp;").replace(/\</g, "&lt;").replace(/\ /g, "&nbsp;").replace(/\n/g, "<br>");
        };
        
        var insertUnicodeSplit = function(str) {
            if (str.length == 0) return "";
            
            var ret = str.charAt(0) + spChar + str.substr(1);
            return ret;
            /*
            for (var i=1; i<str.length; i++) {
                ret = ret + String.fromCharCode(0x4E28) + str.charAt(i);
            }
            return ret;
            */
        };
        
        var appendPart = function(cnt, item, ok) {
			if (ok) {
                //_self.outputPart.push(String.fromCharCode(8238) + strRev(cnt.substr(item.startId, item.endId-item.startId+1)) + String.fromCharCode(8236));
                _self.outputPart.push(insertUnicodeSplit(cnt.substr(item.startId, item.endId-item.startId+1)));
            } else {
                _self.outputPart.push(cnt.substr(item.startId, item.endId-item.startId+1)); // _self.outputPart.push(item);
                _self.UndealtItems.push(_self.outputPart.length-1);
            }
        };
        
        var appendPartAd = function(cnt, item, ok) {
			if (ok) {
                _self.outputPart.push(String.fromCharCode(8238) + strRev(cnt.substr(item.startId, item.endId-item.startId+1)) + String.fromCharCode(8236));
            } else {
                _self.outputPart.push(cnt.substr(item.startId, item.endId-item.startId+1)); // _self.outputPart.push(item);
                _self.UndealtItems.push(_self.outputPart.length-1);
            }
        };
        
        var dealWith = function(cnt, item, advanced) {
            if (advanced) {
                var needManual;
                
                if (item.endId == item.startId) {
                    // 单字短语需要手工替换
                    needManual = true;
                } else {
                    // 检查是否有 PKeywords
                    for (var i=0; i<item.phrases.length; i++) {
                        if (item.phrases[i].kword[1] == k.Types.pKeywords
                            || item.phrases[i].kword[1] == k.Types.iKeywords) {
                            needManual = true;
                            break;
                        }
                    }
                }
                    
                if (needManual) {
                    appendPartAd(cnt, item, false);
                } else {
                    appendPartAd(cnt, item, true);
                }
            } else {
        
                var needManual = item.endId != item.startId && item.type < 9;
    			
                appendPart(cnt, item, needManual);
			    _self.ItemTypes.push(item.type);
			
			}

        };
        
        cnt = cnt || "";
        res = res || [];
        
        // 智能替换
        this.AutoReplace = function() {
            _self.UndealtItems = [];
			_self.ItemTypes = [0];
            var nextCh = 0;
            for (var i=0; i<res.length ; i++)
            {
                _self.outputPart.push(cnt.substr(nextCh, res[i].startId-nextCh));
                //sb.append("<span class='wordGroup'>" + htmlEncode(str.substr(res[i].startId, res[i].endId-res[i].startId+1)) + "</span>");
                dealWith(cnt, res[i]);
                nextCh = res[i].endId+1;
            }
            if (nextCh < cnt.length) {
                _self.outputPart.push(cnt.substr(nextCh));
            }
        };
        
        // 智能替换
        this.SmartReplace = function() {
            _self.UndealtItems = [];
			_self.ItemTypes = [0];
            var nextCh = 0;
            for (var i=0; i<res.length ; i++)
            {
                _self.outputPart.push(cnt.substr(nextCh, res[i].startId-nextCh));
                //sb.append("<span class='wordGroup'>" + htmlEncode(str.substr(res[i].startId, res[i].endId-res[i].startId+1)) + "</span>");
                dealWith(cnt, res[i], true);
                nextCh = res[i].endId+1;
            }
            if (nextCh < cnt.length) {
                _self.outputPart.push(cnt.substr(nextCh));
            }
        };
        
        // 普通替换
        this.ManualReplace = function(rep) {
            _self.UndealtItems = [];
            var nextCh = 0;
            for (var i=0; i<res.length ; i++)
            {
                _self.outputPart.push(cnt.substr(nextCh, res[i].startId-nextCh));
                //sb.append("<span class='wordGroup'>" + htmlEncode(str.substr(res[i].sta rtId, res[i].endId-res[i].startId+1)) + "</span>");
                _self.outputPart.push(rep[i]);
                nextCh = res[i].endId+1;
            }
            if (nextCh < cnt.length) {
                _self.outputPart.push(cnt.substr(nextCh));
            }
        };
        
        this.toString = function() {
            return _self.outputPart.join("");
        };
        
        this.toHTML = function() {
            return htmlEncode(_self.toString());
        };
    };
})();
