项目初始化
This commit is contained in:
353
src/app/shared/components/captcha/dun.ts
Normal file
353
src/app/shared/components/captcha/dun.ts
Normal file
@ -0,0 +1,353 @@
|
||||
/* eslint-disable no-undef */
|
||||
let errorCallbackCount: any = 0;
|
||||
|
||||
// 常量
|
||||
const DEFAULT_VALIDATE =
|
||||
'QjGAuvoHrcpuxlbw7cp4WnIbbjzG4rtSlpc7EDovNHQS._ujzPZpeCInSxIT4WunuDDh8dRZYF2GbBGWyHlC6q5uEi9x-TXT9j7J705vSsBXyTar7aqFYyUltKYJ7f4Y2TXm_1Mn6HFkb4M7URQ_rWtpxQ5D6hCgNJYC0HpRE7.2sttqYKLoi7yP1KHzK-PptdHHkVwb77cwS2EJW7Mj_PsOtnPBubTmTZLpnRECJR99dWTVC11xYG0sx8dJNLUxUFxEyzTfX4nSmQz_T5sXATRKHtVAz7nmV0De5unmflfAlUwMGKlCT1khBtewlgN5nHvyxeD8Z1_fPVzi9oznl-sbegj6lKfCWezmLcwft8.4yaVh6SlzXJq-FnSK.euq9OBd5jYc82ge2_hEca1fGU--SkPRzgwkzew4O4qjdS2utdPwFONnhKAIMJRPUmCV4lPHG1OeRDvyNV8sCnuFMw7leasxIhPoycl4pm5bNy70Z1laozEGJgItVNr3'; // 默认validate
|
||||
const FALLBACK_LANG: any = {
|
||||
'zh-CN': '前方拥堵,已自动跳过验证',
|
||||
en: 'captcha error,Verified automatically',
|
||||
};
|
||||
const CACHE_MIN = 1000 * 60; // 缓存时长单位,1分钟
|
||||
const REQUEST_SCRIPT_ERROR = 502;
|
||||
|
||||
const RESOURCE_CACHE: any = {};
|
||||
|
||||
// 工具函数
|
||||
function loadScript(src: any, cb: any) {
|
||||
const head: any = document.head || document.getElementsByTagName('head')[0];
|
||||
const script: any = document.createElement('script');
|
||||
|
||||
cb = cb || function () {};
|
||||
|
||||
script.type = 'text/javascript';
|
||||
script.charset = 'utf8';
|
||||
script.async = true;
|
||||
script.src = src;
|
||||
|
||||
if (!('onload' in script)) {
|
||||
script.onreadystatechange = function () {
|
||||
if (this.readyState !== 'complete' && this.readyState !== 'loaded') {
|
||||
return;
|
||||
}
|
||||
this.onreadystatechange = null;
|
||||
cb(null, script); // there is no way to catch loading errors in IE8
|
||||
};
|
||||
}
|
||||
|
||||
script.onload = function () {
|
||||
this.onerror = this.onload = null;
|
||||
cb(null, script);
|
||||
};
|
||||
script.onerror = function () {
|
||||
// because even IE9 works not like others
|
||||
this.onerror = this.onload = null;
|
||||
cb(new Error('Failed to load ' + this.src), script);
|
||||
};
|
||||
|
||||
head.appendChild(script);
|
||||
}
|
||||
|
||||
function joinUrl(protocol: any, host: any, path: any) {
|
||||
protocol = protocol || '';
|
||||
host = host || '';
|
||||
path = path || '';
|
||||
if (protocol) {
|
||||
protocol = protocol.replace(/:?\/{0,2}$/, '://');
|
||||
}
|
||||
if (host) {
|
||||
const matched = host.match(/^([-0-9a-zA-Z.:]*)(\/.*)?/);
|
||||
host = matched[1];
|
||||
path = (matched[2] || '') + '/' + path;
|
||||
}
|
||||
!host && (protocol = '');
|
||||
|
||||
return protocol + host + path;
|
||||
}
|
||||
|
||||
function setDomText(el: any, value: any) {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
const nodeType = el.nodeType;
|
||||
if (nodeType === 1 || nodeType === 11 || nodeType === 9) {
|
||||
if (typeof el.textContent === 'string') {
|
||||
el.textContent = value;
|
||||
} else {
|
||||
el.innerText = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function queryAllByClassName(selector: any, node: any) {
|
||||
node = node || document;
|
||||
if (node.querySelectorAll) {
|
||||
return node.querySelectorAll(selector);
|
||||
}
|
||||
if (!/^\.[^.]+$/.test(selector)) {
|
||||
return [];
|
||||
}
|
||||
if (node.getElementsByClassName) {
|
||||
return node.getElementsByClassName(selector);
|
||||
}
|
||||
|
||||
const children = node.getElementsByTagName('*');
|
||||
let current;
|
||||
const result = [];
|
||||
const className = selector.slice(1);
|
||||
for (let i = 0, l = children.length; i < l; i++) {
|
||||
current = children[i];
|
||||
if (~(' ' + current.className + ' ').indexOf(' ' + className + ' ')) {
|
||||
result.push(current);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function assert(condition: any, msg: any) {
|
||||
if (!condition) {
|
||||
throw new Error('[NECaptcha] ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
function isInteger(val: any) {
|
||||
if (Number.isInteger) {
|
||||
return Number.isInteger(val);
|
||||
}
|
||||
return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
|
||||
}
|
||||
|
||||
function isArray(val: any) {
|
||||
if (Array.isArray) {
|
||||
return Array.isArray(val);
|
||||
}
|
||||
return Object.prototype.toString.call(val) === '[object Array]';
|
||||
}
|
||||
|
||||
function ObjectAssign(a: any, b: any, c: any) {
|
||||
if (Object.assign) {
|
||||
// return Object.assign.apply(null, arguments);
|
||||
return Object.assign.apply(null, arguments as any);
|
||||
}
|
||||
|
||||
const target: any = {};
|
||||
for (let index = 1; index < arguments.length; index++) {
|
||||
const source = arguments[index];
|
||||
if (source != null) {
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function getTimestamp(msec: any) {
|
||||
msec = !msec && msec !== 0 ? msec : 1;
|
||||
return parseInt((new Date().valueOf() / msec).toString(), 10);
|
||||
}
|
||||
|
||||
// 降级方案
|
||||
function normalizeFallbackConfig(customConfig: any) {
|
||||
const siteProtocol = window.location.protocol.replace(':', '');
|
||||
const defaultConf: any = {
|
||||
protocol: siteProtocol === 'http' ? 'http' : 'https',
|
||||
lang: 'zh-CN',
|
||||
errorFallbackCount: 3,
|
||||
};
|
||||
const config: any = ObjectAssign({}, defaultConf, customConfig);
|
||||
|
||||
const errorFallbackCount: any = config.errorFallbackCount;
|
||||
assert(
|
||||
errorFallbackCount === undefined || (isInteger(errorFallbackCount) && errorFallbackCount >= 1),
|
||||
"errorFallbackCount must be an integer, and it's value greater than or equal one",
|
||||
);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
function loadResource(config: any, cb: any) {
|
||||
if ((window as any).initNECaptcha) {
|
||||
return cb(null);
|
||||
}
|
||||
function genUrl(server: any) {
|
||||
const path = 'load.min.js';
|
||||
let _urls = [];
|
||||
if (isArray(server)) {
|
||||
for (let i = 0, len = server.length; i < len; i++) {
|
||||
_urls.push(joinUrl(config.protocol, server[i], path));
|
||||
}
|
||||
} else {
|
||||
const url = joinUrl(config.protocol, server, path);
|
||||
_urls = [url, url];
|
||||
}
|
||||
|
||||
return _urls;
|
||||
}
|
||||
const urls = genUrl(config.staticServer || ['cstaticdun.126.net', 'cstaticdun1.126.net', 'cstatic.dun.163yun.com']);
|
||||
|
||||
function step(i: any) {
|
||||
const url = urls[i] + '?v=' + getTimestamp(CACHE_MIN);
|
||||
loadScript(url, function (err: any) {
|
||||
if (err || !(window as any).initNECaptcha) {
|
||||
// loadjs的全局变量
|
||||
i = i + 1;
|
||||
if (i === urls.length) {
|
||||
return cb(new Error('Failed to load script(' + url + ').' + (err ? err.message : 'unreliable script')));
|
||||
}
|
||||
return step(i);
|
||||
}
|
||||
return cb(null);
|
||||
});
|
||||
}
|
||||
step(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* entry: initNECaptchaWithFallback
|
||||
* options:
|
||||
* errorFallbackCount: 触发降级的错误次数,默认第三次错误降级
|
||||
* defaultFallback: 是否开启默认降级
|
||||
* onFallback: 自定义降级方案,参数为默认validate
|
||||
*/
|
||||
export function initNECaptchaWithFallback(options: any, onload: any, onerror: any) {
|
||||
let captchaIns: any = null;
|
||||
|
||||
const config = normalizeFallbackConfig(options);
|
||||
const defaultFallback = config.defaultFallback !== false;
|
||||
const langPkg = FALLBACK_LANG[config.lang === 'zh-CN' ? config.lang : 'en'];
|
||||
const storeKey = window.location.pathname + '_' + config.captchaId + '_NECAPTCHA_ERROR_COUNTS';
|
||||
try {
|
||||
errorCallbackCount = parseInt(localStorage.getItem(storeKey)?.toString() || '0', 10);
|
||||
} catch (error) {}
|
||||
|
||||
const fallbackFn = !defaultFallback
|
||||
? config.onFallback || function () {}
|
||||
: (validate: any) => {
|
||||
function setFallbackTip(instance: any) {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
setFallbackTip(instance._captchaIns);
|
||||
if (!instance.$el) {
|
||||
return;
|
||||
}
|
||||
const tipEles = queryAllByClassName('.yidun-fallback__tip', instance.$el);
|
||||
if (!tipEles.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保在队列的最后
|
||||
setTimeout(() => {
|
||||
for (let i = 0, l = tipEles.length; i < l; i++) {
|
||||
setDomText(tipEles[i], langPkg);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
setFallbackTip(captchaIns);
|
||||
|
||||
config.onVerify && config.onVerify(null, { validate: validate });
|
||||
};
|
||||
const noFallback = !defaultFallback && !config.onFallback;
|
||||
|
||||
const proxyOnError = (error: any) => {
|
||||
errorCallbackCount++;
|
||||
if (errorCallbackCount < config.errorFallbackCount) {
|
||||
try {
|
||||
localStorage.setItem(storeKey, errorCallbackCount);
|
||||
} catch (err) {}
|
||||
|
||||
onerror(error);
|
||||
} else {
|
||||
fallbackFn(DEFAULT_VALIDATE);
|
||||
proxyRefresh();
|
||||
noFallback && onerror(error);
|
||||
}
|
||||
};
|
||||
|
||||
const proxyRefresh = () => {
|
||||
errorCallbackCount = 0;
|
||||
try {
|
||||
localStorage.setItem(storeKey, '0');
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
const triggerInitError = (error: any) => {
|
||||
if (initialTimer && initialTimer.isError()) {
|
||||
initialTimer.resetError();
|
||||
return;
|
||||
}
|
||||
initialTimer && initialTimer.resetTimer();
|
||||
noFallback ? onerror(error) : proxyOnError(error);
|
||||
};
|
||||
|
||||
config.onError = (error: any) => {
|
||||
if (initialTimer && initialTimer.isError()) {
|
||||
initialTimer.resetError();
|
||||
}
|
||||
proxyOnError(error);
|
||||
};
|
||||
config.onDidRefresh = () => {
|
||||
if (initialTimer && initialTimer.isError()) {
|
||||
initialTimer.resetError();
|
||||
}
|
||||
proxyRefresh();
|
||||
};
|
||||
|
||||
const initialTimer = options.initTimeoutError ? options.initTimeoutError(proxyOnError) : null; // initialTimer is only for mobile.html
|
||||
|
||||
const loadResolve = () => {
|
||||
(window as any).initNECaptcha(
|
||||
config,
|
||||
(instance: any) => {
|
||||
if (initialTimer && initialTimer.isError()) {
|
||||
return;
|
||||
}
|
||||
initialTimer && initialTimer.resetTimer();
|
||||
captchaIns = instance;
|
||||
onload && onload(instance);
|
||||
},
|
||||
triggerInitError,
|
||||
);
|
||||
};
|
||||
const cacheId = 'load-queue';
|
||||
if (!RESOURCE_CACHE[cacheId]) {
|
||||
RESOURCE_CACHE[cacheId] = {
|
||||
rejects: [],
|
||||
resolves: [],
|
||||
status: 'error',
|
||||
};
|
||||
}
|
||||
if (RESOURCE_CACHE[cacheId].status === 'error') {
|
||||
RESOURCE_CACHE[cacheId].status = 'pending';
|
||||
loadResource(config, (error: any) => {
|
||||
if (error) {
|
||||
const err: any = new Error();
|
||||
err.code = REQUEST_SCRIPT_ERROR;
|
||||
err.message = config.staticServer + '/load.min.js error';
|
||||
|
||||
const rejects = RESOURCE_CACHE[cacheId].rejects;
|
||||
for (let i = 0, iLen = rejects.length; i < iLen; i++) {
|
||||
rejects.pop()(err);
|
||||
}
|
||||
RESOURCE_CACHE[cacheId].status = 'error';
|
||||
} else {
|
||||
RESOURCE_CACHE[cacheId].status = 'done';
|
||||
const resolves = RESOURCE_CACHE[cacheId].resolves;
|
||||
for (let j = 0, jLen = resolves.length; j < jLen; j++) {
|
||||
resolves.pop()();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (RESOURCE_CACHE[cacheId].status === 'done') {
|
||||
loadResolve();
|
||||
}
|
||||
if (RESOURCE_CACHE[cacheId].status === 'pending') {
|
||||
RESOURCE_CACHE[cacheId].rejects.push(function loadReject(err: any) {
|
||||
triggerInitError(err);
|
||||
});
|
||||
RESOURCE_CACHE[cacheId].resolves.push(loadResolve);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user