UNPKG

vue-client-recaptcha

Version:
278 lines (276 loc) 9.25 kB
/* * vue-client-recaptcha * Creator:parsajiravand * Build simple recaptcha for vuejs without need server * v2.0.1 * MIT License */ import { ref as P, defineComponent as F, useSlots as I, computed as p, onMounted as U, watch as L, watchEffect as H, openBlock as j, createBlock as N, h as f } from "vue"; const k = { alphanumeric: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", numeric: "0123456789", letters: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" }; function K() { if (typeof crypto < "u" && crypto.getRandomValues) { const i = new Uint32Array(1); return crypto.getRandomValues(i), i[0] / 4294967296; } return Math.random(); } function W(i = {}) { const w = typeof i == "function" ? i : () => i, v = P(""); function m() { const { chars: h = k.alphanumeric, charsPreset: r = "alphanumeric", count: a = 5 } = w(), g = r === "custom" ? h : k[r] ?? h; let s = ""; const c = g; for (let u = 0; u < a; u++) { const x = Math.floor(K() * c.length); s += c[x]; } return v.value = s, s; } function l(h) { return !!v.value && v.value === h; } function e() { return m(); } return { code: v, generate: m, validate: l, reset: e }; } const $ = /* @__PURE__ */ F({ __name: "vue-client-recaptcha", props: { modelValue: { default: "" }, value: { default: "" }, chars: { default: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" }, charsPreset: { default: "alphanumeric" }, count: { default: 5 }, hideLines: { type: Boolean, default: !1 }, customTextColor: { default: "" }, textColors: { default: () => [] }, width: { type: [Number, Function], default: (i) => i.count * 30 }, height: { default: 50 }, canvasClass: { default: "" }, icon: { default: "refresh" }, captchaFont: { default: "bold 28px sans-serif" }, hideRefreshIcon: { type: Boolean, default: !1 }, radius: { default: 0 }, refreshLabel: { default: "Refresh captcha" }, canvasLabel: { default: "Captcha image" }, theme: { default: "light" }, noiseDots: { default: 0 }, noiseLines: { default: -1 }, distortion: { default: "lines" }, audioEnabled: { type: Boolean, default: !1 }, simpleMode: { type: Boolean, default: !1 } }, emits: ["isValid", "update:valid", "getCode", "update:modelValue", "refresh", "ready", "error"], setup(i, { expose: w, emit: v }) { const m = { alphanumeric: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", numeric: "0123456789", letters: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", custom: "" }; function l() { if (typeof crypto < "u" && crypto.getRandomValues) { const t = new Uint32Array(1); return crypto.getRandomValues(t), t[0] / 4294967296; } return Math.random(); } const e = i, h = I(), r = v, a = P(null), g = W(() => ({ chars: e.chars, charsPreset: e.charsPreset, count: e.count })), s = g.code, c = p( () => typeof e.width == "function" ? e.width(e) : e.width ), u = p(() => e.height); p( () => e.charsPreset === "custom" ? e.chars : m[e.charsPreset] || m.alphanumeric ); const x = p( () => (e.modelValue !== void 0 && e.modelValue !== null ? e.modelValue : e.value) ?? "" ), S = p(() => !!(s.value && s.value === x.value)); U(() => { if (!a.value) { r("error", new Error("Canvas ref not available")); return; } try { y(), r("ready"); } catch (t) { r("error", t); } }), L( [c, u], () => { a.value && y(); }, { flush: "post" } ), L( [ () => e.distortion, () => e.noiseDots, () => e.noiseLines, () => e.hideLines, () => e.customTextColor, () => e.textColors, () => e.captchaFont, () => e.simpleMode, () => e.theme ], () => { a.value && y(); }, { flush: "post" } ); const y = () => { var d; if (!a.value) return; s.value = "", a.value.width = c.value, a.value.height = u.value; const t = a.value.getContext("2d"); if (!t) { r("error", new Error("Failed to get canvas 2d context")); return; } g.generate(); const o = s.value; if (o) { if (e.simpleMode) { const n = e.theme === "dark" || e.theme === "auto" && ((d = window.matchMedia) == null ? void 0 : d.call(window, "(prefers-color-scheme: dark)").matches); t.fillStyle = n ? "#ffffff" : "#000000", t.font = e.captchaFont, t.textAlign = "center", t.fillText(o, c.value / 2, u.value / 2 + 10); } else for (let n = 0; n < o.length; n++) { const b = o[n], M = l() * 30 * Math.PI / 180, R = 10 + n * 25, V = 30 + l() * 8; t.font = e.captchaFont, t.translate(R, V), e.customTextColor ? t.fillStyle = e.customTextColor : e.textColors.length ? t.fillStyle = e.textColors[Math.floor(l() * e.textColors.length)] : t.fillStyle = C(), t.rotate(M), t.fillText(b, 0, 0), t.rotate(-M), t.translate(-R, -V); } if (!e.simpleMode) { const n = !e.hideLines && (e.distortion === "lines" || e.distortion === "both"), b = e.distortion === "dots" || e.distortion === "both"; n && D(), b && e.noiseDots && E(); } if (B(), r("refresh", s.value), e.audioEnabled && typeof window < "u" && "speechSynthesis" in window) { const n = new SpeechSynthesisUtterance(s.value.split("").join(" ")); n.lang = "en-US", n.rate = 0.8, window.speechSynthesis.cancel(), window.speechSynthesis.speak(n); } } }, T = p( () => e.noiseLines >= 0 ? e.noiseLines : e.count ), D = () => { if (!a.value) return; const t = a.value.getContext("2d"); if (!t) return; const o = T.value; for (let d = 0; d < o; d++) t.strokeStyle = C(), t.beginPath(), t.moveTo(l() * c.value, l() * u.value), t.lineTo(l() * c.value, l() * u.value), t.stroke(); }, E = () => { if (!a.value || !e.noiseDots) return; const t = a.value.getContext("2d"); if (!t) return; const o = c.value, d = u.value; for (let n = 0; n < e.noiseDots; n++) t.fillStyle = C(), t.beginPath(), t.arc(l() * o, l() * d, 1, 0, Math.PI * 2), t.fill(); }, B = () => { r("getCode", s.value); }; H(() => { const t = S.value; r("isValid", t), r("update:valid", t); }); const C = () => { const t = Math.floor(l() * 256), o = Math.floor(l() * 256), d = Math.floor(l() * 256); return `rgb(${t},${o},${d})`; }, _ = () => { if (!a.value) return; const t = a.value.getContext("2d"); t && (t.clearRect(0, 0, c.value, u.value), y()); }; w({ resetCaptcha: _ }); const A = () => { const t = e.theme === "auto" ? "vue_client_recaptcha_theme_auto" : e.theme === "dark" ? "vue_client_recaptcha_theme_dark" : ""; return f( "div", { class: ["vue_client_recaptcha", t].filter(Boolean).join(" "), style: { borderRadius: `var(--vcr-radius, ${e.radius}px)`, width: `${c.value + 50}px` } }, [ f( "span", { "aria-live": "polite", "aria-atomic": "true", class: "vue_client_recaptcha_sr_only" }, S.value ? "Captcha verified" : "" ), ...e.hideRefreshIcon ? [] : [ f( "div", { class: "vue_client_recaptcha_icon", role: "button", tabindex: 0, "aria-label": e.refreshLabel, onClick: () => _(), onKeydown: (o) => { (o.key === "Enter" || o.key === " ") && (o.preventDefault(), _()); } }, [ h.icon ? f(h.icon) : f( "svg", { class: "vue_client_recaptcha_icon_svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, [ f("path", { d: "M2 12a9 9 0 0 0 9 9c2.39 0 4.68-.94 6.4-2.6l-1.5-1.5A6.706 6.706 0 0 1 11 19c-6.24 0-9.36-7.54-4.95-11.95C10.46 2.64 18 5.77 18 12h-3l4 4h.1l3.9-4h-3a9 9 0 0 0-18 0Z", fill: "currentColor" }) ] ) ] ) ], f( "canvas", { id: "captcha_canvas", class: `captcha_canvas ${e.canvasClass}`, role: "img", "aria-label": e.canvasLabel, ref: a }, s.value ) ] ); }; return (t, o) => (j(), N(A)); } }); export { $ as VueClientRecaptcha, W as useCaptcha };