JXA (JavaScript) equivalent of AppleScript to move rows

What would be the JXA (or OmniJS) equivalent of this simple AppleScript?

tell application "OmniOutliner"
	tell front document to move selected rows to row "ARCHIVE"'s children's end
end tell

This, of course, doesn’t work:

oo = Application('OmniOutliner');
var doc = oo.documents[0];
var sel = doc.selectedRows;
var arch = doc.rows.byName('ARCHIVE');
oo.move(sel,{to: arch});

… because, perhaps among other reasons (JavaScript beginner), it’s trying to move it to an existing row. How does one get the equivalent of children's end?

SG

Any chance of some guidance here?

So far the only way I’ve found to move selected rows to the children a row with the topic ARCHIVE is to create new notes there and delete the original ones:

var  oo  = Application('OmniOutliner'),
     doc = oo.documents[0],
     sel = doc.selectedRows,
	 arc = doc.rows.byName('ARCHIVE');

for (var i=0; i < sel.length; i++) {
      arc.children.push(    // make new row under ARCHIVE
			oo.Row({ 
						topic: sel[i].topic,
						note: sel[i].note
			})
		);    
        sel[i]().delete();  // delete original row
		i = i-1;
}

This isn’t pretty, and gets even more complicated with values in columns! Is there no way in JXA (or OmniJS) to use the move() method similar to the very simple AppleScript above?

SG

We might expect something like this to work:

(function () {
    'use strict';

    var oo = Application('OmniOutliner'),
        ds = oo.documents,
        d = ds.length > 0 ? ds.at(0) : undefined;

    var seln = d.selectedRows,
        arc = d.rows.byName('Archive');

    oo.move(seln, {
        to: arc.rows
    });
})();

But given the result (below) you may need to choose OmniOutliner > Help > Contact Omni in the main menu, and send a query. I think this forum is mainly understood as user-to-user, in practice.

In the meanwhile, for a more manual approach (make copies, then delete originals), you may be able to make some progress (on the copying side), with code like this:

(function () {
    'use strict';

    var oo = Application('OmniOutliner'),
        ds = oo.documents,
        d = ds.length > 0 ? ds.at(0) : undefined;

    if (d) {
        var seln = d.selectedRows(),
            arc = d.rows.byName('Archive');

        seln.forEach(function (x) {
            return arc.rows.push(oo.Row({
                topic: x.topic,
                note: x.note
            }));
        });
        arc.expanded = true;
    }
})();

@draft8

Thanks much! Will study your examples of better code.

In my original experimentation I saw the ‘Replacement not supported’ message too. I assumed it meant I was trying to move onto an existing object and replace it, rather than copying to the end of a list of objects the way the AppleScript does.

I was thinking another potential “manual” approach might be outdent to the ‘Archive’ level, set the index property to follow ‘Archive’, and indent again. That way, don’t have to worry about handling any values in columns. But still a hack.

SG

It seems that the deletions have to be done right to left – from the end of the (selection) collection/array towards the beginning.

(Deleting something to the left breaks the references to its rightward siblings, but deleting from the right leaves leftward things intact)

Array.reduceRight() would be one way:

(function () {
    'use strict';

    var oo = Application('OmniOutliner'),
        ds = oo.documents,
        d = ds.length > 0 ? ds.at(0) : undefined;

    if (d) {
        var seln = d.selectedRows(),
            arc = d.rows.byName('Archive'),
            arcRows = arc.rows;

        seln.forEach(function (x) {
            return arcRows.push(oo.Row({
                topic: x.topic,
                note: x.note
            }));
        });

        arc.expanded = true;
        seln.reduceRight(function (a, x) {
            return oo.delete(x);
        }, undefined);
    }
})();

Or in an ES6 idiom, if you are using Sierra:

(() => {
    'use strict';

    const
        oo = Application('OmniOutliner'),
        ds = oo.documents,
        d = ds.length > 0 ? ds.at(0) : undefined;

    if (d) {
        const
            seln = d.selectedRows(),
            arc = d.rows.byName('Archive'),
            arcRows = arc.rows;

        seln.forEach(x =>
            arcRows.push(oo.Row({
                topic: x.topic,
                note: x.note
            })));

        arc.expanded = true;
        seln.reduceRight((_, x) => oo.delete(x), undefined)
    }
})();

@draft8

These examples are invaluable for those of us struggling up the learning curve. Thanks!

SG

1 Like