var $doc = $(document); var sysConfig, currentProfile; var seatOrder = ["", "", "", "", "9", "P", "M", "O", "6", "4", "3", "2", "1", "0", "*"]; var isDebug = (compressFunc + '').indexOf("false") != -1; var tagMap = { "动车或高铁或城铁": "[DGC].*", "特快或直达": "[TZ].*", "快车": "K.*", "高铁": "G.*", "动车": "D.*", "城铁": "C.*", "直达": "Z.*", "特快": "T.*", "任意车次": ".*" }; var passengerTypeCode = { "1": "成人", "2": "儿童", "3": "学生", "4": "残疾军人、伤残人民警察" }; var EVT_ONQUERYPAGE = "onQueryPage"; var EVT_ONLOGINPAGE = "onLoginPage"; var EVT_ONSUBMITPAGE = "onSubmitPage"; var EVT_ONMAINPAGE = "onMainPage"; var EVT_ONPASSENGERPAGE = "onPassengerPage"; var evtTag = new Date().getTime(); Date.prototype.format = function (format) { /// 格式化指定日期 /// 格式化字符串 format = format || "yyyy-MM-dd"; var o = { "M+": this.getMonth() + 1, //month "d+": this.getDate(), //day "h+": this.getHours(), //hour "m+": this.getMinutes(), //minute "s+": this.getSeconds(), //second "q+": Math.floor((this.getMonth() + 3) / 3), //quarter "S": this.getMilliseconds() //millisecond }; if (/(y+)/i.test(format)) { format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); } for (var k in o) { if (new RegExp("(" + k + ")").test(format)) { format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); } } return format; }; Date.prototype.trimToDay = function () { /// 返回当前的日期和时间中的日期部分 return new Date(this.getFullYear(), this.getMonth(), this.getDate()); }; $.fn.extend({ nativeClick: function (timeout) { /// 触发原生的点击 var __ = this; setTimeout(function () { __.each(function () { this.click && this.click(); }); }, timeout || 10); return __; }, nativeFocus: function () { /// 触发原生的获得焦点 this.each(function () { this.focus && this.focus(); }); return this; }, toBase64Data: function (callback) { var internalcallback = function () { $(this).unbind("load", internalcallback); var canvas = document.createElement("canvas"); var height = this.naturalHeight; var width = this.naturalWidth; canvas.width = width; canvas.height = height; var ctx = canvas.getContext("2d"); ctx.drawImage(this, 0, 0, width, height); var url = canvas.toDataURL(); delete canvas; url = url.substr(url.indexOf(",") + 1); callback && callback.call(this, url); }; this.each(function () { if (this.naturalHeight === 0) { $(this).load(internalcallback); } else { internalcallback.call(this); } }); return this; } }); //#region 消息处理 var msg = (function () { var control = function () { var handlermap = {}; var __ = this; this.on = function (name, callback) { var queue = handlermap[name] = handlermap[name] || []; queue.push(callback); }; this.off = function (name, callback) { var queue = handlermap[name] = handlermap[name] || []; for (var i = queue.length - 1; i >= 0; i++) { if (queue[i] === callback) { queue.splice(i, 1); } } }; chrome.runtime.onMessage.addListener(function (message, sender, response) { if (message.tag && message.tag === evtTag) return; var action = message.action || ""; var queue = handlermap[action]; if (!queue) return; $.each(queue, function () { return this.call(message.detail, response, message, sender); }); }); this.send = function (message, callback) { message = $.extend(message, { tag: evtTag }); chrome.runtime.sendMessage(null, message, callback || function () { }); }; this.sendAction = function (action, detail, callback) { __.send({ action: action, detail: detail }, callback); }; }; return new control(); })(); //#endregion //#region 辅助函数 var getTicketInfo = function (v) { var data = {}, info = v.indexOf("#") === -1 ? v : /getSelected\(['"](.*?)['"]\)/i.exec(v)[1].split('#')[11], match = info.match(/([A-Z\d])0*?([\*\d]{5})0*?(\d{4})/gi); for (var j in match) { var m = /([A-Z\d])0*?([\*\d]{5})0*?(\d{4})/i.exec(match[j]); var sc = m[1]; var sp = m[2][0] == '*' ? null : parseInt(m[2], 10); var st = parseInt(m[3], 10); if (st < 3000) { data[sc] = st; data["_" + sc] = sp; //一等软座 7, 二等软座 8 if (sc == "7") { data['M'] = st; data["_M"] = sp; } else if (sc == "8") { data['O'] = st; data['_O'] = sp; } } else { data['0'] = st - 3000; data['_0'] = sp; } }; return data; }; var log = (function () { var fun = function () { this.print = function () { /// 向控制台打印消息 if (!isDebug) return; console.log.apply(console, $.makeArray(arguments)); }; return this; }; return new fun(); })(); function compressFunc() { return false; } (function injectCode() { var s = document.createElement("script"); s.id = "s_" + evtTag; s.textContent = "(" + ((function () { document.addEventListener('$', function (e) { eval(e.detail); }); var x = document.getElementById("s_$"); document.head.removeChild(x); }) + '').replace(/\$/g, evtTag) + ')();'; document.head.appendChild(s); })(); function unsafeInvoke(callback) { /// 非沙箱模式下的回调 if (isDebug) { var cb = document.createElement("script"); cb.type = "text/javascript"; cb.textContent = buildCallback(callback); document.head.appendChild(cb); } else { document.dispatchEvent(new CustomEvent(evtTag, { detail: buildCallback(callback) })); } } function buildCallback(callback) { var content = ""; var name = ""; if (typeof (callback) == "function") name = callback.name; if (!name) name = Math.random() + ""; content += "(" + buildObjectJavascriptCode(callback) + ")();" + (isDebug ? "\r\n//@ sourceURL=http://www.fishlee.net/soft/44/scripts/" + name + ".js" : ""); return content.replace(/\$_\$/g, evtTag); } function buildObjectJavascriptCode(obj) { /// 将指定的Javascript对象编译为脚本 if (obj === null) return "null"; if (obj === undefined) return 'undefined'; var t = typeof (obj); if (t === "string") { return "\"" + obj.replace(/(\r|\n|\\)/gi, function (a, b) { switch (b) { case "\r": return "\\r"; case "\n": return "\\n"; case "\\": return "\\\\"; } return b; }) + "\""; } if (t !== "object") return obj + ""; var code = []; for (var i in obj) { var o = obj[i]; var objType = typeof (o); if (objType === "object" || objType === "string") { code.push(i + ":" + buildObjectJavascriptCode(o)); } else { code.push(i + ":" + o); } } return "{" + code.join(",") + "}"; } //#endregion //数据交换 var toRawPassenger = function (p) { return { passenger_name: p.name, passenger_type: p.type, passenger_id_type_code: p.idtype, passenger_id_no: p.id, passenger_first_letter: p.firstLetter, mobile_no: "", passenger_id_type_name: p.idtypeName, passenger_type_name: p.typename }; }; var evalCode = (function () { var dataExchange; document.body.addEventListener(evtTag, function (e) { dataExchange = e && e.detail && e.detail.action == "dr" && e.detail.detail; }); var fun = function () { this.eval = function (code) { if (jQuery.isFunction(code)) code = "(" + code + ")()"; document.dispatchEvent(new CustomEvent(evtTag, { detail: { action: "eval", detail: { code: code } } })); return dataExchange; }; }; return new fun(); })(); (function () { $.fn.extend({ getTrimedText: function () { return $.trim(this.text()); } }); })(); //#region 系统配置更新函数和引导函数 function refreshSysConfig(callback) { chrome.runtime.sendMessage({ action: "getSysConfig" }, function (message) { sysConfig = message.detail; $doc.trigger("sysConfigUpdated"); callback(); }); msg.on("sysConfigUpdate", function () { sysConfig = this; $doc.trigger("sysConfigUpdated"); }); } $(function () { var timer = null, timercheck = null, routed = false; var ensureReady = function () { chrome.runtime.sendMessage({ action: "isReady" }, function () { if (timer) clearInterval(timer); if (timercheck) clearInterval(timercheck); if (!routed) { routed = true; $(document).trigger("routePage"); } }); }; var notloadNotify = function () { if (timer) clearInterval(timer); if (timercheck) clearInterval(timercheck); chrome.runtime.sendMessage({ action: "notLoadTip", detail: { url: self.location + '', host: self.location.host } }); }; log.print(evtTag); refreshSysConfig(function () { timer = setInterval(ensureReady, 100); timercheck = setTimeout(notloadNotify, 15000); ensureReady(); }); msg.send({ action: "enter12306" }); }); //#endregion //#region 查询页公共函数 $(document).on("filterTrains", function (evt, data) { if (!currentProfile) return; //过滤车次列表 log.print("开始过滤车次"); if (currentProfile.hideNoTicket) { //隐藏无票车次 var filtered = _.where(data.available, { available: 0 }); $.each(filtered, function () { this.reason = 1; data.filtered.push(this); if (data.rowInfo) data.rowInfo[this.id].$row.remove(); log.print("车次【" + this.code + "】因为无票被过滤。"); }); data.available = _.difference(data.available, filtered); } if (currentProfile.hideNotSameFrom) { //隐藏发站不同车次 var filtered1 = _.filter(data.available, function (e) { return e.from.code != currentProfile.fromCode; }); $.each(filtered1, function () { this.reason = 2; data.filtered.push(this); if (data.rowInfo) data.rowInfo[this.id].$row.remove(); log.print("车次【" + this.code + "】因为发站不一致被过滤。"); }); data.available = _.difference(data.available, filtered1); } if (currentProfile.hideNotSameTo) { //隐藏到站不同车次 var filtered2 = _.filter(data.available, function (e) { return e.to.code != currentProfile.toCode; }); $.each(filtered2, function () { this.reason = 3; data.filtered.push(this); if (data.rowInfo) data.rowInfo[this.id].$row.remove(); log.print("车次【" + this.code + "】因为发站不一致被过滤。"); }); data.available = _.difference(data.available, filtered2); } //按照时间过滤 (function () { var r1 = _.filter(data.available, function (e) { var t1 = parseInt(/^0*(\d+)\:/i.exec(e.from.time)[1], 10); var t2 = parseInt(/^0*(\d+)\:/i.exec(e.to.time)[1], 10); return t1 < currentProfile.timeRangeDepFrom || t1 > currentProfile.timeRangeDepTo || t2 < currentProfile.timeRangeArrFrom || t2 > currentProfile.timeRangeArrTo; }); $.each(r1, function () { this.reason = 4; data.filtered.push(this); if (data.rowInfo) data.rowInfo[this.id].$row.remove(); log.print("车次【" + this.code + "】因为时间不在指定的区域中而被过滤。"); }); data.available = _.difference(data.available, r1); })(); //如果开启了自动预定,那么不显示预定结果以外的车次 if (currentProfile.selectedTrain && currentProfile.selectedTrain.length) { var rule = new RegExp("^(" + currentProfile.selectedTrain.join("|") + ")$", "i"); var filtered3 = _.filter(data.available, function (e) { return !rule.test(e.code); }); data.include = _.difference(data.available, filtered3); if (currentProfile.hideNotInListTrain) { $.each(filtered3, function () { this.reason = 5; data.filtered.push(this); if (data.rowInfo) data.rowInfo[this.id].$row.remove(); log.print("车次【" + this.code + "】不在自动预定列表中而被过滤。"); }); data.available = data.include; } } log.print("车次过滤结束。"); }); $(document).on("processTrains", function (e, data) { if (!currentProfile) return; var count = Math.max(currentProfile.partialSubmitEnabled ? 0 : currentProfile.passengers.length, 1); var seat = null, train = null, entry = null; if (data.original.length && !data.include.length && currentProfile.selectedTrain && currentProfile.selectedTrain.length && !currentProfile.byAuto) { msg.sendAction("notify", { title: "提示", message: "貌似没有查到您要预定的任何车次,是否设置存在问题?", iconUrl: "/icons/icon_128.png" }); } var trainRegCache = _.map(currentProfile.selectedTrain, function (s) { return new RegExp("^" + s + "$", "i"); }); if (!trainRegCache || !trainRegCache.length) { trainRegCache = [/.*/]; } //搜索车次 if (currentProfile.selectSeatFirst) { //席别优先 seat = _.find(currentProfile.selectedSeatType, function (s) { var _treg = _.find(trainRegCache, function (t) { entry = _.find(data.available, function (item) { return t.test(item.code) && item.tickets[s] >= count; }); return typeof (entry) != 'undefined'; }); return typeof (_treg) != 'undefined'; }); } else { //车次优先 _.find(trainRegCache, function (t) { seat = _.find(currentProfile.selectedSeatType, function (s) { entry = _.find(data.available, function (item) { return t.test(item.code) && item.tickets[s] >= count; }); return typeof (entry) != 'undefined'; }); return typeof (seat) != 'undefined'; }); } //查找结果 if (seat) { train = entry.code; //乘客 var ticketCount = entry.tickets[seat]; var pcount = Math.min(ticketCount, currentProfile.passengers.length); data.auto = { train: train, seat: seat, passengers: _.first(currentProfile.passengers, pcount), data: entry }; } else { data.auto = null; } data.allowAutoSubmit = false; //判断是否需要继续刷新 if (data.auto !== null) { //找到车次了 data.nextTime = null; if (currentProfile.autoSubmitEnabled) { chrome.runtime.sendMessage({ action: "checkAutoRefreshEnabled" }, function (info) { data.allowAutoSubmit = info.enabled; if (info.enabled) $(document).trigger("performAutoSubmit", data.auto); }); } } else { //规则:如果只有待售的,那么等到指定的时间 //如果没有且在整点附近的,那么等到整点 //否则就按常规时间 var now = new Date(); if (data.available.length && sysConfig.autoWaitToSell && currentProfile.autoWaitToSell && data.include.length && _.every(data.include, function (x) { return x.available === -1; })) { var next = _.min(_.map(data.include, function (x) { return x.selltime; })); data.nextTime = now.getDate() === next.getDate() && next > now ? (next - now) / 1000 + 15 : 5; } else if (now.getMinutes() >= 59) { data.nextTime = 61 - now.getSeconds(); } else { data.nextTime = 5; } } log.print("queryReport"); if ($("#autoSubmit:checked").length || (!currentProfile.autoSubmitEnabled && $("#auto_query:checked").length)) { data.auto = null; } chrome.runtime.sendMessage({ action: "queryStatisticsReport", detail: { stat: _.omit(data, "rowInfo"), auto: data.auto, nextTime: data.nextTime } }); }); //查询失败 (function () { $(document).bind("qe", function () { if ($("#auto_query:checked").length) { var a = $("#query_ticket").data("suspend", true)[0]; a && a.click(); } chrome.runtime.sendMessage({ action: "queryStatisticsReport", detail: { failed: true, nextTime: 5 } }); }); })(); //#endregion //#region 验证码识别 $(document).on("routePage", function () { var failedCount = 0; var tipShown = false; var stopped = false; $(document).on("randCodeLoaded", function (e, id) { var nowMinutes = new Date().getMinutes(); if ((nowMinutes >= 59 && nowMinutes <= 5) || (nowMinutes >= 28 && nowMinutes <= 35)) return; if (!sysConfig || !sysConfig.enableAutoCaptcha) return; if (failedCount >= sysConfig.autoCaptchaFailedLimit) { failedCount = 0; tipShown = false; //msg.sendAction("pageNotification", { title: "验证码识别失败", message: "验证码识别失败,请手动填写" }); return; } if (!tipShown) { tipShown = true; //msg.sendAction("pageNotification", { title: "正在自动识别验证码", message: "正在为您识别验证码。验证码识别不一定成功且受限网络速度,可能的话请尽量手动填写。点击验证码输入框取消自动识别。" }); } if (stopped) { stopped = false; return; } var img = $("#" + id); var type = /rand=([a-z]+)/.exec(img.attr("src")) && RegExp.$1; img.toBase64Data(function (url) { chrome.runtime.sendMessage({ action: "captcha", detail: { base64: url } }, function (info) { if (stopped) { stopped = false; failedCount = 0; return; } var code = info.code; if (code && code.length == 4) { img.parent().prev("input").val(code); //msg.sendAction("pageNotification", { title: "验证码识别成功", message: "验证码识别成功,请看看是否正确再提交!" }); } //if (!code || code.length !== 4) { // failedCount++; // img.nativeClick(); //} else { // //判断是否起效 // $.post("/otn/passcodeNew/checkRandCodeAnsyn", { // randCode: code, // rand: type || 'sjrand' // }, function (data) { // if (!data.status || data.data === 'N') { // //失败 // failedCount++; // img.nativeClick(); // } else { // failedCount = 0; // tipShown = false; // msg.sendAction("pageNotification", { title: "验证码识别成功", message: "验证码识别成功,请看看是否正确再提交!" }); // img.parent().prev("input").val(code).change().keyup(); // } // }); //} }); }); }); $(document).on("click", "#randCode, #randCode2", function () { stopped = true; }); $("#img_rand_code:visible, #img_rand_code2:visible").each(function () { if (this.naturalHeight > 0) $(document).trigger("randCodeLoaded", this.getAttribute("id")); else { $(this).load(function () { $(this).unbind("load"); $(document).trigger("randCodeLoaded", this.getAttribute("id")); }); } }); }); //#endregion