539 lines
14 KiB
JavaScript
539 lines
14 KiB
JavaScript
"v4.1.8 Geetest Inc."
|
||
|
||
;(function (window) {
|
||
"use strict"
|
||
if (typeof window === "undefined") {
|
||
throw new Error("Geetest requires browser environment")
|
||
}
|
||
|
||
var document = window.document
|
||
var Math = window.Math
|
||
var head = document.getElementsByTagName("head")[0]
|
||
var TIMEOUT = 10000
|
||
|
||
function _Object(obj) {
|
||
this._obj = obj
|
||
}
|
||
|
||
_Object.prototype = {
|
||
_each: function (process) {
|
||
var _obj = this._obj
|
||
for (var k in _obj) {
|
||
if (_obj.hasOwnProperty(k)) {
|
||
process(k, _obj[k])
|
||
}
|
||
}
|
||
return this
|
||
},
|
||
_extend: function (obj) {
|
||
var self = this
|
||
new _Object(obj)._each(function (key, value) {
|
||
self._obj[key] = value
|
||
})
|
||
},
|
||
}
|
||
|
||
var uuid = function () {
|
||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
||
/[xy]/g,
|
||
function (c) {
|
||
var r = (Math.random() * 16) | 0
|
||
var v = c === "x" ? r : (r & 0x3) | 0x8
|
||
return v.toString(16)
|
||
},
|
||
)
|
||
}
|
||
|
||
function Config(config) {
|
||
var self = this
|
||
new _Object(config)._each(function (key, value) {
|
||
self[key] = value
|
||
})
|
||
}
|
||
|
||
Config.prototype = {
|
||
apiServers: [
|
||
"gcaptcha4.geetest.com",
|
||
"gcaptcha4.geevisit.com",
|
||
"gcaptcha4.gsensebot.com",
|
||
],
|
||
staticServers: ["static.geetest.com", "static.geevisit.com"],
|
||
protocol: "http://",
|
||
typePath: "/load",
|
||
fallback_config: {
|
||
bypass: {
|
||
staticServers: ["static.geetest.com", "static.geevisit.com"],
|
||
type: "bypass",
|
||
bypass: "/v4/bypass.js",
|
||
},
|
||
},
|
||
_get_fallback_config: function () {
|
||
var self = this
|
||
if (isString(self.type)) {
|
||
return self.fallback_config[self.type]
|
||
} else {
|
||
return self.fallback_config.bypass
|
||
}
|
||
},
|
||
_extend: function (obj) {
|
||
var self = this
|
||
new _Object(obj)._each(function (key, value) {
|
||
self[key] = value
|
||
})
|
||
},
|
||
}
|
||
var isNumber = function (value) {
|
||
return typeof value === "number"
|
||
}
|
||
var isString = function (value) {
|
||
return typeof value === "string"
|
||
}
|
||
var isBoolean = function (value) {
|
||
return typeof value === "boolean"
|
||
}
|
||
var isObject = function (value) {
|
||
return typeof value === "object" && value !== null
|
||
}
|
||
var isFunction = function (value) {
|
||
return typeof value === "function"
|
||
}
|
||
var MOBILE = /Mobi/i.test(navigator.userAgent)
|
||
|
||
var callbacks = {}
|
||
var status = {}
|
||
|
||
var random = function () {
|
||
return parseInt(Math.random() * 10000) + new Date().valueOf()
|
||
}
|
||
|
||
// bind 函数polify, 不带new功能的bind
|
||
|
||
var bind = function (target, context) {
|
||
if (typeof target !== "function") {
|
||
return
|
||
}
|
||
var args = Array.prototype.slice.call(arguments, 2)
|
||
|
||
if (Function.prototype.bind) {
|
||
return target.bind(context, args)
|
||
} else {
|
||
return function () {
|
||
var _args = Array.prototype.slice.call(arguments)
|
||
return target.apply(context, args.concat(_args))
|
||
}
|
||
}
|
||
}
|
||
|
||
var toString = Object.prototype.toString
|
||
|
||
var _isFunction = function (obj) {
|
||
return typeof obj === "function"
|
||
}
|
||
var _isObject = function (obj) {
|
||
return obj === Object(obj)
|
||
}
|
||
var _isArray = function (obj) {
|
||
return toString.call(obj) == "[object Array]"
|
||
}
|
||
var _isDate = function (obj) {
|
||
return toString.call(obj) == "[object Date]"
|
||
}
|
||
var _isRegExp = function (obj) {
|
||
return toString.call(obj) == "[object RegExp]"
|
||
}
|
||
var _isBoolean = function (obj) {
|
||
return toString.call(obj) == "[object Boolean]"
|
||
}
|
||
|
||
function resolveKey(input) {
|
||
return input.replace(/(\S)(_([a-zA-Z]))/g, function (match, $1, $2, $3) {
|
||
return $1 + $3.toUpperCase() || ""
|
||
})
|
||
}
|
||
|
||
function camelizeKeys(input, convert) {
|
||
if (
|
||
!_isObject(input) ||
|
||
_isDate(input) ||
|
||
_isRegExp(input) ||
|
||
_isBoolean(input) ||
|
||
_isFunction(input)
|
||
) {
|
||
return convert ? resolveKey(input) : input
|
||
}
|
||
|
||
if (_isArray(input)) {
|
||
var temp = []
|
||
for (var i = 0; i < input.length; i++) {
|
||
temp.push(camelizeKeys(input[i]))
|
||
}
|
||
} else {
|
||
var temp = {}
|
||
for (var prop in input) {
|
||
if (input.hasOwnProperty(prop)) {
|
||
temp[camelizeKeys(prop, true)] = camelizeKeys(input[prop])
|
||
}
|
||
}
|
||
}
|
||
return temp
|
||
}
|
||
|
||
var loadScript = function (url, cb, timeout) {
|
||
var script = document.createElement("script")
|
||
script.charset = "UTF-8"
|
||
script.async = true
|
||
|
||
// 对geetest的静态资源添加 crossOrigin
|
||
if (/static\.geetest\.com/g.test(url)) {
|
||
script.crossOrigin = "anonymous"
|
||
}
|
||
|
||
script.onerror = function () {
|
||
cb(true)
|
||
// 错误触发了,超时逻辑就不用了
|
||
loaded = true
|
||
}
|
||
var loaded = false
|
||
script.onload = script.onreadystatechange = function () {
|
||
if (
|
||
!loaded &&
|
||
(!script.readyState ||
|
||
"loaded" === script.readyState ||
|
||
"complete" === script.readyState)
|
||
) {
|
||
loaded = true
|
||
setTimeout(function () {
|
||
cb(false)
|
||
}, 0)
|
||
}
|
||
}
|
||
script.src = url
|
||
head.appendChild(script)
|
||
|
||
setTimeout(function () {
|
||
if (!loaded) {
|
||
script.onerror = script.onload = null
|
||
script.remove && script.remove()
|
||
cb(true)
|
||
}
|
||
}, timeout || TIMEOUT)
|
||
}
|
||
|
||
var normalizeDomain = function (domain) {
|
||
// special domain: uems.sysu.edu.cn/jwxt/geetest/
|
||
// return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
|
||
return domain.replace(/^https?:\/\/|\/$/g, "") // uems.sysu.edu.cn/jwxt/geetest
|
||
}
|
||
var normalizePath = function (path) {
|
||
path = path && path.replace(/\/+/g, "/")
|
||
if (path.indexOf("/") !== 0) {
|
||
path = "/" + path
|
||
}
|
||
return path
|
||
}
|
||
var normalizeQuery = function (query) {
|
||
if (!query) {
|
||
return ""
|
||
}
|
||
var q = "?"
|
||
new _Object(query)._each(function (key, value) {
|
||
if (isString(value) || isNumber(value) || isBoolean(value)) {
|
||
q = q + encodeURIComponent(key) + "=" + encodeURIComponent(value) + "&"
|
||
}
|
||
})
|
||
if (q === "?") {
|
||
q = ""
|
||
}
|
||
return q.replace(/&$/, "")
|
||
}
|
||
var makeURL = function (protocol, domain, path, query) {
|
||
domain = normalizeDomain(domain)
|
||
|
||
var url = normalizePath(path) + normalizeQuery(query)
|
||
if (domain) {
|
||
url = protocol + domain + url
|
||
}
|
||
|
||
return url
|
||
}
|
||
|
||
var load = function (config, protocol, domains, path, query, cb, handleCb) {
|
||
var tryRequest = function (at) {
|
||
// 处理jsonp回调,这里为了保证每个不同jsonp都有唯一的回调函数
|
||
if (handleCb) {
|
||
var cbName = "geetest_" + random()
|
||
// 需要与预先定义好cbname参数,删除对象
|
||
window[cbName] = bind(handleCb, null, cbName)
|
||
query.callback = cbName
|
||
}
|
||
var url = makeURL(protocol, domains[at], path, query)
|
||
loadScript(
|
||
url,
|
||
function (err) {
|
||
if (err) {
|
||
// 超时或者出错的时候 移除回调
|
||
if (cbName) {
|
||
try {
|
||
window[cbName] = function () {
|
||
window[cbName] = null
|
||
}
|
||
} catch (e) {}
|
||
}
|
||
|
||
if (at >= domains.length - 1) {
|
||
cb(true)
|
||
// report gettype error
|
||
} else {
|
||
tryRequest(at + 1)
|
||
}
|
||
} else {
|
||
cb(false)
|
||
}
|
||
},
|
||
config.timeout,
|
||
)
|
||
}
|
||
tryRequest(0)
|
||
}
|
||
|
||
var jsonp = function (domains, path, config, callback) {
|
||
var handleCb = function (cbName, data) {
|
||
// 保证只执行一次,全部超时的情况下不会再触发;
|
||
|
||
if (data.status == "success") {
|
||
callback(data.data)
|
||
} else if (!data.status) {
|
||
callback(data)
|
||
} else {
|
||
//接口有返回,但是返回了错误状态,进入报错逻辑
|
||
callback(data)
|
||
}
|
||
window[cbName] = undefined
|
||
try {
|
||
delete window[cbName]
|
||
} catch (e) {}
|
||
}
|
||
load(
|
||
config,
|
||
config.protocol,
|
||
domains,
|
||
path,
|
||
{
|
||
callback: "",
|
||
captcha_id: config.captchaId,
|
||
challenge: config.challenge || uuid(),
|
||
client_type: MOBILE ? "h5" : "web",
|
||
risk_type: config.riskType,
|
||
user_info: config.userInfo,
|
||
call_type: config.callType,
|
||
lang: config.language
|
||
? config.language
|
||
: navigator.appName === "Netscape"
|
||
? navigator.language.toLowerCase()
|
||
: navigator.userLanguage.toLowerCase(),
|
||
},
|
||
function (err) {
|
||
// 网络问题接口没有返回,直接使用本地验证码,走宕机模式
|
||
// 这里可以添加用户的逻辑
|
||
if (err && typeof config.offlineCb === "function") {
|
||
// 执行自己的宕机
|
||
config.offlineCb()
|
||
return
|
||
}
|
||
if (err) {
|
||
callback(config._get_fallback_config())
|
||
}
|
||
},
|
||
handleCb,
|
||
)
|
||
}
|
||
|
||
var reportError = function (config, url) {
|
||
load(
|
||
config,
|
||
config.protocol,
|
||
["monitor.geetest.com"],
|
||
"/monitor/send",
|
||
{
|
||
time: Date.now().getTime(),
|
||
captcha_id: config.gt,
|
||
challenge: config.challenge,
|
||
exception_url: url,
|
||
error_code: config.error_code,
|
||
},
|
||
function (err) {},
|
||
)
|
||
}
|
||
|
||
var throwError = function (errorType, config, errObj) {
|
||
var errors = {
|
||
networkError: "网络错误",
|
||
gtTypeError: "gt字段不是字符串类型",
|
||
}
|
||
if (typeof config.onError === "function") {
|
||
config.onError({
|
||
desc: errObj.desc,
|
||
msg: errObj.msg,
|
||
code: errObj.code,
|
||
})
|
||
} else {
|
||
throw new Error(errors[errorType])
|
||
}
|
||
}
|
||
|
||
var detect = function () {
|
||
return window.Geetest || document.getElementById("gt_lib")
|
||
}
|
||
|
||
if (detect()) {
|
||
status.slide = "loaded"
|
||
}
|
||
var GeetestIsLoad = function (fname) {
|
||
var GeetestIsLoad = false
|
||
var tags = { js: "script", css: "link" }
|
||
var tagname = fname && tags[fname.split(".").pop()]
|
||
if (tagname !== undefined) {
|
||
var elts = document.getElementsByTagName(tagname)
|
||
for (var i in elts) {
|
||
if (
|
||
(elts[i].href && elts[i].href.toString().indexOf(fname) > 0) ||
|
||
(elts[i].src && elts[i].src.toString().indexOf(fname) > 0)
|
||
) {
|
||
GeetestIsLoad = true
|
||
}
|
||
}
|
||
}
|
||
return GeetestIsLoad
|
||
}
|
||
window.initGeetest4 = function (userConfig, callback) {
|
||
var config = new Config(userConfig)
|
||
if (userConfig.https) {
|
||
config.protocol = "https://"
|
||
} else if (!userConfig.protocol) {
|
||
config.protocol = window.location.protocol + "//"
|
||
}
|
||
|
||
if (isObject(userConfig.getType)) {
|
||
config._extend(userConfig.getType)
|
||
}
|
||
|
||
jsonp(config.apiServers, config.typePath, config, function (newConfig) {
|
||
//错误捕获,第一个load请求可能直接报错
|
||
var newConfig = camelizeKeys(newConfig)
|
||
|
||
if (newConfig.status === "error") {
|
||
return throwError("networkError", config, newConfig)
|
||
}
|
||
|
||
var type = newConfig.type
|
||
if (config.debug) {
|
||
new _Object(newConfig)._extend(config.debug)
|
||
}
|
||
var init = function () {
|
||
config._extend(newConfig)
|
||
callback(new window.Geetest4(config))
|
||
}
|
||
|
||
callbacks[type] = callbacks[type] || []
|
||
|
||
var s = status[type] || "init"
|
||
if (s === "init") {
|
||
status[type] = "loading"
|
||
|
||
callbacks[type].push(init)
|
||
|
||
if (newConfig.gctPath) {
|
||
load(
|
||
config,
|
||
config.protocol,
|
||
Object.hasOwnProperty.call(config, "staticServers")
|
||
? config.staticServers
|
||
: newConfig.staticServers || config.staticServers,
|
||
newConfig.gctPath,
|
||
null,
|
||
function (err) {
|
||
if (err) {
|
||
throwError("networkError", config, {
|
||
code: "60205",
|
||
msg: "Network failure",
|
||
desc: {
|
||
detail: "gct resource load timeout",
|
||
},
|
||
})
|
||
}
|
||
},
|
||
)
|
||
}
|
||
|
||
load(
|
||
config,
|
||
config.protocol,
|
||
Object.hasOwnProperty.call(config, "staticServers")
|
||
? config.staticServers
|
||
: newConfig.staticServers || config.staticServers,
|
||
newConfig.bypass || newConfig.staticPath + newConfig.js,
|
||
null,
|
||
function (err) {
|
||
if (err) {
|
||
status[type] = "fail"
|
||
throwError("networkError", config, {
|
||
code: "60204",
|
||
msg: "Network failure",
|
||
desc: {
|
||
detail: "js resource load timeout",
|
||
},
|
||
})
|
||
} else {
|
||
status[type] = "loaded"
|
||
var cbs = callbacks[type]
|
||
for (var i = 0, len = cbs.length; i < len; i = i + 1) {
|
||
var cb = cbs[i]
|
||
if (isFunction(cb)) {
|
||
cb()
|
||
}
|
||
}
|
||
callbacks[type] = []
|
||
status[type] = "init"
|
||
}
|
||
},
|
||
)
|
||
} else if (s === "loaded") {
|
||
// 判断gct是否需要重新加载
|
||
if (newConfig.gctPath && !GeetestIsLoad(newConfig.gctPath)) {
|
||
load(
|
||
config,
|
||
config.protocol,
|
||
Object.hasOwnProperty.call(config, "staticServers")
|
||
? config.staticServers
|
||
: newConfig.staticServers || config.staticServers,
|
||
newConfig.gctPath,
|
||
null,
|
||
function (err) {
|
||
if (err) {
|
||
throwError("networkError", config, {
|
||
code: "60205",
|
||
msg: "Network failure",
|
||
desc: {
|
||
detail: "gct resource load timeout",
|
||
},
|
||
})
|
||
}
|
||
},
|
||
)
|
||
}
|
||
return init()
|
||
} else if (s === "fail") {
|
||
throwError("networkError", config, {
|
||
code: "60204",
|
||
msg: "Network failure",
|
||
desc: {
|
||
detail: "js resource load timeout",
|
||
},
|
||
})
|
||
} else if (s === "loading") {
|
||
callbacks[type].push(init)
|
||
}
|
||
})
|
||
}
|
||
})(window)
|