补充主题静态资源与归档模板

This commit is contained in:
kate522 committed 2026-07-03 23:15:11 +08:00
1 parent 1268a55dd0
commit 3247db7ed1
265 files changed
+59174

No files matched your search

+481
View File
@@ -0,0 +1,481 @@
/**
* AKSRT Weixin Share
*
* Bridge-first WeChat share helper with QR fallback.
*
* @package AKSRT
*/
(function () {
'use strict';
/**
* Local QR code data URL generator — no third-party services.
* Uses canvas rendering with a minimal QR matrix encoder.
*/
function buildQRCodeDataUrl(text, size) {
size = size || 160;
try {
var matrix = generateQRMatrix(text || ' ');
if (!matrix) return '';
var canvas = document.createElement('canvas');
var moduleCount = matrix.length;
var scale = Math.floor(size / (moduleCount + 8));
var margin = Math.floor((size - scale * moduleCount) / 2);
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = '#000000';
for (var r = 0; r < moduleCount; r++) {
for (var c = 0; c < moduleCount; c++) {
if (matrix[r][c]) {
ctx.fillRect(margin + c * scale, margin + r * scale, scale, scale);
}
}
}
return canvas.toDataURL('image/png');
} catch (e) {
return '';
}
}
/**
* Minimal QR Code matrix generator (Version 1-6, Byte mode, Error Correction L).
* Returns a 2D boolean array or null on failure.
*/
function generateQRMatrix(text) {
var data = encodeUTF8(text);
// Choose version based on data length (EC level L, Byte mode)
var version = 1;
var capacities = [0, 17, 32, 53, 78, 106, 134];
for (var v = 1; v <= 6; v++) {
if (data.length <= capacities[v]) { version = v; break; }
if (v === 6) return null; // data too long for small versions
}
var moduleCount = version * 4 + 17;
var matrix = createMatrix(moduleCount);
var reserved = createMatrix(moduleCount);
// Place finder patterns
placeFinder(matrix, reserved, 0, 0);
placeFinder(matrix, reserved, moduleCount - 7, 0);
placeFinder(matrix, reserved, 0, moduleCount - 7);
// Place alignment patterns (version >= 2)
if (version >= 2) {
var alignPos = getAlignmentPositions(version);
for (var i = 0; i < alignPos.length; i++) {
for (var j = 0; j < alignPos.length; j++) {
if (isInFinder(alignPos[i], alignPos[j], moduleCount)) continue;
placeAlignment(matrix, reserved, alignPos[i], alignPos[j]);
}
}
}
// Place timing patterns
for (var i = 8; i < moduleCount - 8; i++) {
if (!reserved[6][i]) { matrix[6][i] = (i % 2 === 0); reserved[6][i] = true; }
if (!reserved[i][6]) { matrix[i][6] = (i % 2 === 0); reserved[i][6] = true; }
}
// Reserve format info areas
for (var i = 0; i < 8; i++) {
reserved[8][i] = true; reserved[i][8] = true;
reserved[8][moduleCount - 1 - i] = true; reserved[moduleCount - 1 - i][8] = true;
}
reserved[8][8] = true;
if (version >= 2) {
for (var i = moduleCount - 11; i < moduleCount - 8; i++) {
for (var j = 0; j < 6; j++) { reserved[j][i] = true; reserved[i][j] = true; }
}
}
// Dark module
matrix[moduleCount - 8][8] = true;
reserved[moduleCount - 8][8] = true;
// Encode data
var dataBits = encodeData(data, version);
placeData(matrix, reserved, dataBits, moduleCount);
// Apply best mask
var bestMask = 0;
var bestPenalty = Infinity;
for (var m = 0; m < 8; m++) {
var masked = applyMask(matrix, reserved, m, moduleCount);
var penalty = calcPenalty(masked, moduleCount);
if (penalty < bestPenalty) { bestPenalty = penalty; bestMask = m; }
}
matrix = applyMask(matrix, reserved, bestMask, moduleCount);
// Place format info
placeFormatInfo(matrix, bestMask, moduleCount);
return matrix;
}
function createMatrix(n) {
var m = [];
for (var i = 0; i < n; i++) { m[i] = []; for (var j = 0; j < n; j++) m[i][j] = false; }
return m;
}
function encodeUTF8(str) {
var bytes = [];
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 0x80) bytes.push(c);
else if (c < 0x800) { bytes.push(0xc0 | (c >> 6)); bytes.push(0x80 | (c & 0x3f)); }
else { bytes.push(0xe0 | (c >> 12)); bytes.push(0x80 | ((c >> 6) & 0x3f)); bytes.push(0x80 | (c & 0x3f)); }
}
return bytes;
}
function placeFinder(m, r, row, col) {
for (var dr = -1; dr <= 7; dr++) {
for (var dc = -1; dc <= 7; dc++) {
var rr = row + dr, cc = col + dc;
if (rr < 0 || rr >= m.length || cc < 0 || cc >= m.length) continue;
var val = (dr >= 0 && dr <= 6 && (dc === 0 || dc === 6)) || (dc >= 0 && dc <= 6 && (dr === 0 || dr === 6)) || (dr >= 2 && dr <= 4 && dc >= 2 && dc <= 4);
m[rr][cc] = val;
r[rr][cc] = true;
}
}
}
function placeAlignment(m, r, row, col) {
for (var dr = -2; dr <= 2; dr++) {
for (var dc = -2; dc <= 2; dc++) {
var val = Math.abs(dr) === 2 || Math.abs(dc) === 2 || (dr === 0 && dc === 0);
m[row + dr][col + dc] = val;
r[row + dr][col + dc] = true;
}
}
}
function getAlignmentPositions(v) {
if (v <= 1) return [];
if (v === 2) return [6, 18];
if (v === 3) return [6, 22];
if (v === 4) return [6, 26];
if (v === 5) return [6, 30];
return [6, 34];
}
function isInFinder(row, col, n) {
return (row < 9 && col < 9) || (row < 9 && col >= n - 8) || (row >= n - 8 && col < 9);
}
function encodeData(data, version) {
var bits = [];
// Mode indicator: Byte = 0100
bits.push(0, 1, 0, 0);
// Character count
var ccBits = version <= 9 ? 8 : 16;
for (var i = ccBits - 1; i >= 0; i--) bits.push((data.length >> i) & 1);
// Data
for (var i = 0; i < data.length; i++) {
for (var j = 7; j >= 0; j--) bits.push((data[i] >> j) & 1);
}
// Terminator
var capacities = [0, 152, 272, 440, 640, 864, 1088];
var cap = capacities[version] || 152;
for (var i = 0; i < 4 && bits.length < cap; i++) bits.push(0);
// Pad to byte boundary
while (bits.length % 8 !== 0) bits.push(0);
// Pad bytes
var padBytes = [0xEC, 0x11];
var pi = 0;
while (bits.length < cap) {
var pb = padBytes[pi % 2];
for (var j = 7; j >= 0; j--) bits.push((pb >> j) & 1);
pi++;
}
return bits;
}
function placeData(matrix, reserved, bits, n) {
var bitIndex = 0;
for (var right = n - 1; right >= 1; right -= 2) {
if (right === 6) right = 5;
for (var vert = 0; vert < n; vert++) {
for (var j = 0; j < 2; j++) {
var col = right - j;
var upward = ((right + 1) & 2) === 0;
var row = upward ? n - 1 - vert : vert;
if (!reserved[row][col] && bitIndex < bits.length) {
matrix[row][col] = bits[bitIndex] === 1;
bitIndex++;
}
}
}
}
}
function applyMask(matrix, reserved, maskNum, n) {
var result = createMatrix(n);
for (var r = 0; r < n; r++) {
for (var c = 0; c < n; c++) {
result[r][c] = matrix[r][c];
if (!reserved[r][c]) {
var invert = false;
switch (maskNum) {
case 0: invert = (r + c) % 2 === 0; break;
case 1: invert = r % 2 === 0; break;
case 2: invert = c % 3 === 0; break;
case 3: invert = (r + c) % 3 === 0; break;
case 4: invert = (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0; break;
case 5: invert = (r * c) % 2 + (r * c) % 3 === 0; break;
case 6: invert = ((r * c) % 2 + (r * c) % 3) % 2 === 0; break;
case 7: invert = ((r + c) % 2 + (r * c) % 3) % 2 === 0; break;
}
if (invert) result[r][c] = !result[r][c];
}
}
}
return result;
}
function calcPenalty(matrix, n) {
var penalty = 0;
// Rule 1: consecutive same-color modules in row/column
for (var r = 0; r < n; r++) {
var run = 1;
for (var c = 1; c < n; c++) {
if (matrix[r][c] === matrix[r][c - 1]) { run++; } else { run = 1; }
if (run === 5) penalty += 3;
else if (run > 5) penalty += 1;
}
}
for (var c = 0; c < n; c++) {
var run = 1;
for (var r = 1; r < n; r++) {
if (matrix[r][c] === matrix[r - 1][c]) { run++; } else { run = 1; }
if (run === 5) penalty += 3;
else if (run > 5) penalty += 1;
}
}
return penalty;
}
function placeFormatInfo(matrix, maskNum, n) {
var formatInfo = getFormatInfo(maskNum);
// Place around top-left finder
var positions = [
[8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [8, 5], [8, 7], [8, 8],
[7, 8], [5, 8], [4, 8], [3, 8], [2, 8], [1, 8], [0, 8]
];
for (var i = 0; i < 15; i++) {
var r = positions[i][0], c = positions[i][1];
matrix[r][c] = formatInfo[i] === 1;
}
// Place around bottom-left and top-right finders
var positions2 = [
[n - 1, 8], [n - 2, 8], [n - 3, 8], [n - 4, 8], [n - 5, 8], [n - 6, 8], [n - 7, 8],
[8, n - 8], [8, n - 7], [8, n - 6], [8, n - 5], [8, n - 4], [8, n - 3], [8, n - 2], [8, n - 1]
];
for (var i = 0; i < 15; i++) {
var r = positions2[i][0], c = positions2[i][1];
matrix[r][c] = formatInfo[i] === 1;
}
}
function getFormatInfo(maskNum) {
// EC level L = 01, mask number = maskNum
var data = (0x01 << 3) | maskNum; // 5 bits
var bits = data << 10;
// BCH(15,5) error correction
var gen = 0x537; // generator polynomial
for (var i = 14; i >= 10; i--) {
if (bits & (1 << i)) bits ^= gen << (i - 10);
}
var formatBits = (data << 10) | bits;
// XOR with mask pattern
formatBits ^= 0x5412;
var result = [];
for (var i = 14; i >= 0; i--) result.push((formatBits >> i) & 1);
return result;
}
function isWeixinEnv() {
return /micromessenger/i.test(window.navigator.userAgent || '') || !!window.WeixinJSBridge;
}
function normalizePayload(payload) {
payload = payload || {};
return {
title: payload.title || document.title,
url: payload.url || window.location.href,
desc: payload.desc || '',
image: payload.image || ''
};
}
function hideQrFallback(qrBox) {
if (!qrBox) return;
qrBox.classList.remove('is-visible');
qrBox.setAttribute('aria-hidden', 'true');
}
function showQrFallback(payload) {
var qrBox = payload.qrBox;
var qrImage = payload.qrImage;
if (!qrBox || !qrImage || !payload.url) {
return Promise.resolve({
mode: 'error',
message: '当前页面缺少微信二维码容器'
});
}
if (qrImage.dataset.qrValue !== payload.url || !qrImage.getAttribute('src')) {
qrImage.dataset.qrValue = payload.url;
var dataUrl = buildQRCodeDataUrl(payload.url, 160);
if (dataUrl) {
qrImage.src = dataUrl;
} else {
qrImage.alt = '二维码生成失败,请直接复制链接分享';
}
}
qrBox.classList.add('is-visible');
qrBox.setAttribute('aria-hidden', 'false');
return Promise.resolve({
mode: 'qr',
message: '请使用微信扫一扫分享'
});
}
function bridgeWithWxSdk(payload) {
return new Promise(function (resolve, reject) {
if (!window.wx || typeof window.wx.ready !== 'function') {
reject(new Error('wx sdk unavailable'));
return;
}
var done = false;
var timer = window.setTimeout(function () {
if (done) return;
done = true;
reject(new Error('微信分享桥接未就绪'));
}, 1200);
function finishSuccess() {
if (done) return;
done = true;
window.clearTimeout(timer);
resolve({
mode: 'bridge',
message: '已同步微信分享信息,请使用右上角菜单继续分享'
});
}
function finishError(error) {
if (done) return;
done = true;
window.clearTimeout(timer);
reject(error instanceof Error ? error : new Error('微信分享桥接失败'));
}
try {
if (typeof window.wx.error === 'function') {
window.wx.error(function (error) {
finishError(new Error((error && error.errMsg) || '微信签名校验失败'));
});
}
window.wx.ready(function () {
try {
if (typeof window.wx.updateAppMessageShareData === 'function') {
window.wx.updateAppMessageShareData({
title: payload.title,
desc: payload.desc,
link: payload.url,
imgUrl: payload.image
});
}
if (typeof window.wx.updateTimelineShareData === 'function') {
window.wx.updateTimelineShareData({
title: payload.title,
link: payload.url,
imgUrl: payload.image
});
}
finishSuccess();
} catch (error) {
finishError(error);
}
});
} catch (error) {
finishError(error);
}
});
}
function bridgeWithWeixinJsBridge(payload) {
return new Promise(function (resolve, reject) {
var bridge = window.WeixinJSBridge;
if (!bridge || typeof bridge.invoke !== 'function') {
reject(new Error('WeixinJSBridge unavailable'));
return;
}
try {
bridge.invoke('sendAppMessage', {
title: payload.title,
desc: payload.desc,
link: payload.url,
img_url: payload.image
}, function (response) {
var errMsg = response && response.err_msg ? response.err_msg : '';
if (!errMsg || /:ok$/i.test(errMsg)) {
resolve({
mode: 'bridge',
message: '已调起微信分享面板'
});
return;
}
reject(new Error(errMsg));
});
} catch (error) {
reject(error);
}
});
}
function openWeixinShare(payload) {
var data = normalizePayload(payload);
data.qrBox = payload && payload.qrBox ? payload.qrBox : null;
data.qrImage = payload && payload.qrImage ? payload.qrImage : null;
if (!isWeixinEnv()) {
return showQrFallback(data);
}
return bridgeWithWxSdk(data)
.catch(function () {
return bridgeWithWeixinJsBridge(data);
})
.catch(function (error) {
return showQrFallback(data).then(function (result) {
result.message = (error && error.message ? error.message + ',' : '') + '已切换为二维码分享';
return result;
});
});
}
window.AksrtShare = Object.assign({}, window.AksrtShare || {}, {
isWeixinEnv: isWeixinEnv,
hideQrFallback: hideQrFallback,
showQrFallback: showQrFallback,
openWeixinShare: openWeixinShare,
// Exposed so the unified share-data-builder can render poster QR codes
// without a second encoder.
buildQrDataUrl: buildQRCodeDataUrl
});
})();