403Webshell
Server IP : 167.235.67.158  /  Your IP : 216.73.216.95
Web Server : Apache
System : Linux ubuntu-8gb-nbg1-1 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11 23:16:02 UTC 2026 x86_64
User : upstairsbar.co.uk ( 982)
PHP Version : 8.3.6
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/bc.the-word.com/oliver/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/bc.the-word.com/oliver/pascals-triangle.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Pascal's Triangle</title>
<style>
  :root {
    --red: #e02323;
    --orange: #f5a623;
    --bg: #fafafa;
    --panel: rgba(255, 255, 255, 0.92);
    --border: #1a1a1a;
    --text: #1a1a1a;
  }

  * { box-sizing: border-box; }

  html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    overflow: hidden;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    background: var(--bg);
    color: var(--text);
    -webkit-tap-highlight-color: transparent;
  }

  #app { position: fixed; inset: 0; }

  canvas {
    display: block;
    width: 100%;
    height: 100%;
    touch-action: none;
    cursor: grab;
  }
  canvas:active { cursor: grabbing; }

  #counter {
    position: fixed;
    top: 16px;
    right: 16px;
    background: var(--panel);
    backdrop-filter: blur(6px);
    border: 2px solid var(--border);
    border-radius: 10px;
    padding: 8px 14px;
    font-size: 16px;
    font-weight: 700;
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
    user-select: none;
  }

  #controls {
    position: fixed;
    left: 16px;
    bottom: 16px;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
  }

  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 2px solid var(--border);
    background: var(--panel);
    backdrop-filter: blur(6px);
    color: var(--text);
    border-radius: 10px;
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
    user-select: none;
    transition: transform 0.05s ease, background 0.15s ease;
  }
  .btn:hover { background: #fff; }
  .btn:active { transform: translateY(1px) scale(0.98); }

  #addLayer, #removeLayer, #save { padding: 10px 18px; }
  #save.saved { background: #2ea843; color: #fff; border-color: #1f7a31; }

  .icon-btn { width: 46px; height: 46px; padding: 0; font-size: 0; }
  .icon-btn svg { width: 22px; height: 22px; }

  #zoom {
    position: fixed;
    right: 16px;
    bottom: 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }

  /* Colour picker popup */
  #palette {
    position: fixed;
    display: none;
    z-index: 50;
    background: var(--panel);
    backdrop-filter: blur(8px);
    border: 2px solid var(--border);
    border-radius: 12px;
    padding: 10px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.22);
    width: 188px;
  }
  #palette .title {
    font-size: 12px;
    font-weight: 700;
    margin: 0 0 8px;
    color: #333;
    user-select: none;
  }
  #palette .swatches {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 6px;
  }
  #palette .chip {
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 6px;
    border: 2px solid rgba(0,0,0,0.25);
    cursor: pointer;
    transition: transform 0.05s ease;
  }
  #palette .chip:hover { transform: scale(1.1); }
  #palette .chip.reset {
    background:
      linear-gradient(45deg, #fff 45%, #e02323 45%, #e02323 55%, #fff 55%);
    position: relative;
  }
  #palette .reset-row {
    margin-top: 8px;
    display: flex;
    gap: 6px;
  }
  #palette .reset-btn {
    flex: 1;
    border: 2px solid var(--border);
    background: #fff;
    border-radius: 8px;
    padding: 6px;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
  }
  #palette .reset-btn:hover { background: #f3f3f3; }
</style>
</head>
<body>
<div id="app">
  <canvas id="pyramid"></canvas>

  <div id="counter">Layers: <span id="layerCount">1</span></div>

  <div id="controls">
    <button id="addLayer" class="btn">+ Add layer</button>
    <button id="removeLayer" class="btn">- Remove layer</button>
    <button id="save" class="btn">Save</button>
  </div>

  <div id="zoom">
    <button id="zoomIn" class="btn icon-btn" aria-label="Zoom in" title="Zoom in">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="10.5" cy="10.5" r="6.5"></circle>
        <line x1="15.5" y1="15.5" x2="21" y2="21"></line>
        <line x1="10.5" y1="7.5" x2="10.5" y2="13.5"></line>
        <line x1="7.5" y1="10.5" x2="13.5" y2="10.5"></line>
      </svg>
    </button>
    <button id="zoomOut" class="btn icon-btn" aria-label="Zoom out" title="Zoom out">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="10.5" cy="10.5" r="6.5"></circle>
        <line x1="15.5" y1="15.5" x2="21" y2="21"></line>
        <line x1="7.5" y1="10.5" x2="13.5" y2="10.5"></line>
      </svg>
    </button>
    <button id="fit" class="btn icon-btn" aria-label="Fit to view" title="Fit to view">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M4 9V5a1 1 0 0 1 1-1h4"></path>
        <path d="M20 9V5a1 1 0 0 0-1-1h-4"></path>
        <path d="M4 15v4a1 1 0 0 0 1 1h4"></path>
        <path d="M20 15v4a1 1 0 0 1-1 1h-4"></path>
      </svg>
    </button>
  </div>

  <div id="palette">
    <p class="title">Pick a colour</p>
    <div class="swatches" id="swatches"></div>
    <div class="reset-row">
      <button class="reset-btn" id="resetCell">Reset to default</button>
    </div>
  </div>
</div>

<script>
(() => {
  "use strict";

  const STORAGE_KEY = "pascals-triangle-state-v1";

  // ---- Geometry constants (world units) ----
  const CELL = 64;
  const FIT_PADDING = 80;
  const MIN_SCALE = 0.0005;
  const MAX_SCALE = 6;
  const TAP_MOVE_THRESHOLD = 6; // px

  // ---- State ----
  let layers = 1;                 // number of rows (row n = 0 .. layers-1)
  let scale = 1;
  let tx = 0;
  let ty = 0;
  let customColors = {};          // "n,k" -> hex string

  const canvas = document.getElementById("pyramid");
  const ctx = canvas.getContext("2d");
  let dpr = Math.max(1, window.devicePixelRatio || 1);
  let viewW = 0, viewH = 0;

  // ---- Pascal's triangle values (BigInt), cached by row ----
  const triRows = [[1n]]; // triRows[n] = row n (n+1 entries)
  function ensureRows(maxN) {
    for (let n = triRows.length; n <= maxN; n++) {
      const prev = triRows[n - 1];
      const row = new Array(n + 1);
      row[0] = 1n;
      row[n] = 1n;
      for (let k = 1; k < n; k++) row[k] = prev[k - 1] + prev[k];
      triRows[n] = row;
    }
  }
  const strCache = new Map(); // "n,k" -> decimal string
  function valueStr(n, k) {
    const key = n + "," + k;
    let s = strCache.get(key);
    if (s === undefined) {
      ensureRows(n);
      s = triRows[n][k].toString();
      strCache.set(key, s);
    }
    return s;
  }

  // Default colour: plain white background
  function defaultColorFor() {
    return "#ffffff";
  }

  // Choose readable text colour for a given background hex
  function textColorFor(hex) {
    const h = hex.replace("#", "");
    const r = parseInt(h.slice(0, 2), 16);
    const g = parseInt(h.slice(2, 4), 16);
    const b = parseInt(h.slice(4, 6), 16);
    const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
    return lum > 0.6 ? "#1a1a1a" : "#ffffff";
  }

  // ---- Layout ----
  // Row n (0-indexed) has n+1 cells, centered on x = 0.
  function pyramidBounds() {
    const w = layers * CELL;       // bottom row (n = layers-1) has `layers` cells
    const h = layers * CELL;
    return { minX: -w / 2, maxX: w / 2, minY: 0, maxY: h, w, h };
  }
  function rowLeft(n) {
    const count = n + 1;
    return -count * CELL / 2;
  }

  // ---- View transforms ----
  function worldToScreen(wx, wy) { return { x: wx * scale + tx, y: wy * scale + ty }; }
  function screenToWorld(sx, sy) { return { x: (sx - tx) / scale, y: (sy - ty) / scale }; }
  function clampScale(s) { return Math.min(MAX_SCALE, Math.max(MIN_SCALE, s)); }

  function fitView() {
    const b = pyramidBounds();
    const availW = Math.max(10, viewW - FIT_PADDING * 2);
    const availH = Math.max(10, viewH - FIT_PADDING * 2);
    scale = clampScale(Math.min(availW / b.w, availH / b.h));
    const cx = (b.minX + b.maxX) / 2;
    const cy = (b.minY + b.maxY) / 2;
    tx = viewW / 2 - cx * scale;
    ty = viewH / 2 - cy * scale;
  }

  function pyramidFits() {
    const b = pyramidBounds();
    const tl = worldToScreen(b.minX, b.minY);
    const br = worldToScreen(b.maxX, b.maxY);
    return (
      tl.x >= FIT_PADDING && tl.y >= FIT_PADDING &&
      br.x <= viewW - FIT_PADDING && br.y <= viewH - FIT_PADDING
    );
  }

  function zoomAt(sx, sy, factor) {
    const before = screenToWorld(sx, sy);
    scale = clampScale(scale * factor);
    tx = sx - before.x * scale;
    ty = sy - before.y * scale;
    hidePalette();
    draw();
  }

  // ---- Rendering ----
  function resize() {
    dpr = Math.max(1, window.devicePixelRatio || 1);
    viewW = canvas.clientWidth;
    viewH = canvas.clientHeight;
    canvas.width = Math.round(viewW * dpr);
    canvas.height = Math.round(viewH * dpr);
    draw();
  }

  function draw() {
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.clearRect(0, 0, viewW, viewH);

    const cellScreen = CELL * scale;
    const vMin = screenToWorld(0, 0);
    const vMax = screenToWorld(viewW, viewH);
    const drawText = cellScreen > 18;

    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.lineWidth = Math.max(1, Math.min(2, cellScreen * 0.03));
    ctx.strokeStyle = "#1a1a1a";

    for (let n = 0; n < layers; n++) {
      const count = n + 1;
      const left = rowLeft(n);
      const wy = n * CELL;
      if (wy + CELL < vMin.y || wy > vMax.y) continue;

      ensureRows(n);
      for (let k = 0; k < count; k++) {
        const wx = left + k * CELL;
        if (wx + CELL < vMin.x || wx > vMax.x) continue;

        const str = valueStr(n, k);
        const custom = customColors[n + "," + k];
        const col = custom || defaultColorFor(str);

        const p = worldToScreen(wx, wy);
        const size = cellScreen;

        ctx.fillStyle = col;
        ctx.fillRect(p.x, p.y, size, size);
        ctx.strokeRect(p.x, p.y, size, size);

        if (drawText) {
          ctx.fillStyle = textColorFor(col);
          let fontSize = Math.min(size * 0.42, size * 0.9 / Math.max(1, str.length * 0.62));
          fontSize = Math.max(6, fontSize);
          ctx.font = `700 ${fontSize}px -apple-system, "Segoe UI", Roboto, Arial, sans-serif`;
          let measured = ctx.measureText(str).width;
          const maxW = size * 0.86;
          if (measured > maxW) {
            fontSize *= maxW / measured;
            fontSize = Math.max(5, fontSize);
            ctx.font = `700 ${fontSize}px -apple-system, "Segoe UI", Roboto, Arial, sans-serif`;
          }
          ctx.fillText(str, p.x + size / 2, p.y + size / 2 + size * 0.02);
        }
      }
    }
  }

  // ---- Add layer ----
  function addLayer() {
    layers += 1;
    ensureRows(layers - 1);
    document.getElementById("layerCount").textContent = String(layers);
    if (!pyramidFits()) fitView();
    hidePalette();
    draw();
  }

  function removeLayer() {
    if (layers <= 1) return;
    const removed = layers - 1; // row index being removed (0-indexed)
    layers -= 1;
    // drop any custom colours that belonged to the removed row
    for (const key of Object.keys(customColors)) {
      if (parseInt(key.split(",")[0], 10) === removed) delete customColors[key];
    }
    document.getElementById("layerCount").textContent = String(layers);
    hidePalette();
    draw();
  }

  // ---- Hit testing (which cell is at a screen point) ----
  function hitTest(sx, sy) {
    const w = screenToWorld(sx, sy);
    const n = Math.floor(w.y / CELL);
    if (n < 0 || n >= layers) return null;
    const left = rowLeft(n);
    const k = Math.floor((w.x - left) / CELL);
    if (k < 0 || k > n) return null;
    return { n, k };
  }

  // ---- Colour palette popup ----
  const PALETTE = [
    "#e02323", "#f5a623", "#f8e71c", "#2ea843", "#1abc9c", "#2d7ff9",
    "#8e44ad", "#ff6fb5", "#8b5a2b", "#1a1a1a", "#ffffff", "#9b9b9b",
  ];
  const paletteEl = document.getElementById("palette");
  const swatchesEl = document.getElementById("swatches");
  let selectedCell = null;

  function buildSwatches() {
    swatchesEl.innerHTML = "";
    for (const c of PALETTE) {
      const chip = document.createElement("div");
      chip.className = "chip";
      chip.style.background = c;
      chip.title = c;
      chip.addEventListener("click", () => applyColor(c));
      swatchesEl.appendChild(chip);
    }
  }

  function showPalette(clientX, clientY, cell) {
    selectedCell = cell;
    paletteEl.style.display = "block";
    const pw = paletteEl.offsetWidth || 188;
    const ph = paletteEl.offsetHeight || 160;
    let x = clientX + 12;
    let y = clientY + 12;
    if (x + pw > window.innerWidth - 8) x = clientX - pw - 12;
    if (y + ph > window.innerHeight - 8) y = clientY - ph - 12;
    paletteEl.style.left = Math.max(8, x) + "px";
    paletteEl.style.top = Math.max(8, y) + "px";
  }
  function hidePalette() {
    paletteEl.style.display = "none";
    selectedCell = null;
  }
  function applyColor(c) {
    if (!selectedCell) return;
    customColors[selectedCell.n + "," + selectedCell.k] = c;
    draw();
    hidePalette();
  }
  document.getElementById("resetCell").addEventListener("click", () => {
    if (!selectedCell) return;
    delete customColors[selectedCell.n + "," + selectedCell.k];
    draw();
    hidePalette();
  });

  // ---- Pointer / gesture handling ----
  const pointers = new Map();
  let pinchPrev = null;

  function getPos(e) {
    const rect = canvas.getBoundingClientRect();
    return { x: e.clientX - rect.left, y: e.clientY - rect.top };
  }

  canvas.addEventListener("pointerdown", (e) => {
    canvas.setPointerCapture(e.pointerId);
    const pos = getPos(e);
    pointers.set(e.pointerId, {
      x: pos.x, y: pos.y,
      startX: pos.x, startY: pos.y,
      clientX: e.clientX, clientY: e.clientY,
      moved: false,
    });
    if (pointers.size === 2) pinchPrev = pinchState();
  });

  canvas.addEventListener("pointermove", (e) => {
    const info = pointers.get(e.pointerId);
    if (!info) return;
    const pos = getPos(e);
    const dx = pos.x - info.x, dy = pos.y - info.y;
    info.x = pos.x; info.y = pos.y;
    info.clientX = e.clientX; info.clientY = e.clientY;
    if (Math.hypot(pos.x - info.startX, pos.y - info.startY) > TAP_MOVE_THRESHOLD) {
      info.moved = true;
    }

    if (pointers.size === 1) {
      tx += dx; ty += dy;
      draw();
    } else if (pointers.size === 2) {
      const cur = pinchState();
      if (pinchPrev) {
        const factor = cur.dist / pinchPrev.dist;
        const before = screenToWorld(cur.midX, cur.midY);
        scale = clampScale(scale * factor);
        tx = cur.midX - before.x * scale;
        ty = cur.midY - before.y * scale;
        tx += cur.midX - pinchPrev.midX;
        ty += cur.midY - pinchPrev.midY;
        draw();
      }
      pinchPrev = cur;
    }
  });

  function endPointer(e) {
    const info = pointers.get(e.pointerId);
    const wasOnly = pointers.size === 1;
    pointers.delete(e.pointerId);
    if (pointers.size < 2) pinchPrev = null;

    if (info && wasOnly && !info.moved) {
      // treat as a tap -> select cell
      const cell = hitTest(info.x, info.y);
      if (cell) {
        buildSwatches();
        showPalette(info.clientX, info.clientY, cell);
      } else {
        hidePalette();
      }
    }
  }
  canvas.addEventListener("pointerup", endPointer);
  canvas.addEventListener("pointercancel", (e) => { pointers.delete(e.pointerId); if (pointers.size < 2) pinchPrev = null; });

  function pinchState() {
    const pts = [...pointers.values()];
    const dx = pts[0].x - pts[1].x;
    const dy = pts[0].y - pts[1].y;
    return {
      dist: Math.hypot(dx, dy) || 1,
      midX: (pts[0].x + pts[1].x) / 2,
      midY: (pts[0].y + pts[1].y) / 2,
    };
  }

  canvas.addEventListener("wheel", (e) => {
    e.preventDefault();
    const pos = getPos(e);
    zoomAt(pos.x, pos.y, Math.exp(-e.deltaY * 0.0015));
  }, { passive: false });

  // hide palette when clicking elsewhere
  document.addEventListener("pointerdown", (e) => {
    if (!paletteEl.contains(e.target) && e.target !== canvas) hidePalette();
  });

  // ---- Persistence ----
  function saveState() {
    const state = {
      layers,
      scale, tx, ty,
      customColors,
    };
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
      const btn = document.getElementById("save");
      btn.textContent = "Saved \u2713";
      btn.classList.add("saved");
      setTimeout(() => { btn.textContent = "Save"; btn.classList.remove("saved"); }, 1400);
    } catch (err) {
      alert("Could not save: " + err.message);
    }
  }

  function loadState() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return false;
      const s = JSON.parse(raw);
      if (typeof s.layers === "number" && s.layers >= 1) layers = Math.floor(s.layers);
      if (typeof s.scale === "number") scale = s.scale;
      if (typeof s.tx === "number") tx = s.tx;
      if (typeof s.ty === "number") ty = s.ty;
      if (s.customColors && typeof s.customColors === "object") customColors = s.customColors;
      ensureRows(layers - 1);
      document.getElementById("layerCount").textContent = String(layers);
      return true;
    } catch (err) {
      return false;
    }
  }

  // ---- Buttons ----
  document.getElementById("addLayer").addEventListener("click", addLayer);
  document.getElementById("removeLayer").addEventListener("click", removeLayer);
  document.getElementById("save").addEventListener("click", saveState);
  document.getElementById("zoomIn").addEventListener("click", () => zoomAt(viewW / 2, viewH / 2, 1.25));
  document.getElementById("zoomOut").addEventListener("click", () => zoomAt(viewW / 2, viewH / 2, 0.8));
  document.getElementById("fit").addEventListener("click", () => { hidePalette(); fitView(); draw(); });
  window.addEventListener("resize", resize);

  // ---- Init ----
  function init() {
    viewW = canvas.clientWidth;
    viewH = canvas.clientHeight;
    const restored = loadState();
    resize();
    if (!restored) fitView();
    draw();
  }
  requestAnimationFrame(init);
})();
</script>
</body>
</html>

Youez - 2016 - github.com/yon3zu
LinuXploit