{"spec_id":"datamatrix-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// datamatrix-basic: Basic Data Matrix 2D Barcode\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\n\n//# anyplot-orientation: square\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data --------------------------------------------------------------\n// Chart.js has no core \"matrix\" chart type — chartjs-chart-matrix is a\n// community plugin and is not installed. The module grid below is drawn by\n// a custom plugin instead, so the data model here is the ISO/IEC 16022\n// module layout itself: a solid L-shaped finder pattern (left column +\n// bottom row), an alternating clock/timing track (top row + right column),\n// and interior \"data\" modules derived deterministically from the ASCII\n// bits of the encoded content string.\nconst content = \"SERIAL:12345678\";\nconst gridSize = 16;\n\nconst bits = [];\nfor (const char of content) {\n  const code = char.charCodeAt(0);\n  for (let b = 7; b >= 0; b--) bits.push((code >> b) & 1);\n}\n\nconst modules = [];\nfor (let row = 0; row < gridSize; row++) {\n  const line = [];\n  for (let col = 0; col < gridSize; col++) {\n    let dark;\n    if (col === 0 || row === gridSize - 1) {\n      dark = true; // solid L-shaped finder pattern\n    } else if (row === 0) {\n      dark = col % 2 === 0; // top timing/clock track\n    } else if (col === gridSize - 1) {\n      dark = row % 2 === 0; // right timing/clock track\n    } else {\n      const bitIndex = (row - 1) * (gridSize - 2) + (col - 1);\n      dark = bits[bitIndex % bits.length] === 1;\n    }\n    line.push(dark);\n  }\n  modules.push(line);\n}\n\n// --- Mount -----------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart -----------------------------------------------------------------\n// An empty scatter chart provides the theme-aware title/canvas scaffolding;\n// a custom plugin draws the module grid directly onto the chart area so the\n// modules stay perfectly square regardless of the title's reserved height.\n// The card + modules stay fixed black-on-white in BOTH themes (per the spec's\n// \"high contrast black on white\" note and the qrcode-basic precedent) — only\n// the surrounding page background/title/subtitle/caption follow theme tokens.\nconst QUIET = 2; // module-widths of quiet zone around the grid\nconst CAPTION_H = 34; // space reserved below the card for the caption line\nconst moduleGridPlugin = {\n  id: \"datamatrixGrid\",\n  afterDraw(chart) {\n    const { ctx, chartArea } = chart;\n    const areaWidth = chartArea.right - chartArea.left;\n    const areaHeight = chartArea.bottom - chartArea.top;\n    const totalModules = gridSize + QUIET * 2;\n    const side = Math.min(areaWidth, areaHeight - CAPTION_H) * 0.86;\n    const cell = side / totalModules;\n    const cardX = chartArea.left + (areaWidth - side) / 2;\n    const cardY = chartArea.top + (areaHeight - CAPTION_H - side) / 2;\n    const gridX = cardX + QUIET * cell;\n    const gridY = cardY + QUIET * cell;\n\n    ctx.save();\n\n    // Fixed white card behind the grid (includes the quiet zone).\n    ctx.fillStyle = \"#FFFFFF\";\n    ctx.fillRect(cardX, cardY, side, side);\n\n    // Fixed black modules, identical in both themes.\n    ctx.fillStyle = \"#000000\";\n    for (let row = 0; row < gridSize; row++) {\n      for (let col = 0; col < gridSize; col++) {\n        if (modules[row][col]) {\n          ctx.fillRect(gridX + col * cell, gridY + row * cell, cell + 0.6, cell + 0.6);\n        }\n      }\n    }\n\n    // Brand accent: thin #009E73 border framing the card (theme-adaptive palette token).\n    ctx.strokeStyle = t.palette[0];\n    ctx.lineWidth = 3;\n    ctx.strokeRect(cardX + 1.5, cardY + 1.5, side - 3, side - 3);\n\n    // Caption below the card, theme-adaptive.\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    ctx.fillStyle = t.inkSoft;\n    ctx.font = `14px -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif`;\n    ctx.fillText(\n      `${gridSize}×${gridSize} modules · ECC 200 (stylized)`,\n      cardX + side / 2,\n      cardY + side + CAPTION_H / 2\n    );\n\n    ctx.restore();\n  },\n};\n\nnew Chart(canvas, {\n  type: \"scatter\",\n  data: { datasets: [] },\n  plugins: [moduleGridPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: 40 },\n    scales: {\n      x: { display: false },\n      y: { display: false },\n    },\n    plugins: {\n      legend: { display: false },\n      title: {\n        display: true,\n        text: \"datamatrix-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n        padding: { top: 10, bottom: 4 },\n      },\n      subtitle: {\n        display: true,\n        text: `Encodes \"${content}\"`,\n        color: t.inkSoft,\n        font: { size: 16, style: \"normal\" },\n        padding: { bottom: 20 },\n      },\n    },\n  },\n});\n"}