PageSourceSearch

https://viewmycube.com/assets/scripts/slider.captcha.js

js viewmycube.com collected 2026-10-08 10:06:29 UTC 13,714 bytes, 347 lines download raw bytes

1(function () {
2    'use strict';
3
4    var extend = function () {
5        var length = arguments.length;
6        var target = arguments[0] || {};
7        if (typeof target != "object" && typeof target != "function") {
8            target = {};
9        }
10        if (length == 1) {
11            target = this;
12            i--;
13        }
14        for (var i = 1; i < length; i++) {
15            var source = arguments[i];
16            for (var key in source) {
17                // 使用for in会遍历数组所有的可枚举属性,包括原型。
18                if (Object.prototype.hasOwnProperty.call(source, key)) {
19                    target[key] = source[key];
20                }
21            }
22        }
23        return target;
24    }
25
26    var isFunction = function isFunction(obj) {
27        return typeof obj === "function" && typeof obj.nodeType !== "number";
28    };
29
30    var SliderCaptcha = function (element, options) {
31        this.$element = element;
32        this.options = extend({}, SliderCaptcha.DEFAULTS, options);
33        this.$element.style.position = 'relative';
34        this.$element.style.width = this.options.width + 'px';
35        this.$element.style.margin = '0 auto';
36        this.init();
37    };
38
39    SliderCaptcha.VERSION = '1.0';
40    SliderCaptcha.Author = '[email protected]';
41    SliderCaptcha.DEFAULTS = {
42        width: 280,     // canvas宽度
43        height: 155,    // canvas高度
44        PI: Math.PI,
45        sliderL: 42,    // 滑块边长
46        sliderR: 9,     // 滑块半径
47        offset: 5,      // 容错偏差
48        loadingText: '正在加载中...',
49        failedText: '再试一次',
50        barText: '向右滑动填充拼图',
51        repeatIcon: 'fa fa-repeat',
52        maxLoadCount: 3,
53        localImages: function () {
54            return 'images/Pic' + Math.round(Math.random() * 4) + '.jpg';
55        },
56        verify: function (arr, url) {
57            var ret = false;
58            $.ajax({
59                url: url,
60                data: {
61                    "datas": JSON.stringify(arr),
62                },
63                dataType: "json",
64                type: "post",
65                async: false,
66                success: function (result) {
67                    ret = JSON.stringify(result);
68                    // console.log("返回结果:" + ret)
69                }
70            });
71            return ret;
72        },
73        remoteUrl: null
74    };
75
76    function Plugin(option) {
77        var $this = document.getElementById(option.id);
78        var options = typeof option === 'object' && option;
79        return new SliderCaptcha($this, options);
80    }
81
82    window.sliderCaptcha = Plugin;
83    window.sliderCaptcha.Constructor = SliderCaptcha;
84
85    var _proto = SliderCaptcha.prototype;
86    _proto.init = function () {
87        this.initDOM();
88        this.initImg();
89        this.bindEvents();
90    };
91
92    _proto.initDOM = function () {
93        var createElement = function (tagName, className) {
94            var elment = document.createElement(tagName);
95            elment.className = className;
96            return elment;
97        };
98
99        var createCanvas = function (width, height) {
100            var canvas = document.createElement('canvas');
101            canvas.width = width;
102            canvas.height = height;
103            return canvas;
104        };
105
106        var canvas = createCanvas(this.options.width - 2, this.options.height); // 画布
107        var block = canvas.cloneNode(true); // 滑块
108        var sliderContainer = createElement('div', 'sliderContainer');
109        var refreshIcon = createElement('i', 'refreshIcon ' + this.options.repeatIcon);
110        var sliderMask = createElement('div', 'sliderMask');
111        var sliderbg = createElement('div', 'sliderbg');
112        var slider = createElement('div', 'slider');
113        var sliderIcon = createElement('i', 'fa fa-arrow-right sliderIcon');
114        var text = createElement('span', 'sliderText');
115
116        block.className = 'block';
117        text.innerHTML = this.options.barText;
118
119        var el = this.$element;
120        el.appendChild(canvas);
121        el.appendChild(refreshIcon);
122        el.appendChild(block);
123        slider.appendChild(sliderIcon);
124        sliderMask.appendChild(slider);
125        sliderContainer.appendChild(sliderbg);
126        sliderContainer.appendChild(sliderMask);
127        sliderContainer.appendChild(text);
128        el.appendChild(sliderContainer);
129
130        var _canvas = {
131            canvas: canvas,
132            block: block,
133            sliderContainer: sliderContainer,
134            refreshIcon: refreshIcon,
135            slider: slider,
136            sliderMask: sliderMask,
137            sliderIcon: sliderIcon,
138            text: text,
139            canvasCtx: canvas.getContext('2d'),
140            blockCtx: block.getContext('2d')
141        };
142
143        if (isFunction(Object.assign)) {
144            Object.assign(this, _canvas);
145        }
146        else {
147            extend(this, _canvas);
148        }
149    };
150
151    _proto.initImg = function () {
152        var that = this;
153        var isIE = window.navigator.userAgent.indexOf('Trident') > -1;
154        var L = this.options.sliderL + this.options.sliderR * 2 + 3; // 滑块实际边长
155        var drawImg = function (ctx, operation) {
156            var l = that.options.sliderL;
157            var r = that.options.sliderR;
158            var PI = that.options.PI;
159            var x = that.x;
160            var y = that.y;
161            ctx.beginPath();
162            ctx.moveTo(x, y);
163            ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
164            ctx.lineTo(x + l, y);
165            ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);
166            ctx.lineTo(x + l, y + l);
167            ctx.lineTo(x, y + l);
168            ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
169            ctx.lineTo(x, y);
170            ctx.lineWidth = 2;
171            ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
172            ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
173            ctx.stroke();
174            ctx[operation]();
175            ctx.globalCompositeOperation = isIE ? 'xor' : 'destination-over';
176        };
177
178        var getRandomNumberByRange = function (start, end) {
179            return Math.round(Math.random() * (end - start) + start);
180        };
181        var img = new Image();
182        img.crossOrigin = "Anonymous";
183        var loadCount = 0;
184        img.onload = function () {
185            // 随机创建滑块的位置
186            that.x = getRandomNumberByRange(L + 10, that.options.width - (L + 10));
187            that.y = getRandomNumberByRange(10 + that.options.sliderR * 2, that.options.height - (L + 10));
188            drawImg(that.canvasCtx, 'fill');
189            drawImg(that.blockCtx, 'clip');
190
191            that.canvasCtx.drawImage(img, 0, 0, that.options.width - 2, that.options.height);
192            that.blockCtx.drawImage(img, 0, 0, that.options.width - 2, that.options.height);
193            var y = that.y - that.options.sliderR * 2 - 1;
194            var ImageData = that.blockCtx.getImageData(that.x - 3, y, L, L);
195            that.block.width = L;
196            that.blockCtx.putImageData(ImageData, 0, y + 1);
197            that.text.textContent = that.text.getAttribute('data-text');
198        };
199        img.onerror = function () {
200            loadCount++;
201            if (window.location.protocol === 'file:') {
202                loadCount = that.options.maxLoadCount;
203                console.error("can't load pic resource file from File protocal. Please try http or https");
204            }
205            if (loadCount >= that.options.maxLoadCount) {
206                that.text.textContent = '加载失败';
207                that.classList.add('text-danger');
208                return;
209            }
210            img.src = that.options.localImages();
211        };
212        img.setSrc = function () {
213            var src = '';
214            loadCount = 0;
215            that.text.classList.remove('text-danger');
216            if (isFunction(that.options.setSrc)) src = that.options.setSrc();
217            if (!src || src === '') src = 'https://picsum.photos/' + that.options.width + '/' + that.options.height + '/?image=' + Math.round(Math.random() * 20);
218            if (isIE) { // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示
219                var xhr = new XMLHttpRequest();
220                xhr.onloadend = function (e) {
221                    var file = new FileReader(); // FileReader仅支持IE10+
222                    file.readAsDataURL(e.target.response);
223                    file.onloadend = function (e) {
224                        img.src = e.target.result;
225                    };
226                };
227                xhr.open('GET', src);
228                xhr.responseType = 'blob';
229                xhr.send();
230            } else img.src = src;
231        };
232        img.setSrc();
233        this.text.setAttribute('data-text', this.options.barText);
234        this.text.textContent = this.options.loadingText;
235        this.img = img;
236    };
237
238    _proto.clean = function () {
239        this.canvasCtx.clearRect(0, 0, this.options.width, this.options.height);
240        this.blockCtx.clearRect(0, 0, this.options.width, this.options.height);
241        this.block.width = this.options.width;
242    };
243
244    _proto.bindEvents = function () {
245        var that = this;
246        this.$element.addEventListener('selectstart', function () {
247            return false;
248        });
249
250        this.refreshIcon.addEventListener('click', function () {
251            that.text.textContent = that.options.barText;
252            that.reset();
253            if (isFunction(that.options.onRefresh)) that.options.onRefresh.call(that.$element);
254        });
255
256        var originX, originY, trail = [],
257            isMouseDown = false;
258
259        var handleDragStart = function (e) {
260            if (that.text.classList.contains('text-danger')) return;
261            originX = e.clientX || e.touches[0].clientX;
262            originY = e.clientY || e.touches[0].clientY;
263            isMouseDown = true;
264        };
265
266        var handleDragMove = function (e) {
267            if (!isMouseDown) return false;
268            var eventX = e.clientX || e.touches[0].clientX;
269            var eventY = e.clientY || e.touches[0].clientY;
270            var moveX = eventX - originX;
271            var moveY = eventY - originY;
272            if (moveX < 0 || moveX + 40 > that.options.width) return false;
273            that.slider.style.left = (moveX - 1) + 'px';
274            var blockLeft = (that.options.width - 40 - 20) / (that.options.width - 40) * moveX;
275            that.block.style.left = blockLeft + 'px';
276
277            that.sliderContainer.classList.add('sliderContainer_active');
278            that.sliderMask.style.width = (moveX + 4) + 'px';
279            trail.push(Math.round(moveY));
280        };
281
282        var handleDragEnd = function (e) {
283            if (!isMouseDown) return false;
284            isMouseDown = false;
285            var eventX = e.clientX || e.changedTouches[0].clientX;
286            if (eventX === originX) return false;
287            that.sliderContainer.classList.remove('sliderContainer_active');
288            that.trail = trail;
289            var data = that.verify();
290            if (data.spliced && data.verified) {
291                that.sliderContainer.classList.add('sliderContainer_success');
292                if (isFunction(that.options.onSuccess)) that.options.onSuccess.call(that.$element);
293            } else {
294                that.sliderContainer.classList.add('sliderContainer_fail');
295                if (isFunction(that.options.onFail)) that.options.onFail.call(that.$element);
296                setTimeout(function () {
297                    that.text.innerHTML = that.options.failedText;
298                    that.reset();
299                }, 1000);
300            }
301        };
302
303        this.slider.addEventListener('mousedown', handleDragStart);
304        this.slider.addEventListener('touchstart', handleDragStart);
305        document.addEventListener('mousemove', handleDragMove);
306        document.addEventListener('touchmove', handleDragMove);
307        document.addEventListener('mouseup', handleDragEnd);
308        document.addEventListener('touchend', handleDragEnd);
309
310        document.addEventListener('mousedown', function () { return false; });
311        document.addEventListener('touchstart', function () { return false; });
312        document.addEventListener('swipe', function () { return false; });
313    };
314
315    _proto.verify = function () {
316        var arr = this.trail; // 拖动时y轴的移动距离
317        var left = parseInt(this.block.style.left);
318        var verified = false;
319        if (this.options.remoteUrl !== null) {
320            verified = this.options.verify(arr, this.options.remoteUrl);
321        }
322        else {
323            var sum = function (x, y) { return x + y; };
324            var square = function (x) { return x * x; };
325            var average = arr.reduce(sum) / arr.length;
326            var deviations = arr.map(function (x) { return x - average; });
327            var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length);
328            verified = stddev !== 0;
329        }
330        return {
331            spliced: Math.abs(left - this.x) < this.options.offset,
332            verified: verified
333        };
334    };
335
336    _proto.reset = function () {
337        this.sliderContainer.classList.remove('sliderContainer_fail');
338        this.sliderContainer.classList.remove('sliderContainer_success');
339        this.slider.style.left = 0;
340        this.block.style.left = 0;
341        this.sliderMask.style.width = 0;
342        this.clean();
343        this.text.setAttribute('data-text', this.text.textContent);
344        this.text.textContent = this.options.loadingText;
345        this.img.setSrc();
346    };
347})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.