Collaboration

Collaboration demo that showcases multi-user editing with real-time sync using the JavaScript spreadsheet component. Changes made by one user are instantly reflected across sessions, enabling teams to work together on the same spreadsheet, similar to Google Sheets, but embedded within your application.

<!DOCTYPE html>
<html lang="en">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Material+Icons&display=swap" />

<style>
.example:has(.collab-container) {
  display: block;
}
.collab-container {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.collab-panel-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.collab-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 600;
  font-size: 13px;
  font-family: 'Inter', sans-serif;
}
.collab-avatar.user-a { background: #4285f4; }
.collab-avatar.user-b { background: #34a853; }
.collab-label {
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 500;
  color: #444;
}
.collab-frame {
  width: 100%;
  height: 420px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  background: #fff;
}
</style>

<div class="collab-container">
  <div class="collab-panel">
    <div class="collab-panel-header">
      <div class="collab-avatar user-a">A</div>
      <span class="collab-label">User A</span>
    </div>
    <iframe id="frameA" class="collab-frame"></iframe>
  </div>
  <div class="collab-panel">
    <div class="collab-panel-header">
      <div class="collab-avatar user-b">B</div>
      <span class="collab-label">User B</span>
    </div>
    <iframe id="frameB" class="collab-frame"></iframe>
  </div>
</div>

<p align="center" style="margin-top:16px;">
  Click here to find more information about the
  <a href="/products/server">Jspreadsheet Server</a> extension
</p>

<script>
(function () {
  var GUID = '08d29049-e951-6722-dff0-d6149e62c0e2';
  var LICENSE = 'MTk5OTcwNjQxYjRmYjFiOTM4ZDQyMWY1YzNmOWZhNjYzYzI2YmNhYjQ2OTExZDEzMWU0NjYzNjcyNzJiOTQ4MGNmZGE1M2YxMjQyZTAxM2MyYzRlNTI0ZmNiYzNlN2FmM2RlNDRkMGU3OTZmM2JhMjMyNzdjYTRjYWM5NWZlNzEsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjeU5UTTNPVFEwTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9';

  function getFrameHTML() {
    return '<!DOCTYPE html>' +
    '<html><head>' +
    '<link rel="stylesheet" href="https://jspreadsheet.com/v12/jspreadsheet.css" />' +
    '<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" />' +
    '<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Material+Icons&display=swap" />' +
    '<style>body{margin:0;padding:0;overflow:auto;}</style>' +
    '</head><body>' +
    '<div id="spreadsheet"></div>' +
    '<scr' + 'ipt src="https://jspreadsheet.com/v12/jspreadsheet.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://jsuites.net/v6/jsuites.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/@lemonadejs/studio/dist/index.min.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/@jspreadsheet/formula-pro/dist/index.min.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/@jspreadsheet/client/dist/index.min.js"></scr' + 'ipt>' +
    '<scr' + 'ipt src="https://cdn.jsdelivr.net/npm/socket.io@4.7.5/client-dist/socket.io.min.js"></scr' + 'ipt>' +
    '<scr' + 'ipt>' +
    'jspreadsheet.setLicense("' + LICENSE + '");' +
    'jspreadsheet.setExtensions({ formula, client });' +
    'var remote = client.connect({ url: "https://jspreadsheet.com", path: "s/" });' +
    'remote.create("' + GUID + '", {' +
    '  tabs: false,' +
    '  toolbar: true,' +
    '  worksheets: [{ minDimensions: [4, 6], worksheetName: "Sheet 1" }]' +
    '});' +
    'function removeImageButton(t) {' +
    '  for (var i = 0; i < t.items.length; i++) {' +
    '    if (t.items[i].content === "add_photo_alternate") { t.items.splice(i, 1); i--; }' +
    '  }' +
    '  return t;' +
    '}' +
    'jspreadsheet(document.getElementById("spreadsheet"), {' +
    '  guid: "' + GUID + '",' +
    '  toolbar: removeImageButton,' +
    '  onbeforeloadimage: function() { return "/templates/default/img/blocked.png"; }' +
    '});' +
    '</scr' + 'ipt>' +
    '</body></html>';
  }

  function loadFrame(id) {
    var frame = document.getElementById(id);
    var doc = frame.contentDocument || frame.contentWindow.document;
    doc.open();
    doc.write(getFrameHTML());
    doc.close();
  }

  loadFrame('frameA');
  loadFrame('frameB');
})();
</script>
</html>
import React, { useRef, useEffect } from 'react';
import { jspreadsheet } from '@jspreadsheet/react';
import formula from '@jspreadsheet/formula-pro';
import client from '@jspreadsheet/client';

const GUID = "08d29049-e951-6722-dff0-d6149e62c0e2";
const SERVER_URL = "https://jspreadsheet.com";
const SERVER_PATH = "s/";

// Set your JSS license key (The following key only works for one day)
jspreadsheet.setLicense('MTk5OTcwNjQxYjRmYjFiOTM4ZDQyMWY1YzNmOWZhNjYzYzI2YmNhYjQ2OTExZDEzMWU0NjYzNjcyNzJiOTQ4MGNmZGE1M2YxMjQyZTAxM2MyYzRlNTI0ZmNiYzNlN2FmM2RlNDRkMGU3OTZmM2JhMjMyNzdjYTRjYWM5NWZlNzEsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjeU5UTTNPVFEwTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
jspreadsheet.setExtensions({ formula, client });

function CollabFrame({ label, color }) {
  const frameRef = useRef(null);

  useEffect(() => {
    const frame = frameRef.current;
    if (!frame) return;

    const doc = frame.contentDocument || frame.contentWindow.document;
    const LICENSE = 'MTk5OTcwNjQxYjRmYjFiOTM4ZDQyMWY1YzNmOWZhNjYzYzI2YmNhYjQ2OTExZDEzMWU0NjYzNjcyNzJiOTQ4MGNmZGE1M2YxMjQyZTAxM2MyYzRlNTI0ZmNiYzNlN2FmM2RlNDRkMGU3OTZmM2JhMjMyNzdjYTRjYWM5NWZlNzEsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjeU5UTTNPVFEwTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9';

    const html = `<!DOCTYPE html>
<html><head>
<link rel="stylesheet" href="https://jspreadsheet.com/v12/jspreadsheet.css" />
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Material+Icons&display=swap" />
<style>body{margin:0;padding:0;overflow:auto;}</style>
</head><body>
<div id="spreadsheet"></div>
<script src="https://jspreadsheet.com/v12/jspreadsheet.js"><\/script>
<script src="https://jsuites.net/v6/jsuites.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/studio/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/formula-pro/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/client/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/socket.io@4.7.5/client-dist/socket.io.min.js"><\/script>
<script>
jspreadsheet.setLicense("${LICENSE}");
jspreadsheet.setExtensions({ formula, client });
var remote = client.connect({ url: "${SERVER_URL}", path: "${SERVER_PATH}" });
remote.create("${GUID}", {
  tabs: false, toolbar: true,
  worksheets: [{ minDimensions: [4, 6], worksheetName: "Sheet 1" }]
});
jspreadsheet(document.getElementById("spreadsheet"), {
  guid: "${GUID}",
  toolbar: function(t) {
    t.items = t.items.filter(function(i) { return i.content !== "add_photo_alternate"; });
    return t;
  },
  onbeforeloadimage: function() { return "/templates/default/img/blocked.png"; }
});
<\/script>
</body></html>`;

    doc.open();
    doc.write(html);
    doc.close();
  }, []);

  return (
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12, padding: '0 4px' }}>
        <div style={{
          width: 28, height: 28, borderRadius: '50%',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          color: '#fff', fontWeight: 600, fontSize: 13, background: color,
        }}>
          {label[0]}
        </div>
        <span style={{ fontSize: 14, fontWeight: 500, color: '#444' }}>{label}</span>
      </div>
      <iframe
        ref={frameRef}
        style={{
          width: '100%', height: 420, border: '1px solid #e0e0e0',
          borderRadius: 8, background: '#fff',
        }}
      />
    </div>
  );
}

export default function CollaborationDemo() {
  return (
    <div>
      <div style={{ display: 'flex', gap: 24, maxWidth: 1360, margin: '0 auto' }}>
        <CollabFrame label="User A" color="#4285f4" />
        <CollabFrame label="User B" color="#34a853" />
      </div>
      <p style={{ textAlign: 'center', marginTop: 16 }}>
        Click here to find more information about the{' '}
        <a href="/products/server">Jspreadsheet Server</a> extension
      </p>
    </div>
  );
}
<template>
  <div>
    <div style="display:flex; gap:24px; max-width:1360px; margin:0 auto;">
      <div style="flex:1; min-width:0;" v-for="user in users" :key="user.label">
        <div style="display:flex; align-items:center; gap:8px; margin-bottom:12px; padding:0 4px;">
          <div :style="{
            width:'28px', height:'28px', borderRadius:'50%',
            display:'flex', alignItems:'center', justifyContent:'center',
            color:'#fff', fontWeight:600, fontSize:'13px', background: user.color
          }">
            {{ user.label[0] }}
          </div>
          <span style="font-size:14px; font-weight:500; color:#444;">{{ user.label }}</span>
        </div>
        <iframe
          :ref="el => user.ref = el"
          style="width:100%; height:420px; border:1px solid #e0e0e0; border-radius:8px; background:#fff;"
        ></iframe>
      </div>
    </div>
    <p style="text-align:center; margin-top:16px;">
      Click here to find more information about the
      <a href="/products/server">Jspreadsheet Server</a> extension
    </p>
  </div>
</template>

<script>
import "jspreadsheet/dist/jspreadsheet.css";
import "jsuites/dist/jsuites.css";

const GUID = "08d29049-e951-6722-dff0-d6149e62c0e2";
const SERVER_URL = "https://jspreadsheet.com";
const SERVER_PATH = "s/";

export default {
  name: "CollaborationDemo",
  data() {
    return {
      users: [
        { label: "User A", color: "#4285f4", ref: null },
        { label: "User B", color: "#34a853", ref: null },
      ],
    };
  },
  mounted() {
    this.users.forEach((user) => {
      this.loadFrame(user.ref);
    });
  },
  methods: {
    loadFrame(frame) {
      if (!frame) return;
      const doc = frame.contentDocument || frame.contentWindow.document;
      doc.open();
      doc.write(this.getFrameHTML());
      doc.close();
    },
    getFrameHTML() {
      // Set your JSS license key (The following key only works for one day)
      const LICENSE = 'MTk5OTcwNjQxYjRmYjFiOTM4ZDQyMWY1YzNmOWZhNjYzYzI2YmNhYjQ2OTExZDEzMWU0NjYzNjcyNzJiOTQ4MGNmZGE1M2YxMjQyZTAxM2MyYzRlNTI0ZmNiYzNlN2FmM2RlNDRkMGU3OTZmM2JhMjMyNzdjYTRjYWM5NWZlNzEsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjeU5UTTNPVFEwTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9';
      return `<!DOCTYPE html>
<html><head>
<link rel="stylesheet" href="https://jspreadsheet.com/v12/jspreadsheet.css" />
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Material+Icons&display=swap" />
<style>body{margin:0;padding:0;overflow:auto;}</style>
</head><body>
<div id="spreadsheet"></div>
<script src="https://jspreadsheet.com/v12/jspreadsheet.js"><\/script>
<script src="https://jsuites.net/v6/jsuites.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/studio/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/formula-pro/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/client/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/socket.io@4.7.5/client-dist/socket.io.min.js"><\/script>
<script>
jspreadsheet.setLicense("${LICENSE}");
jspreadsheet.setExtensions({ formula, client });
var remote = client.connect({ url: "${SERVER_URL}", path: "${SERVER_PATH}" });
remote.create("${GUID}", {
  tabs: false, toolbar: true,
  worksheets: [{ minDimensions: [4, 6], worksheetName: "Sheet 1" }]
});
jspreadsheet(document.getElementById("spreadsheet"), {
  guid: "${GUID}",
  toolbar: function(t) {
    t.items = t.items.filter(function(i) { return i.content !== "add_photo_alternate"; });
    return t;
  },
  onbeforeloadimage: function() { return "/templates/default/img/blocked.png"; }
});
<\/script>
</body></html>`;
    },
  },
  beforeUnmount() {
    if (window.jspreadsheet && typeof window.jspreadsheet.destroyAll === "function") {
      window.jspreadsheet.destroyAll();
    }
  },
};
</script>
import { Component, OnInit, OnDestroy, ElementRef, ViewChildren, QueryList, AfterViewInit } from '@angular/core';

const GUID = '08d29049-e951-6722-dff0-d6149e62c0e2';
const SERVER_URL = 'https://jspreadsheet.com';
const SERVER_PATH = 's/';

@Component({
  selector: 'app-collaboration-demo',
  standalone: true,
  template: `
    <div style="display:flex; gap:24px; max-width:1360px; margin:0 auto;">
      <div *ngFor="let user of users" style="flex:1; min-width:0;">
        <div style="display:flex; align-items:center; gap:8px; margin-bottom:12px; padding:0 4px;">
          <div [style.background]="user.color" style="
            width:28px; height:28px; border-radius:50%;
            display:flex; align-items:center; justify-content:center;
            color:#fff; font-weight:600; font-size:13px;">
            {{ user.label[0] }}
          </div>
          <span style="font-size:14px; font-weight:500; color:#444;">{{ user.label }}</span>
        </div>
        <iframe
          #collabFrame
          style="width:100%; height:420px; border:1px solid #e0e0e0; border-radius:8px; background:#fff;">
        </iframe>
      </div>
    </div>
    <p style="text-align:center; margin-top:16px;">
      Click here to find more information about the
      <a href="/products/server">Jspreadsheet Server</a> extension
    </p>
  `
})
export class CollaborationDemoComponent implements AfterViewInit, OnDestroy {
  @ViewChildren('collabFrame') frames!: QueryList<ElementRef<HTMLIFrameElement>>;

  users = [
    { label: 'User A', color: '#4285f4' },
    { label: 'User B', color: '#34a853' },
  ];

  ngAfterViewInit(): void {
    this.frames.forEach((frameRef) => {
      this.loadFrame(frameRef.nativeElement);
    });
  }

  ngOnDestroy(): void {
    const jss = (window as any).jspreadsheet;
    if (jss && typeof jss.destroyAll === 'function') {
      jss.destroyAll();
    }
  }

  private loadFrame(frame: HTMLIFrameElement): void {
    const doc = frame.contentDocument || frame.contentWindow?.document;
    if (!doc) return;
    doc.open();
    doc.write(this.getFrameHTML());
    doc.close();
  }

  private getFrameHTML(): string {
    // Set your JSS license key (The following key only works for one day)
    const LICENSE = 'MTk5OTcwNjQxYjRmYjFiOTM4ZDQyMWY1YzNmOWZhNjYzYzI2YmNhYjQ2OTExZDEzMWU0NjYzNjcyNzJiOTQ4MGNmZGE1M2YxMjQyZTAxM2MyYzRlNTI0ZmNiYzNlN2FmM2RlNDRkMGU3OTZmM2JhMjMyNzdjYTRjYWM5NWZlNzEsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjeU5UTTNPVFEwTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9';
    return `<!DOCTYPE html>
<html><head>
<link rel="stylesheet" href="https://jspreadsheet.com/v12/jspreadsheet.css" />
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Material+Icons&display=swap" />
<style>body{margin:0;padding:0;overflow:auto;}</style>
</head><body>
<div id="spreadsheet"></div>
<script src="https://jspreadsheet.com/v12/jspreadsheet.js"><\/script>
<script src="https://jsuites.net/v6/jsuites.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/studio/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/formula-pro/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/client/dist/index.min.js"><\/script>
<script src="https://cdn.jsdelivr.net/npm/socket.io@4.7.5/client-dist/socket.io.min.js"><\/script>
<script>
jspreadsheet.setLicense("${LICENSE}");
jspreadsheet.setExtensions({ formula, client });
var remote = client.connect({ url: "${SERVER_URL}", path: "${SERVER_PATH}" });
remote.create("${GUID}", {
  tabs: false, toolbar: true,
  worksheets: [{ minDimensions: [4, 6], worksheetName: "Sheet 1" }]
});
jspreadsheet(document.getElementById("spreadsheet"), {
  guid: "${GUID}",
  toolbar: function(t) {
    t.items = t.items.filter(function(i) { return i.content !== "add_photo_alternate"; });
    return t;
  },
  onbeforeloadimage: function() { return "/templates/default/img/blocked.png"; }
});
<\/script>
</body></html>`;
  }
}