// ==UserScript==
// @name:zh-CN 51job search filter with distance
// @name 51job search filter with distance
// @namespace https://github.com/zhuzemin
// @description:zh-CN 51job搜索结果以距离过滤
// @description 51job搜索结果以距离过滤
// @author zhuzemin
// @include https://search.51job.com/list/*
// @version 1.3
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @connect-src api.map.baidu.com
// @connect-src jobs.51job.com
// @run-at document-end
// ==/UserScript==
let config = {
'debug': false,
'origin': GM_getValue('origin').replace(/\s/, '') || null,
'origin_list': [],
'max_distence': GM_getValue('max_distence') || 12000,
'max_joblist': GM_getValue('max_joblist') || 10,
'joblist': [],
'button': null,
'running': false,
'api': {
'baidu': {
'url': `https://api.map.baidu.com/direction/v2/riding?origin={{origin}}&destination={{lat}},{{lng}}&ak={{ak}}`,
'ak': [
"RGBBNuGoAcxvzl02ibOAxGZM",
'xop0xOYgFxbts1hZhT8YAS2o4BoKfDZp'
],
'getpoint': 'https://api.map.baidu.com/lbsapi/getpoint/index.html'
},
'51job': {
'url': 'https://search.51job.com/jobsearch/bmap/map.php?jobid={{jobid}}'
}
},
}
let debug = config.debug ? console.log.bind(console) : function () {
};
debug(config.max_distence);
debug(config.max_joblist);
// prepare UserPrefs
setUserPref(
'origin',
config.origin,
'设置家的坐标',
`
(经度, 纬度), Example: "117.215491, 39.122174";
多个坐标, 以";"分隔;
*坐标来自百度地图: `+ config.api.baidu.getpoint
,
);
setUserPref(
'max_distence',
config.max_distence,
'设置距离上限',
'骑行距离, 单位(米)'
);
setUserPref(
'max_joblist',
config.max_joblist,
'搜索结果上限',
'搜索结果上限'
);
class requestObject {
constructor(url) {
this.method = 'GET';
this.respType = 'text';
this.url = url;
this.body = null;
this.headers = {
'User-agent': window.navigator.userAgent,
'Referer': window.location.href,
};
this.mimeType = 'text/html;charset=utf8';
}
}
async function init() {
debug(config.origin);
if (config.origin != null) {
for (let origin of config.origin.split(';')) {
let lat, lng;
for (let item of origin.split(',')) {
if (item.length == 9) {
lat = item;
}
else {
lng = item;
}
}
config.origin_list.push({
'lat': lat,
'lng': lng
});
}
debug(JSON.stringify(config.origin_list));
config.button = document.createElement('button');;
config.button.textContent = '按距离筛选';
config.button.className = 'p_but';
config.button.style.width = '100px';
config.button.addEventListener('click', () => {
config.joblist = [];
config.running = true;
filter();
});
document.querySelector('div.j_tlc').appendChild(config.button);
}
}
window.addEventListener('DOMContentLoaded', init);
async function filter() {
debug('filter');
if (config.running) {
config.button.textContent = "搜索中...";
setTimeout(async () => {
let j_joblist = document.querySelector("div.j_joblist");
for (let item of j_joblist.childNodes) {
let jobid = item.querySelector('input[name="delivery_jobid"').getAttribute('value');
let url = config.api['51job'].url.replace('{{jobid}}', jobid);
debug(url);
let obj = new requestObject(url);
obj.mimeType = 'text/html;charset=gb2312';
await httpRequest(obj).then(async (resp) => {
let dom = new DOMParser().parseFromString(resp.response, 'text/html');
let script = dom.querySelector('script');
debug(script.textContent);
eval(script.textContent);
let suc = false;
for (let origin of config.origin_list) {
if (g_company.lat > 0 && Math.abs(origin.lat - g_company.lat) <= 1 && Math.abs(origin.lng - g_company.lng) <= 1) {
let rnd = Math.floor(Math.random() * config.api.baidu.ak.length);
url = config.api.baidu.url.replace('{{ak}}', config.api.baidu.ak[rnd]);
url = url.replace('{{origin}}', origin.lat + ',' + origin.lng);
url = url.replace('{{lat}}', g_company.lat);
url = url.replace('{{lng}}', g_company.lng);
debug(url);
obj = new requestObject(url);
obj.respType = 'json';
await httpRequest(obj).then((resp) => {
if (resp.status == 200) {
//0=ok
if (resp.response.status == 0) {
let distance = resp.response.result.routes[0].distance;
debug(distance);
if (distance > config.max_distence) {
debug('jobid: ' + jobid + '; too far');
}
else {
debug('jobid: ' + jobid + '; distance: ' + distance);
config.joblist.push(item);
suc = true;
}
}
else {
debug('jobid: ' + jobid + '; status error: ' + JSON.stringify(resp.response) + '; finalUrl: ' + resp.finalUrl);
}
}
});
if (suc) {
debug('distance confort');
break;
}
}
else {
debug(g_company.name);
}
}
if (!suc) {
item.style.display = "none";
}
});
}
let page_num = document.querySelector('div.rt.rt_page');
let arr = page_num.textContent.match(/(\d+)\s\/\s(\d+)/);
let current = parseInt(arr[1]);
let total = parseInt(arr[2]);
debug(current + '/' + total);
debug('config.joblist: ' + config.joblist.length);
debug('config.max_joblist: ' + config.max_joblist);
if (config.joblist.length < config.max_joblist && current < total) {
debug('config.joblist: ' + config.joblist.length);
config.running = true;
page_num.querySelector('a.e_icons.i_next').click();
await filter();
}
else {
for (let item of config.joblist) {
let jobid = item.querySelector('input[name="delivery_jobid"').getAttribute('value');
for (let node of j_joblist.childNodes) {
let node_jobid = node.querySelector('input[name="delivery_jobid"').getAttribute('value');
if (jobid == node_jobid) {
break;
}
else if (node == j_joblist.lastChild) {
j_joblist.appendChild(item);
}
}
}
debug('suc');
config.button.textContent = "按距离筛选";
config.running = false;
}
}, 1000);
}
}
/**
* Create a user setting prompt
* @param {string} varName
* @param {any} defaultVal
* @param {string} menuText
* @param {string} promtText
* @param {function} func
*/
function setUserPref(varName, defaultVal, menuText, promtText, func = null) {
GM_registerMenuCommand(menuText, function () {
var val = prompt(promtText, GM_getValue(varName, defaultVal));
if (val === null) { return; }// end execution if clicked CANCEL
GM_setValue(varName, val);
if (func != null) {
func(val);
}
});
}
function httpRequest(object, timeout = 10000) {
return new Promise(
(resolve, reject) => {
GM_xmlhttpRequest({
method: object.method,
url: object.url,
headers: object.headers,
responseType: object.respType,
overrideMimeType: object.mimeType,
data: object.body,
timeout: timeout,
onload: function (responseDetails) {
debug(responseDetails);
//Dowork
resolve(responseDetails);
},
ontimeout: function (responseDetails) {
debug(responseDetails);
//Dowork
resolve(responseDetails);
},
ononerror: function (responseDetails) {
debug(responseDetails);
//Dowork
resolve(responseDetails);
}
});
}
)
}