51job search filter with distance

51job搜索结果以距离过滤

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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);

				}
			});
		}
	)
}