Creating rows with cells with JXA

There’s an OO document with 3 columns.

I want to add a row via Javascript for Automation. At first I tried to create a row and push it into the document.

oo = Application("OmniOutliner");

var doc = oo.documents;

test = oo.Row({
	name: "Title"


This gets me Error -10000: AppleEvent handler failed..

What does it take to create a row in OO? Then I also need to attach cells, but if I can’t create a simple row, then it is pointless to ask that right now as well.

The previous part is solved. Name is a r/o attribute, which means read only, that’s why I couldn’t define it (it could have a better error though).

oo = Application("OmniOutliner");
var doc = oo.documents;
test = oo.Row({
	topic: "Title"

Now I can’t find a way to set the text of a column.

So, I think I solved the problem. I’ll post the solution since there’s a lack of examples of OmniOutliner with JXA here:

oo = Application("OmniOutliner");
oo.includeStandardAdditions = true

var doc = oo.documents;

// There's either a nasty hidden column or the noteColumn is 1-based
var noteColumn = doc[0].noteColumn.index() - 1

// Topic||Column||Column
// Just use || as separator and it should do the job for you.
var clipboard = oo.theClipboard().split('\r');
var final = [];

//Splits the clipboard items and inserts an empty string in the index of the noteColumn.
clipboard.forEach(function(el) {
	var elem = el.split('||');
	elem.splice(noteColumn, 0, "");

final.forEach(function(movie, idx) {
	// Creates an empty row.
	var row = oo.Row({});
	// Appends the row to your document. It returns the index, so we're storing it.
	var index = doc[0].rows.push(row);
	// Grabs all the rows, then select the last item (which we just appended) and loop through its cells, modifying the values according to our clipboard array.
	doc[0].rows()[index - 1].cells().forEach(function(cell, i) {
		cell.value = movie[i];

So I appended the row, grabbed it, looped through its cells and replaced their contents.

Thanks ! Very good to see some JavaScript for Automation (JXA) examples here.

A couple of thoughts:

  • We can make our variables local by wrapping our code in a run() { ... code ... } function, or in an immediately executed anonymous function (function () { ... code ... })(); An advantage of doing this is that it separates our variables out into a local panel if we use the Safari JS debugger and saves us the trouble of hunting for them in the forest of global variables.
  • Perhaps, for clarity and reuse, we can separate out the clipboard parsing from the row creation ?
  • I find it quite helpful to add 'use strict'; at the start of a function. It adds some useful error messages, warning for example of variables which are undeclared (and therefore inadvertently global rather than local);

e.g. something like:

(function () {
    'use strict';

    // IDs of rows created from (field-delimited) text lines
    // ooDoc -> String -> [ooRowID]
    function lineRows(doc, strLines, rowDelim, colDelim) {
        // optional arguments, falling back to defaults
        rowDelim = rowDelim || /[\n\r]+/;
        colDelim = colDelim || '||';

        if (doc && strLines) {

            var lstLines = lineRecords(strLines, rowDelim, colDelim),
                rows = doc.rows,
                iFirstCol = doc.topicColumn.index(),
                lstRows = [];

            lstLines.forEach(function (lstLine, i) {
                var iRow = rows.push(
                            topic: lstLine[0],
                            note: '\n'
                    ) - 1,
                    cells =;

                // Remaining columns
                lstLine.slice(1).forEach(function (strField, j) {
                    if (strField) {
               + iFirstCol).value = strField;


            return rows.slice(lstRows[0], lstRows.slice(-1)[0]).id();

        } else return [];

    // String and optional row/field delimiters -> array of arrays
    // (lines of fields)
    // String -> String -> String -> [[String]]
    function lineRecords(str, rowDelim, colDelim) {
        // optional arguments, falling back to defaults
        rowDelim = rowDelim || /[\n\r]+/;
        colDelim = colDelim || '||';

        return str.split(rowDelim).map(function (r) {
            return r.split(colDelim);

    // MAIN
    var oo = Application("OmniOutliner"),
        docs = oo.documents,
        doc = docs.length ? : undefined;

    oo.includeStandardAdditions = true;

    // Array of row IDs of any new rows created in front document
    return doc ? lineRows(doc, oo.theClipboard()) : [];