Light12306/RwTicketAssistantV2/app/contentscripts/framework.js
iFish addcafcdf8 + 集成扩展的源码
+ 增加相关TypedScript的导入
2014-08-08 14:33:43 +08:00

674 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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) {
/// <summary>格式化指定日期</summary>
/// <param name="format" type="String">格式化字符串</param>
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 () {
/// <summary>返回当前的日期和时间中的日期部分</summary>
return new Date(this.getFullYear(), this.getMonth(), this.getDate());
};
$.fn.extend({
nativeClick: function (timeout) {
/// <summary>触发原生的点击</summary>
var __ = this;
setTimeout(function () {
__.each(function () {
this.click && this.click();
});
}, timeout || 10);
return __;
},
nativeFocus: function () {
/// <summary>触发原生的获得焦点</summary>
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 () {
/// <summary>向控制台打印消息</summary>
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) {
/// <summary>非沙箱模式下的回调</summary>
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) {
/// <summary>将指定的Javascript对象编译为脚本</summary>
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