Calculator

Calculater button on the corner of the screen, has dark/light theme switch and history.

目前為 2025-07-09 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Calculator
// @version      1.0
// @author       Mane
// @license     CC0-1.0
// @description  Calculater button on the corner of the screen, has dark/light theme switch and history.
// @match        *://*/*
// @run-at       document-end
// @namespace https://greasyfork.org/users/1491313
// ==/UserScript==

;(function(){
  'use strict';

  // --- KEYS ---
  const KEY_EXPR  = 'cc_expr';
  const KEY_HIST  = 'cc_hist';
  const KEY_THEME = 'cc_theme';

  // --- STATE ---
  let expr    = localStorage.getItem(KEY_EXPR) || '';
  let history = JSON.parse(localStorage.getItem(KEY_HIST) || '[]');
  let theme   = localStorage.getItem(KEY_THEME) || 'dark';

  // --- STYLES ---
  const css = `
    :root {
      --bg:#1e1e1e;--fg:#f1f1f1;--btn-bg:#333;--btn-fg:#f1f1f1;--accent:#007acc;
    }
    [data-theme="light"]{
      --bg:#f1f1f1;--fg:#1e1e1e;--btn-bg:#ddd;--btn-fg:#1e1e1e;--accent:#005a9e;
    }
    #cc-toggle {
      position:fixed;bottom:20px;right:20px;width:36px;height:36px;
      background:var(--accent);color:#fff;border:none;border-radius:4px;
      cursor:pointer;z-index:999999;font-size:18px;
    }
    #cc-panel {
      position:fixed;bottom:60px;right:20px;width:280px;
      background:var(--bg);color:var(--fg);padding:10px;
      border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.3);
      font-family:sans-serif;display:none;z-index:999999;
    }
    .small-btn {
      position:absolute;width:24px;height:24px;
      border:none;border-radius:4px;cursor:pointer;
      background:var(--btn-bg);color:var(--btn-fg);
      font-size:14px;line-height:1;
    }
    #cc-theme {top:8px;left:8px;}
    #cc-history-toggle {top:8px;right:8px;}
    #cc-display {
      width:100%;height:36px;margin:32px 0 8px;
      padding:0 8px;font-size:18px;text-align:right;
      background:var(--btn-bg);color:var(--fg);border:none;
    }
    .cc-row{display:flex;margin:4px 0}
    .cc-btn{
      flex:1;margin:2px;height:32px;
      background:var(--btn-bg);color:var(--btn-fg);
      border:none;border-radius:4px;cursor:pointer;
      font-size:16px;
    }
    .cc-op{background:var(--accent);color:#fff}
    #cc-history {
      display:none;margin-top:8px;
      max-height:140px;overflow:auto;
      background:var(--btn-bg);padding:6px;border-radius:4px;
    }
    #cc-history header{
      display:flex;justify-content:space-between;align-items:center;
      margin-bottom:4px;
    }
    #cc-history ul{
      list-style:none;padding:0;margin:0;font-size:14px;
    }
    #cc-history li{
      padding:2px 0;border-bottom:1px solid rgba(255,255,255,.1);
    }
    #cc-clear {
      width:100%;margin-top:4px;padding:4px;
      background:var(--btn-bg);color:var(--btn-fg);
      border:none;border-radius:4px;cursor:pointer;
    }
  `;
  const style = document.createElement('style');
  style.textContent = css;
  document.head.appendChild(style);

  // --- APPLY THEME ---
  document.documentElement.setAttribute('data-theme', theme);

  // --- BUILD UI ---
  const toggle = document.createElement('button');
  toggle.id = 'cc-toggle';
  toggle.textContent = '🖩';
  document.body.appendChild(toggle);

  const panel = document.createElement('div');
  panel.id = 'cc-panel';
  panel.innerHTML = `
    <button id="cc-theme" class="small-btn">${theme==='dark'?'☀️':'🌙'}</button>
    <button id="cc-history-toggle" class="small-btn">📜</button>
    <input id="cc-display" type="text" readonly />
    <div class="cc-row">
      <button class="cc-btn">7</button><button class="cc-btn">8</button>
      <button class="cc-btn">9</button><button class="cc-btn cc-op">/</button>
    </div>
    <div class="cc-row">
      <button class="cc-btn">4</button><button class="cc-btn">5</button>
      <button class="cc-btn">6</button><button class="cc-btn cc-op">*</button>
    </div>
    <div class="cc-row">
      <button class="cc-btn">1</button><button class="cc-btn">2</button>
      <button class="cc-btn">3</button><button class="cc-btn cc-op">-</button>
    </div>
    <div class="cc-row">
      <button class="cc-btn">0</button><button class="cc-btn">.</button>
      <button class="cc-btn cc-op">=</button><button class="cc-btn cc-op">+</button>
    </div>
    <div class="cc-row">
      <button class="cc-btn cc-op">(</button><button class="cc-btn cc-op">)</button>
      <button class="cc-btn">C</button><button class="cc-btn">√</button>
    </div>
    <div id="cc-history">
      <header>
        <strong>History</strong>
        <button id="cc-history-close" class="small-btn">✖️</button>
      </header>
      <ul></ul>
      <button id="cc-clear">Clear</button>
    </div>
  `;
  document.body.appendChild(panel);

  // --- INITIALIZE ---
  const disp = panel.querySelector('#cc-display');
  disp.value = expr;
  renderHistory();

  // --- EVENTS ---
  toggle.addEventListener('click', () => {
    panel.style.display = panel.style.display==='block'?'none':'block';
  });

  panel.addEventListener('click', e => {
    const b = e.target, v = b.textContent;

    // theme toggle
    if (b.id === 'cc-theme') {
      theme = theme==='dark'?'light':'dark';
      document.documentElement.setAttribute('data-theme', theme);
      localStorage.setItem(KEY_THEME, theme);
      b.textContent = theme==='dark'?'☀️':'🌙';
      return;
    }

    // history open/close
    if (b.id === 'cc-history-toggle') {
      const h = panel.querySelector('#cc-history');
      h.style.display = h.style.display==='block'?'none':'block';
      renderHistory();
      return;
    }
    if (b.id === 'cc-history-close') {
      panel.querySelector('#cc-history').style.display = 'none';
      return;
    }
    if (b.id === 'cc-clear') {
      history = [];
      saveHistory();
      renderHistory();
      return;
    }

    // calculator buttons
    if (b.classList.contains('cc-btn')) {
      if (v === 'C') {
        expr = '';
      } else if (v === '=') {
        evaluate();
      } else {
        expr += v;
      }
      updateDisplay();
    }
  });

  // --- CALC LOGIC ---
  function evaluate(){
    try {
      const safe = expr
        .replace(/√/g,'Math.sqrt')
        .replace(/\b(sin|cos|tan|log)\b/g,'Math.$1');
      const res = Function('return '+safe)();
      history.unshift({input:expr,result:res});
      history = history.slice(0,50);
      saveHistory();
      expr = String(res);
    } catch {
      expr = 'Error';
    }
  }

  function updateDisplay(){
    disp.value = expr;
    localStorage.setItem(KEY_EXPR, expr);
  }

  // --- HISTORY & STORAGE ---
  function saveHistory(){
    localStorage.setItem(KEY_HIST, JSON.stringify(history));
  }
  function renderHistory(){
    const ul = panel.querySelector('#cc-history ul');
    ul.innerHTML = history.map(it=>
      `<li>${it.input} = ${it.result}</li>`
    ).join('');
  }

})();