Skip to content

Docs: A Complete Demo for various compression/decompression #186

@cyfung1031

Description

@cyfung1031

Could you please add this complete demo to show the usage of lz-string?

Demo Link

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- 123213 -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LZ-String Example</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/setImmediate/1.0.5/setImmediate.min.js"></script>
    <style>
      * {
          box-sizing: border-box;
      }
      body {
          font-family: 'Roboto', sans-serif;
          background-color: #f4f4f4;
          color: #333;
      }
      .container {
          max-width: 600px;
          margin: 20px auto;
          padding: 20px;
          border: 1px solid #ddd;
          background-color: #fff;
          box-shadow: 0 4px 8px rgba(0,0,0,0.05);
          border-radius: 8px;
      }
      h1, h2, h3 {
          text-align: center;
      }
      h2, h3 {
          text-align: left;
      }
      textarea, select, button {
          width: 100%;
          padding: 10px;
          margin-bottom: 15px;
          border: 1px solid #ccc;
          border-radius: 4px;
      }
      button {
          background-color: #0084ff;
          color: white;
          font-weight: 500;
          cursor: pointer;
      }
      button:hover {
          background-color: #0066cc;
      }
      #toolbar {
          --position-default: absolute;
          --position-value: var(--position-default);
          position: var(--position-value);
          display: inline-flex;
          flex-direction: row;
          flex-wrap: nowrap;
          white-space: nowrap;
          transform: translate(-100%,-100%);
          margin-top: -4px;
          gap: 4px;
          align-items: center;
      }
      #toolbar.hide {
          --position-value: none;
      }
      #toolbar:hover {
          position: var(--position-default);
      }
      #toolbar span {
          padding: 0 4px;
      }
      #toolbar button {
          margin-right: 5px;
          width: 4em;
          font-size: 12px;
          padding: 4px;
          margin: 0;
          transition: all 0.1s ease;
      }
      #toolbar button.clicked {
          transform: scale(0.95);
          background-color: #184340;
          box-shadow: 0 2px 4px rgba(0,0,0,0.2);
          color: white;
      }
      #inputText, #compressedInput {
          background: #f0f6ff;
      }
      #compressedText, #decompressedText {
          background: #fafafa;
      }
      @media (max-width: 600px) {
          .container {
              width: 90%;
              margin: 10px auto;
          }
          h1, h2, h3 {
              font-size: 1.2em;
          }
      }

    </style>
</head>
<body>
    <div class="container">
        <h1>LZ-String Compression Example</h1>
        
        <h2>Compression Method</h2>
        <select id="compressionMethod">
            <option value="compress">Compress</option>
            <option value="compressToUTF16">Compress to UTF16</option>
            <option value="compressToBase64">Compress to Base64</option>
            <option value="compressToEncodedURIComponent">Compress to Encoded URI Component</option>
            <option value="compressToUint8Array">Compress to Uint8Array</option>
        </select>

        <h2>Compression</h2>
        <textarea id="inputText" placeholder="Enter text to compress"></textarea>

        <button onclick="compressText()">Compress</button>
        <h3>Compressed Text:</h3>
        <textarea id="compressedText" readonly></textarea>

        <h2>Decompression</h2>
        <textarea id="compressedInput" placeholder="Enter compressed text"></textarea>
        <button onclick="decompressText()">Decompress</button>
        <h3>Decompressed Text:</h3>
        <textarea id="decompressedText" readonly></textarea>
    </div>
    <div id="toolbar">
        <span id="textCount"></span>
        <button onclick="copyText(this)">Copy</button>
        <button onclick="pasteText(this)">Paste</button>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js"></script>
    <script>
      // browser safe text for copy & paste
      const jzEncode = t => JSON.stringify({"t":t}).slice(6, -2);
      const jzDecode = t => JSON.parse(`{"t":"${t}"}`).t;

      function compressText() {
        const inputText = document.getElementById('inputText').value;
        const method = document.getElementById('compressionMethod').value;
        let compressedText = '';
        try {
          switch (method) {
            case 'compressToUTF16':
              compressedText = LZString.compressToUTF16(inputText);
              break;
            case 'compressToBase64':
              compressedText = LZString.compressToBase64(inputText);
              break;
            case 'compressToEncodedURIComponent':
              compressedText = LZString.compressToEncodedURIComponent(inputText);
              break;
            case 'compressToUint8Array':
              const uint8Array = Array.from(LZString.compressToUint8Array(inputText));
              compressedText = uint8Array.map(byte => byte.toString(16).padStart(2, '0').toUpperCase()).join(' ');
              break;
            default:
              compressedText = LZString.compress(inputText);
          }
        } catch (e) {
          compressedText = 'Error during compression: ' + e.message;
        }
        document.getElementById('compressedText').value = compressedText;
      }

      function decompressText() {
        const compressedInput = document.getElementById('compressedInput').value;
        const method = document.getElementById('compressionMethod').value;
        let decompressedText = '';
        try {
          switch (method) {
            case 'compressToUTF16':
              decompressedText = LZString.decompressFromUTF16(compressedInput);
              break;
            case 'compressToBase64':
              decompressedText = LZString.decompressFromBase64(compressedInput);
              break;
            case 'compressToEncodedURIComponent':
              decompressedText = LZString.decompressFromEncodedURIComponent(compressedInput);
              break;
            case 'compressToUint8Array':
              const bytes = compressedInput.split(' ').map(hex => parseInt(hex, 16));
              decompressedText = LZString.decompressFromUint8Array(new Uint8Array(bytes));
              break;
            default:
              decompressedText = LZString.decompress(compressedInput);
          }
        } catch (e) {
          decompressedText = 'Error during decompression: ' + e.message;
        }
        document.getElementById('decompressedText').value = decompressedText;
      }
      
      !('ontouchstart' in document.documentElement) && document.addEventListener('click', function (evt) {
        if (evt.target instanceof HTMLTextAreaElement && !(getSelection() + "")) {
          evt.target.select();
        }
      }, true);
      document.addEventListener('transitionend', function(evt) {
          if (evt.target instanceof HTMLButtonElement) {
              evt.target.classList.remove('clicked'); // Remove the 'clicked' class at the end of the transition
          }
      }, true);




      let cid = 0;

      const copied = new Set();
      async function copyText(btn) {
        clearTimeout(cid);
        let textarea = document.getElementById('toolbar').currentTextArea;
        if (!textarea) return;
        btn.classList.add('clicked');
        textarea.select();
        textarea.setSelectionRange(0, textarea.value.length); // For mobile devices
        if (!document.execCommand("copy")) {
          await navigator.clipboard.writeText(textarea.value)
        };
      }

      async function pasteText(btn) {
        clearTimeout(cid);
        let textarea = document.getElementById('toolbar').currentTextArea;
        if (!textarea || textarea.readOnly) return;
          btn.classList.add('clicked');
        textarea.focus();
        textarea.select();
        textarea.setSelectionRange(0, textarea.value.length); // For mobile devices
        let pastedText = !document.execCommand('paste') ? await navigator.clipboard.readText() : null;
        if (pastedText) {
          if (copied.has(pastedText))  pastedText= jzDecode(pastedText);
          let startPos = textarea.selectionStart;
          let endPos = textarea.selectionEnd;
          // Insert text
          textarea.value = textarea.value.substring(0, startPos)
              + pastedText
              + textarea.value.substring(endPos, textarea.value.length);
          // Update cursor position
          textarea.selectionStart = textarea.selectionEnd = startPos + pastedText.length;
        };
        textarea.select();
        textarea.setSelectionRange(0, textarea.value.length); // For mobile devices
      }

      function updateTextCount() {
        let c = document.getElementById('toolbar') || 0;
        let d = c.currentTextArea || 0;
        if (!d) return;
        document.querySelector('#textCount').textContent = `${d.value.length} chars`;
      }
      document.addEventListener('input', () => {
        updateTextCount();
      }, true);
      document.addEventListener('focusin', function (e) {
        if (e.target.tagName === 'TEXTAREA') {
          clearTimeout(cid);
          cid = setTimeout(() => {
            let toolbar = document.getElementById('toolbar');
            toolbar.classList.remove('hide');
            let rect = e.target.getBoundingClientRect();
            toolbar.style.top = `${rect.top + window.scrollY}px`; // Adjust the position as needed
            toolbar.style.left = `${rect.right}px`;
            toolbar.currentTextArea = e.target;
            updateTextCount();
          }, 30)
        }
      });
      document.addEventListener('focusout', function (e) {
        if (e.target.tagName === 'TEXTAREA') {
          let toolbar = document.getElementById('toolbar');
          clearTimeout(cid);
          cid = setTimeout(() => {
            toolbar.classList.add('hide');
          }, 30);
        }
      });
      document.addEventListener("copy", (e) => {
        let activeElement = document.activeElement;
        const target = e.target;
        if (target instanceof HTMLTextAreaElement && target === activeElement) {
          let paste = window.getSelection().toString();
          if (paste) {
            // Save the original value and selection
            let originalValue = activeElement.value;
            let originalStart = activeElement.selectionStart;
            let originalEnd = activeElement.selectionEnd;
            // Modify the text to be copied
            let modifiedText0 = originalValue.substring(originalStart, originalEnd);
            let modifiedText = jzEncode(modifiedText0);
            if(modifiedText0 !== modifiedText) copied.add(modifiedText);
            // Temporarily change the value and select the modified text
            activeElement.value = modifiedText;
            activeElement.setSelectionRange(0, modifiedText.length);
            // Wait for the browser to copy
            setImmediate(()=> {
              // Restore the original value and selection
              activeElement.value = originalValue;
              activeElement.setSelectionRange(originalStart, originalEnd);
            });
          }
        }
      });
      document.addEventListener("paste", (e) => {
        const target = e.target;
        if (target instanceof HTMLTextAreaElement && !target.readOnly) {
          let paste = (e.clipboardData || window.clipboardData).getData("text");
          if (copied.has(paste)) {
            const decoded = jzDecode(paste);
            e.preventDefault();
            if (!document.execCommand("insertText", false, decoded)) {
              // Modify the pasted data 
              let modifiedData = decoded; // jzDecode(paste)
              // Insert the modified data into the textarea
              let textarea = e.target;
              let cursorPos = textarea.selectionStart;
              let textBefore = textarea.value.substring(0, cursorPos);
              let textAfter  = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
              // Set the new value and position the cursor
              textarea.value = textBefore + modifiedData + textAfter;
              textarea.selectionStart = textarea.selectionEnd = cursorPos + modifiedData.length;
            }
          }
        }
      });
    </script>
</body>
</html>
  • Note: This incorporate the problem for copy and paste in browser so that users can directly copy the compressed text and paste it to decompress. for example, compress "123213" will get a "\u0000" at the end of the string.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions