How to use transactions
Transactions are extremely useful for allowing you to build up more complex XML structures without having to worry about validity at all steps. Validation occurs at the end of the transaction.
Transactions
A transaction is a sequence of actions that are, for one purpose or another, considered to be atomic (that is, not divisible).
Editor.runInTransaction()
Editor.runInTransaction() takes as an argument the code that you wish to run in a single transaction. This can be a single function or whole sequence of functions, lines of code, etc. The only limitation being that the code must save its work in some way, whether by attaching it to the document successfully or storing it in a global variable. This is since Editor.runInTransaction() only returns a boolean value, with true indicating that the transaction was committed and false indicating that it was rolled back.
If you need your code to return a value, you should use Editor.wrapInTransaction() to create a transactionalized function that passes your return value back.
Editor.wrapInTransaction()
Editor.wrapInTransaction() takes an argument of a function and returns a function. Specifically, it returns the same function now wrapped in an Content Editor transaction. The new function has two possible return values: if the transaction succeeds, it returns the same value that the input function would have. However, if the transaction fails, it returns null.
Editor.RevertingException
Throwing an Editor.RevertingException is the standard way of cancelling a change in an event handler. It will also cause any transaction that is in process to rollback and Editor.runInTransaction() to return false.
Examples using transactions
Editor.wrapInTransaction() to create a transactional anonymous function tied to the onclick method of the document element being created. This is necessary because the code invoked by the onclick will execute asynchronously from the rest of Content Editor and the transaction keeps it from interfering with other code that it would otherwise interrupt. In this case, the restructure() function is called, passing it the new node, the name of the new node and the sibling node next to which it should be attached.
function addOption(parent, node, nodeName, sibling)
{
var opt = document.
createElement("div");
opt.onclick = function(){ Editor.wrapInTransaction(restructure(node, nodeName,
sibling));};
opt.className = "addoption";
if (nodeName == "lid")
opt.
appendChild(document.
createTextNode(nodeName));
else
opt.
appendChild((document.
createTextNode(nodeName + "head"));
parent.
appendChild((opt);
}
Editor.runInTransaction() in a similar manner to provide a context to code that will execute asynchronously from Content Editor .
function completeTOC(evt, ask)
{
var xopusElement = evt.childNode;
// various initializations happen here, omitted for length
if(ask && Editor.confirm("Do you want to generate the Table of Contents?"))
{
generateTOC(doc, xopusElement);
}
setTimeout(function()
{
Editor.runInTransaction()(function()
{
Editor.Selection.setRange(range);
labelText.
setNodeValue("Contents");
})
}, 0);
}
The function completeTOC() builds up the Table of Contents and then uses setTimeout() to detach and run asynchronously the code that selects the table of contents element and sets the default value of the top-most node as "Contents". The reason to detach and run the code asynchronously is that the Editor.Selection.setRange(range) method might be slow and take time to complete. The code is thus set up to execute in the transaction so it has a context to execute in and also will not disrupt other code (due to the JavaScript scheduler).