Editor embedding
Events
The seven O3O.Editor events: onReady, onDocumentLoaded, onModified, onSaved, onError, onClose, onLimitReached, each with fields and an example.
On this page
Declared in config.events. Each function receives ONE argument, the event object; missing functions are skipped. Exceptions thrown by your functions are caught and printed to the console; they never break the editor.
| Event | When | Object |
|---|---|---|
onReady | Session created, iframe attached, editor frame ready. | {sessionId, mode} |
onDocumentLoaded | The document is visible. | {sessionId, mode, loadTimeMs} |
onModified | The “unsaved changes” state flips. | {modified} |
onSaved | The gate received a new version; exactly once per version number. | {key, version, url, size, sha256, savedAt} |
onError | Any error. | {code, message, detail} |
onClose | The user clicks close, or the page calls close(). | {sessionId, modified} |
onLimitReached | edit requested but the cap is reached; the editor still opens read-only. | {limit, current, edition, message, mode} |
Usual order: onReady → onDocumentLoaded → (onModified, onSaved)* → onClose.
onReady#
onReady
sessionIdstringrequiredSession id,ses_+ 24 hex.mode"edit" | "view"requiredActual session mode (may beviewif the cap was reached).
const events = {
onReady(e) {
// The editor frame is ready; the document is still loading
document.getElementById("status").textContent = "Opening document…";
console.log(e.sessionId, e.mode); // "ses_…", "edit" | "view"
}
};onDocumentLoaded#
onDocumentLoaded
sessionIdstringrequiredSession id.mode"edit" | "view"requiredSession mode.loadTimeMsnumberrequiredMilliseconds from creating the editor to the document showing, measured on the user's machine.
Right after this event, api.js applies the message-based ui options (status bar, menu bar).
const events = {
onDocumentLoaded(e) {
document.getElementById("status").textContent = "";
console.log("opened in", e.loadTimeMs, "ms, mode =", e.mode);
}
};onModified#
onModified
modifiedbooleanrequiredtruewhen there are unsaved changes.
let dirty = false;
const events = {
onModified(e) {
dirty = e.modified;
document.title = (dirty ? "● " : "") + "Contract";
}
};
// Warn users who leave while changes are unsaved
window.addEventListener("beforeunload", (ev) => { if (dirty) ev.preventDefault(); });onSaved#
onSaved
keystringrequiredThe document'sdocument.key.versionintegerrequiredNew version number, increasing from 1.urlstringrequiredSigned download URL for this version, valid for the retention time (O3O_EMBED_RETAIN_HOURS, 24 hours by default).sizeintegerrequiredSize in bytes.sha256stringrequiredHex SHA-256 of the file.savedAtstringrequiredSave time, ISO 8601 UTC.
{
"key": "contract-42-v7",
"version": 3,
"url": "http://localhost:8080/o3o/embed/files/doc_9835f61009fc659dfedab8696f2d2665?v=3&exp=1790086400&sig=5f1c…",
"size": 48890,
"sha256": "b1946ac92492d2347c6235b4d2611184…",
"savedAt": "2026-09-21T10:05:00Z"
}const events = {
onSaved(e) {
// e.url is a signed download URL, valid for the server retention time (24 hours by default)
console.log(e.key, "v" + e.version, e.size, "bytes", e.sha256, e.savedAt);
document.getElementById("download").href = e.url;
}
};How api.js detects a save: when the editor frame reports a successful save, or when the “modified” state goes from true to false, it polls GET /o3o/embed/session/{id} once a second, up to 10 times, until version exceeds the last known one, and only then fires onSaved.
onError#
onError
codestringrequiredStable error code; see the code table.messagestringrequiredA Vietnamese sentence you can show to users.detailobjectoptionalDefault:{}Extra details, e.g.errorsforinvalid_config.
{
"code": "download_failed",
"message": "Không tải được tài liệu từ địa chỉ đã cho.",
"detail": {}
}const events = {
onError(e) {
// e.message is a Vietnamese sentence you can show; branch on e.code
console.error(e.code, e.message, e.detail);
if (e.code === "network_error") setTimeout(() => location.reload(), 5000);
}
};onClose#
onClose
sessionIdstringrequiredSession id.modifiedbooleanrequiredUnsaved changes remained when closing.
const events = {
onClose(e) {
// the iframe has already been removed when this fires
if (e.modified) console.warn("closed with unsaved changes");
location.href = "/documents";
}
};onLimitReached#
onLimitReached
limitintegerrequiredEffective connection cap.currentintegerrequiredEditing sessions currently counted.edition"community" | "enterprise"requiredRunning edition.messagestringrequiredThe server's Vietnamese sentence;api.jsshows it verbatim in the banner.mode"view"requiredAlwaysview.
{
"limit": 50,
"current": 50,
"edition": "community",
"message": "Hệ thống đã đạt giới hạn 50 phiên soạn thảo đồng thời của bản cộng đồng. Tài liệu của bạn vẫn an toàn. Bạn có thể mở ở chế độ chỉ đọc hoặc thử lại sau ít phút.",
"mode": "view"
}const events = {
onLimitReached(e) {
// The editor IS still open, read-only (e.mode === "view")
console.warn(e.limit, e.current, e.edition, e.message);
}
};Full example: log every event#
<!doctype html>
<meta charset="utf-8">
<div id="o3o-editor" style="height: 70vh"></div>
<pre id="log" style="height: 25vh; overflow: auto; background: #f4f4f4"></pre>
<script src="http://localhost:8080/o3o/api.js"></script>
<script>
const log = (name) => (e) => {
document.getElementById("log").textContent += new Date().toISOString() + " " + name + " " + JSON.stringify(e) + "\n";
};
new O3O.Editor("o3o-editor", {
document: { url: "http://localhost:8080/o3o/demo/sample.docx", title: "sample.docx", fileType: "docx", key: "events-demo-1" },
editor: { mode: "edit", lang: "en-US", user: { id: "dev-1", name: "Dev" } },
ui: { closeButton: true },
events: {
onReady: log("onReady"),
onDocumentLoaded: log("onDocumentLoaded"),
onModified: log("onModified"),
onSaved: log("onSaved"),
onError: log("onError"),
onClose: log("onClose"),
onLimitReached: log("onLimitReached")
}
});
</script>Events for save-as and for version history.