ct4.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. "v1.0.0 Captcha4 Inc.";
  2. (function (window) {
  3. "use strict";
  4. if (typeof window === 'undefined') {
  5. throw new Error('Captcha4 requires browser environment');
  6. }
  7. var document = window.document;
  8. var Math = window.Math;
  9. var head = document.getElementsByTagName("head")[0];
  10. var TIMEOUT = 10000;
  11. function _Object(obj) {
  12. this._obj = obj;
  13. }
  14. _Object.prototype = {
  15. _each: function (process) {
  16. var _obj = this._obj;
  17. for (var k in _obj) {
  18. if (_obj.hasOwnProperty(k)) {
  19. process(k, _obj[k]);
  20. }
  21. }
  22. return this;
  23. },
  24. _extend: function (obj){
  25. var self = this;
  26. new _Object(obj)._each(function (key, value){
  27. self._obj[key] = value;
  28. })
  29. }
  30. };
  31. var uuid = function () {
  32. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
  33. var r = Math.random() * 16 | 0;
  34. var v = c === 'x' ? r : (r & 0x3 | 0x8);
  35. return v.toString(16);
  36. });
  37. };
  38. function Config(config) {
  39. var self = this;
  40. new _Object(config)._each(function (key, value) {
  41. self[key] = value;
  42. });
  43. }
  44. Config.prototype = {
  45. apiServers: ['captcha.alicaptcha.com','captchabak.alicaptcha.com'],
  46. staticServers: ["static.alicaptcha.com"],
  47. protocol: 'http://',
  48. typePath: '/load',
  49. fallback_config: {
  50. bypass: {
  51. staticServers: ["static.alicaptcha.com"],
  52. type: 'bypass',
  53. bypass: '/v4/alibypass.js'
  54. }
  55. },
  56. _get_fallback_config: function () {
  57. var self = this;
  58. if (isString(self.type)) {
  59. return self.fallback_config[self.type];
  60. } else {
  61. return self.fallback_config.bypass;
  62. }
  63. },
  64. _extend: function (obj) {
  65. var self = this;
  66. new _Object(obj)._each(function (key, value) {
  67. self[key] = value;
  68. })
  69. }
  70. };
  71. var isNumber = function (value) {
  72. return (typeof value === 'number');
  73. };
  74. var isString = function (value) {
  75. return (typeof value === 'string');
  76. };
  77. var isBoolean = function (value) {
  78. return (typeof value === 'boolean');
  79. };
  80. var isObject = function (value) {
  81. return (typeof value === 'object' && value !== null);
  82. };
  83. var isFunction = function (value) {
  84. return (typeof value === 'function');
  85. };
  86. var MOBILE = /Mobi/i.test(navigator.userAgent);
  87. var callbacks = {};
  88. var status = {};
  89. var random = function () {
  90. return parseInt(Math.random() * 10000) + (new Date()).valueOf();
  91. };
  92. // bind 函数polify, 不带new功能的bind
  93. var bind = function(target,context){
  94. if(typeof target !== 'function'){
  95. return;
  96. }
  97. var args = Array.prototype.slice.call(arguments,2);
  98. if(Function.prototype.bind){
  99. return target.bind(context, args);
  100. }else {
  101. return function(){
  102. var _args = Array.prototype.slice.call(arguments);
  103. return target.apply(context,args.concat(_args));
  104. }
  105. }
  106. }
  107. var toString = Object.prototype.toString;
  108. var _isFunction = function(obj) {
  109. return typeof(obj) === 'function';
  110. };
  111. var _isObject = function(obj) {
  112. return obj === Object(obj);
  113. };
  114. var _isArray = function(obj) {
  115. return toString.call(obj) == '[object Array]';
  116. };
  117. var _isDate = function(obj) {
  118. return toString.call(obj) == '[object Date]';
  119. };
  120. var _isRegExp = function(obj) {
  121. return toString.call(obj) == '[object RegExp]';
  122. };
  123. var _isBoolean = function(obj) {
  124. return toString.call(obj) == '[object Boolean]';
  125. };
  126. function resolveKey(input){
  127. return input.replace(/(\S)(_([a-zA-Z]))/g, function(match, $1, $2, $3){
  128. return $1 + $3.toUpperCase() || "";
  129. })
  130. }
  131. function camelizeKeys(input, convert){
  132. if(!_isObject(input) || _isDate(input) || _isRegExp(input) || _isBoolean(input) || _isFunction(input)){
  133. return convert ? resolveKey(input) : input;
  134. }
  135. if(_isArray(input)){
  136. var temp = [];
  137. for(var i = 0; i < input.length; i++){
  138. temp.push(camelizeKeys(input[i]));
  139. }
  140. }else {
  141. var temp = {};
  142. for(var prop in input){
  143. if(input.hasOwnProperty(prop)){
  144. temp[camelizeKeys(prop, true)] = camelizeKeys(input[prop]);
  145. }
  146. }
  147. }
  148. return temp;
  149. }
  150. var loadScript = function (url, cb, timeout) {
  151. var script = document.createElement("script");
  152. script.charset = "UTF-8";
  153. script.async = true;
  154. // 对geetest的静态资源添加 crossOrigin
  155. if ( /static\.geetest\.com/g.test(url)) {
  156. script.crossOrigin = "anonymous";
  157. }
  158. script.onerror = function () {
  159. cb(true);
  160. // 错误触发了,超时逻辑就不用了
  161. loaded = true;
  162. };
  163. var loaded = false;
  164. script.onload = script.onreadystatechange = function () {
  165. if (!loaded &&
  166. (!script.readyState ||
  167. "loaded" === script.readyState ||
  168. "complete" === script.readyState)) {
  169. loaded = true;
  170. setTimeout(function () {
  171. cb(false);
  172. }, 0);
  173. }
  174. };
  175. script.src = url;
  176. head.appendChild(script);
  177. setTimeout(function () {
  178. if (!loaded) {
  179. script.onerror = script.onload = null;
  180. script.remove && script.remove();
  181. cb(true);
  182. }
  183. }, timeout || TIMEOUT);
  184. };
  185. var normalizeDomain = function (domain) {
  186. // special domain: uems.sysu.edu.cn/jwxt/geetest/
  187. // return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
  188. return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
  189. };
  190. var normalizePath = function (path) {
  191. path = path && path.replace(/\/+/g, '/');
  192. if (path.indexOf('/') !== 0) {
  193. path = '/' + path;
  194. }
  195. return path;
  196. };
  197. var normalizeQuery = function (query) {
  198. if (!query) {
  199. return '';
  200. }
  201. var q = '?';
  202. new _Object(query)._each(function (key, value) {
  203. if (isString(value) || isNumber(value) || isBoolean(value)) {
  204. q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
  205. }
  206. });
  207. if (q === '?') {
  208. q = '';
  209. }
  210. return q.replace(/&$/, '');
  211. };
  212. var makeURL = function (protocol, domain, path, query) {
  213. domain = normalizeDomain(domain);
  214. var url = normalizePath(path) + normalizeQuery(query);
  215. if (domain) {
  216. url = protocol + domain + url;
  217. }
  218. return url;
  219. };
  220. var load = function (config, protocol, domains, path, query, cb, handleCb) {
  221. var tryRequest = function (at) {
  222. // 处理jsonp回调,这里为了保证每个不同jsonp都有唯一的回调函数
  223. if(handleCb){
  224. var cbName = "captcha4_" + random();
  225. // 需要与预先定义好cbname参数,删除对象
  226. window[cbName] = bind(handleCb, null, cbName);
  227. query.callback = cbName;
  228. }
  229. var url = makeURL(protocol, domains[at], path, query);
  230. loadScript(url, function (err) {
  231. if (err) {
  232. // 超时或者出错的时候 移除回调
  233. if(cbName){
  234. try {
  235. window[cbName] = function(){
  236. window[cbName] = null;
  237. }
  238. } catch (e) {}
  239. }
  240. if (at >= domains.length - 1) {
  241. cb(true);
  242. // report gettype error
  243. } else {
  244. tryRequest(at + 1);
  245. }
  246. } else {
  247. cb(false);
  248. }
  249. }, config.timeout);
  250. };
  251. tryRequest(0);
  252. };
  253. var jsonp = function (domains, path, config, callback) {
  254. var handleCb = function (cbName, data) {
  255. // 保证只执行一次,全部超时的情况下不会再触发;
  256. if (data.status == 'success') {
  257. callback(data.data);
  258. } else if (!data.status) {
  259. callback(data);
  260. } else {
  261. //接口有返回,但是返回了错误状态,进入报错逻辑
  262. callback(data);
  263. }
  264. window[cbName] = undefined;
  265. try {
  266. delete window[cbName];
  267. } catch (e) {
  268. }
  269. };
  270. load(config, config.protocol, domains, path, {
  271. callback: '',
  272. captcha_id: config.captchaId,
  273. challenge: config.challenge || uuid(),
  274. client_type: MOBILE? 'h5':'web',
  275. risk_type: config.riskType,
  276. user_info: config.userInfo,
  277. call_type: config.callType,
  278. lang: config.language? config.language : navigator.appName === 'Netscape' ? navigator.language.toLowerCase() : navigator.userLanguage.toLowerCase()
  279. }, function (err) {
  280. // 网络问题接口没有返回,直接使用本地验证码,走宕机模式
  281. // 这里可以添加用户的逻辑
  282. if(err && typeof config.offlineCb === 'function'){
  283. // 执行自己的宕机
  284. config.offlineCb();
  285. return;
  286. }
  287. if(err){
  288. callback(config._get_fallback_config());
  289. }
  290. }, handleCb);
  291. };
  292. var reportError = function (config, url) {
  293. load(config, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
  294. time: Date.now().getTime(),
  295. captcha_id: config.gt,
  296. challenge: config.challenge,
  297. exception_url: url,
  298. error_code: config.error_code
  299. }, function (err) {})
  300. }
  301. var throwError = function (errorType, config, errObj) {
  302. var errors = {
  303. networkError: '网络错误',
  304. gtTypeError: 'gt字段不是字符串类型'
  305. };
  306. if (typeof config.onError === 'function') {
  307. config.onError({
  308. desc: errObj.desc,
  309. msg: errObj.msg,
  310. code: errObj.code
  311. });
  312. } else {
  313. throw new Error(errors[errorType]);
  314. }
  315. };
  316. var detect = function () {
  317. return window.Captcha4 || document.getElementById("gt_lib");
  318. };
  319. if (detect()) {
  320. status.slide = "loaded";
  321. }
  322. var Captcha4IsLoad = function (fname) {
  323. var Captcha4IsLoad = false;
  324. var tags = { js: 'script', css: 'link' };
  325. var tagname = fname && tags[fname.split('.').pop()];
  326. if (tagname !== undefined) {
  327. var elts = document.getElementsByTagName(tagname);
  328. for (var i in elts) {
  329. if ((elts[i].href && elts[i].href.toString().indexOf(fname) > 0)
  330. || (elts[i].src && elts[i].src.toString().indexOf(fname) > 0)) {
  331. Captcha4IsLoad = true;
  332. }
  333. }
  334. }
  335. return Captcha4IsLoad;
  336. };
  337. window.initAlicom4 = function (userConfig,callback) {
  338. var config = new Config(userConfig);
  339. if (userConfig.https) {
  340. config.protocol = 'https://';
  341. } else if (!userConfig.protocol) {
  342. config.protocol = window.location.protocol + '//';
  343. }
  344. if (isObject(userConfig.getType)) {
  345. config._extend(userConfig.getType);
  346. }
  347. jsonp(config.apiServers , config.typePath, config, function (newConfig) {
  348. //错误捕获,第一个load请求可能直接报错
  349. var newConfig = camelizeKeys(newConfig);
  350. if(newConfig.status === 'error'){
  351. return throwError('networkError', config, newConfig);
  352. }
  353. var type = newConfig.type;
  354. if(config.debug){
  355. new _Object(newConfig)._extend(config.debug)
  356. }
  357. var init = function () {
  358. config._extend(newConfig);
  359. callback(new window.Captcha4(config));
  360. };
  361. callbacks[type] = callbacks[type] || [];
  362. var s = status[type] || 'init';
  363. if (s === 'init') {
  364. status[type] = 'loading';
  365. callbacks[type].push(init);
  366. if(newConfig.gctPath){
  367. load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers , newConfig.gctPath, null, function (err){
  368. if(err){
  369. throwError('networkError', config, {
  370. code: '60205',
  371. msg: 'Network failure',
  372. desc: {
  373. detail: 'gct resource load timeout'
  374. }
  375. });
  376. }
  377. })
  378. }
  379. load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers, newConfig.bypass || (newConfig.staticPath + newConfig.js), null, function (err) {
  380. if (err) {
  381. status[type] = 'fail';
  382. throwError('networkError', config, {
  383. code: '60204',
  384. msg: 'Network failure',
  385. desc: {
  386. detail: 'js resource load timeout'
  387. }
  388. });
  389. } else {
  390. status[type] = 'loaded';
  391. var cbs = callbacks[type];
  392. for (var i = 0, len = cbs.length; i < len; i = i + 1) {
  393. var cb = cbs[i];
  394. if (isFunction(cb)) {
  395. cb();
  396. }
  397. }
  398. callbacks[type] = [];
  399. }
  400. });
  401. } else if (s === "loaded") {
  402. // 判断gct是否需要重新加载
  403. if(newConfig.gctPath && !Captcha4IsLoad(newConfig.gctPath)){
  404. // if(!Captcha4IsLoad(newConfig.gctPath)){
  405. load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers , newConfig.gctPath, null, function (err){
  406. if(err){
  407. throwError('networkError', config, {
  408. code: '60205',
  409. msg: 'Network failure',
  410. desc: {
  411. detail: 'gct resource load timeout'
  412. }
  413. });
  414. }
  415. })
  416. }
  417. return init();
  418. } else if (s === "fail") {
  419. throwError('networkError', config, {
  420. code: '60204',
  421. msg: 'Network failure',
  422. desc: {
  423. detail: 'js resource load timeout'
  424. }
  425. });
  426. } else if (s === "loading") {
  427. callbacks[type].push(init);
  428. }
  429. });
  430. };
  431. })(window);