{"spec_id":"qrcode-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// qrcode-basic: Basic QR Code Generator\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 87/100 | Created: 2026-06-24\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// ── GF(256) arithmetic for Reed-Solomon (primitive poly 0x11D) ───────────────\nconst GF_EXP = new Uint8Array(512);\nconst GF_LOG = new Uint8Array(256);\n(function () {\n  let x = 1;\n  for (let i = 0; i < 255; i++) {\n    GF_EXP[i] = x;\n    GF_LOG[x] = i;\n    x = x < 128 ? x * 2 : ((x * 2) ^ 0x11d) & 0xff;\n  }\n  for (let i = 255; i < 512; i++) GF_EXP[i] = GF_EXP[i - 255];\n}());\n\nfunction gfMul(a, b) {\n  return a && b ? GF_EXP[GF_LOG[a] + GF_LOG[b]] : 0;\n}\n\n// Compute nEC Reed-Solomon error-correction codewords\nfunction rsEncode(data, nEC) {\n  let g = [1];\n  for (let i = 0; i < nEC; i++) {\n    const ng = new Array(g.length + 1).fill(0);\n    for (let j = 0; j < g.length; j++) {\n      ng[j] ^= g[j];\n      ng[j + 1] ^= gfMul(g[j], GF_EXP[i]);\n    }\n    g = ng;\n  }\n  const rem = [...data, ...new Array(nEC).fill(0)];\n  for (let i = 0; i < data.length; i++) {\n    const c = rem[i];\n    if (c === 0) continue;\n    for (let j = 1; j <= nEC; j++) rem[i + j] ^= gfMul(c, g[j]);\n  }\n  return rem.slice(data.length);\n}\n\n// Byte mode, Version 2 M: 22 data codewords, 16 EC codewords\nfunction encodeData(text) {\n  const bytes = Array.from(new TextEncoder().encode(text));\n  const bits = [];\n  const push = (v, n) => { for (let i = n - 1; i >= 0; i--) bits.push((v >> i) & 1); };\n  push(4, 4);            // Byte mode indicator\n  push(bytes.length, 8); // Character count\n  for (const b of bytes) push(b, 8);\n  push(0, 4);            // Terminator\n  while (bits.length % 8) bits.push(0);\n  const cw = [];\n  for (let i = 0; i < bits.length; i += 8)\n    cw.push(bits.slice(i, i + 8).reduce((acc, b, j) => acc | (b << (7 - j)), 0));\n  for (let p = 0; cw.length < 22; p++) cw.push(p % 2 === 0 ? 0xec : 0x11);\n  return cw;\n}\n\n// BCH(15,5) format info, EC level M (00), XOR mask 0x5412\nfunction formatInfo(maskIdx) {\n  let rem = maskIdx << 10;\n  for (let i = 14; i >= 10; i--)\n    if ((rem >> i) & 1) rem ^= 0x537 << (i - 10);\n  return ((maskIdx << 10) | (rem & 0x3ff)) ^ 0x5412;\n}\n\n// Build 25×25 QR matrix (ISO 18004 Version 2)\nfunction buildMatrix(text, maskIdx) {\n  const N = 25;\n  const mat = Array.from({ length: N }, () => new Int8Array(N).fill(-1));\n  const set = (r, c, v) => { mat[r][c] = v; };\n\n  function placeFinder(r0, c0) {\n    for (let r = 0; r < 7; r++)\n      for (let c = 0; c < 7; c++) {\n        const v = (r === 0 || r === 6 || c === 0 || c === 6 ||\n                   (r >= 2 && r <= 4 && c >= 2 && c <= 4)) ? 1 : 0;\n        set(r0 + r, c0 + c, v);\n      }\n  }\n  placeFinder(0, 0);\n  placeFinder(0, N - 7);\n  placeFinder(N - 7, 0);\n\n  for (let i = 0; i < 8; i++) {\n    if (mat[7][i] < 0) set(7, i, 0);\n    if (mat[i][7] < 0) set(i, 7, 0);\n    if (mat[7][N - 1 - i] < 0) set(7, N - 1 - i, 0);\n    if (mat[i][N - 8] < 0) set(i, N - 8, 0);\n    if (mat[N - 8][i] < 0) set(N - 8, i, 0);\n    if (mat[N - 1 - i][7] < 0) set(N - 1 - i, 7, 0);\n  }\n\n  for (let i = 8; i <= N - 9; i++) {\n    const v = i % 2 === 0 ? 1 : 0;\n    if (mat[6][i] < 0) set(6, i, v);\n    if (mat[i][6] < 0) set(i, 6, v);\n  }\n\n  for (let dr = -2; dr <= 2; dr++)\n    for (let dc = -2; dc <= 2; dc++)\n      if (mat[18 + dr][18 + dc] < 0) {\n        const v = (Math.abs(dr) === 2 || Math.abs(dc) === 2 ||\n                   (dr === 0 && dc === 0)) ? 1 : 0;\n        set(18 + dr, 18 + dc, v);\n      }\n\n  set(4 * 2 + 9, 8, 1); // dark module\n\n  for (let i = 0; i <= 8; i++) {\n    if (mat[8][i] < 0) set(8, i, 0);\n    if (mat[i][8] < 0) set(i, 8, 0);\n  }\n  for (let i = 0; i < 8; i++) {\n    if (mat[8][N - 1 - i] < 0) set(8, N - 1 - i, 0);\n  }\n  for (let i = 0; i < 7; i++) {\n    if (mat[N - 1 - i][8] < 0) set(N - 1 - i, 8, 0);\n  }\n\n  const dataCW = encodeData(text);\n  const ecCW = rsEncode(dataCW, 16);\n  const allBits = [];\n  for (const cw of [...dataCW, ...ecCW])\n    for (let i = 7; i >= 0; i--) allBits.push((cw >> i) & 1);\n\n  const MASKS = [\n    (r, c) => (r + c) % 2 === 0,\n    (r, c) => r % 2 === 0,\n    (r, c) => c % 3 === 0,\n    (r, c) => (r + c) % 3 === 0,\n    (r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,\n    (r, c) => (r * c) % 2 + (r * c) % 3 === 0,\n    (r, c) => ((r * c) % 2 + (r * c) % 3) % 2 === 0,\n    (r, c) => ((r + c) % 2 + (r * c) % 3) % 2 === 0,\n  ];\n  const maskFn = MASKS[maskIdx];\n  let bitIdx = 0;\n  let upward = true;\n  for (let col = N - 1; col > 0; col -= 2) {\n    if (col === 6) col--;\n    const rows = upward\n      ? Array.from({ length: N }, (_, i) => N - 1 - i)\n      : Array.from({ length: N }, (_, i) => i);\n    for (const r of rows) {\n      for (const dc of [0, -1]) {\n        const c = col + dc;\n        if (c < 0 || mat[r][c] !== -1) continue;\n        const bit = bitIdx < allBits.length ? allBits[bitIdx++] : 0;\n        set(r, c, maskFn(r, c) ? bit ^ 1 : bit);\n      }\n    }\n    upward = !upward;\n  }\n\n  const fmt = formatInfo(maskIdx);\n  const copy1 = [\n    [8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [8, 5], [8, 7], [8, 8],\n    [7, 8], [5, 8], [4, 8], [3, 8], [2, 8], [1, 8], [0, 8],\n  ];\n  for (let i = 0; i < 15; i++) set(copy1[i][0], copy1[i][1], (fmt >> (14 - i)) & 1);\n  const copy2 = [\n    [8, N - 1], [8, N - 2], [8, N - 3], [8, N - 4],\n    [8, N - 5], [8, N - 6], [8, N - 7], [8, N - 8],\n    [N - 7, 8], [N - 6, 8], [N - 5, 8], [N - 4, 8],\n    [N - 3, 8], [N - 2, 8], [N - 1, 8],\n  ];\n  for (let i = 0; i < 15; i++) set(copy2[i][0], copy2[i][1], (fmt >> i) & 1);\n\n  return mat;\n}\n\n// ── Build QR matrix ───────────────────────────────────────────────────────────\nconst QR_TEXT = \"https://anyplot.ai\";\nconst QR_N = 25;\nconst MASK_IDX = 0; // mask pattern 0: (row+col) % 2 === 0\nconst matrix = buildMatrix(QR_TEXT, MASK_IDX);\n\n// ── Chart.js plugin: renders QR code inside the chart canvas ─────────────────\nconst qrPlugin = {\n  id: 'qrcode',\n  beforeDraw(chart) {\n    // Fill page background before Chart.js draws anything\n    chart.ctx.fillStyle = t.pageBg;\n    chart.ctx.fillRect(0, 0, chart.width, chart.height);\n  },\n  afterDraw(chart) {\n    const ctx = chart.ctx;\n    const ca = chart.chartArea;\n    const caW = ca.right - ca.left;\n    const caH = ca.bottom - ca.top;\n\n    const QUIET = 4;\n    const totalModules = QR_N + QUIET * 2; // 33 modules (incl. quiet zone)\n    const PAD = 16;\n    const LABEL_H = 72; // vertical space for URL + version note below QR\n\n    const availForQR = Math.min(caW - PAD * 2, caH - LABEL_H - PAD);\n    const cellSize = Math.floor(availForQR / totalModules);\n    const qrPx = totalModules * cellSize;\n    const qrLeft = ca.left + Math.round((caW - qrPx) / 2);\n    const qrTop = ca.top + PAD;\n\n    // White card required for QR scannability\n    ctx.fillStyle = \"#FFFFFF\";\n    ctx.fillRect(qrLeft, qrTop, qrPx, qrPx);\n\n    // QR modules: black on white\n    ctx.fillStyle = \"#000000\";\n    for (let r = 0; r < QR_N; r++) {\n      for (let c = 0; c < QR_N; c++) {\n        if (matrix[r][c] === 1) {\n          ctx.fillRect(\n            qrLeft + (QUIET + c) * cellSize,\n            qrTop + (QUIET + r) * cellSize,\n            cellSize,\n            cellSize\n          );\n        }\n      }\n    }\n\n    // Brand accent: thin #009E73 border framing the QR card\n    const bp = 5;\n    ctx.strokeStyle = t.palette[0]; // #009E73\n    ctx.lineWidth = 3;\n    ctx.strokeRect(qrLeft - bp + 0.5, qrTop - bp + 0.5, qrPx + bp * 2 - 1, qrPx + bp * 2 - 1);\n\n    // URL label below QR\n    const centerX = ca.left + caW / 2;\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    ctx.fillStyle = t.inkSoft;\n    ctx.font = `16px \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace`;\n    ctx.fillText(QR_TEXT, centerX, qrTop + qrPx + 30);\n\n    // Version + EC level note\n    ctx.font = `14px -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif`;\n    ctx.fillText(\"Version 2 · Error Correction M (15%)\", centerX, qrTop + qrPx + 56);\n  }\n};\n\n// ── Mount canvas and instantiate Chart.js ─────────────────────────────────────\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\nnew Chart(canvas, {\n  type: 'scatter',\n  data: { datasets: [] },\n  plugins: [qrPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: {\n      padding: { bottom: 20 }\n    },\n    plugins: {\n      title: {\n        display: true,\n        text: \"qrcode-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22, weight: '600' },\n        padding: { top: 16, bottom: 12 }\n      },\n      legend: { display: false },\n    },\n    scales: {\n      x: { display: false },\n      y: { display: false },\n    },\n  },\n});\n"}