How do I adjust this script to place the item at the root of the document?

Hello all,

In the following script, I want to adjust it to place the item at the root of the document, instead of the project Now.

I have tried using outline.root, but my JavaScript is rusty.

I also want to convert var to const, but my attempts thus far have been met with errors.

var filePath = "~/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Focus.txt"; // Path to TaskPaper file
var appendItems = false; // If true, items will be added to the end rather than top of Inbox

function TaskPaperContextScript(editor, options) {
    var outline = editor.outline;
    var inbox = outline.evaluateItemPath('/project Now')[0];
    var items;
    if (options.text && options.text !== "") {
        items = ItemSerializer.deserializeItems(options.text, outline, ItemSerializer.TEXTMimeType);
    }

    if (!inbox) {
        inbox = outline.createItem("Now:");
        outline.root.insertChildrenBefore(inbox, outline.root.firstChild);
    }

    if (items) {
        if (options.append) {
            inbox.appendChildren(items);
        } else {
            inbox.insertChildrenBefore(items, inbox.firstChild);
        }
    }
}

var tp3 = Application("TaskPaper");

var strFullPath = ObjC.unwrap($(filePath).stringByExpandingTildeInPath);
var document = tp3.open(Path(strFullPath));

document.evaluate({
    script: TaskPaperContextScript.toString(),
    withOptions: {text: ("— Walk @now"), append: appendItems}
});

document.save();

I am just doing this quickly without testing, but I think this is what you would want if you wanted to insert at root instead of at project “Now”:

var filePath = "~/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Focus.txt"; // Path to TaskPaper file
var appendItems = false; // If true, items will be added to the end rather than top of Inbox

function TaskPaperContextScript(editor, options) {
    var outline = editor.outline;
    var root = outline.root;
    var items;
    if (options.text && options.text !== "") {
        items = ItemSerializer.deserializeItems(options.text, outline, ItemSerializer.TEXTMimeType);
    }

    if (items) {
        if (options.append) {
            root.appendChildren(items);
        } else {
            root.insertChildrenBefore(items, root.firstChild);
        }
    }
}

var tp3 = Application("TaskPaper");

var strFullPath = ObjC.unwrap($(filePath).stringByExpandingTildeInPath);
var document = tp3.open(Path(strFullPath));

document.evaluate({
    script: TaskPaperContextScript.toString(),
    withOptions: {text: ("— Walk @now"), append: appendItems}
});

document.save();

Sorry, not sure why const won’t work, maybe just try changing one instance such as var filePath to const filePath. If that doesn’t work, then maybe Apples javascript implementation doesn’t support. If that does work then slowly change other cases, until you find the one that fails. For example I expect changing var items; to const items will fail because you are assigning to items, so it’s not a const.

1 Like

That is what I want. Thanks Jesse!

Good strategy on tracking down the errant var.

The one that that gives an issue is:

var items;

Since @complexpoint is the one that has convinced me to used const in place of var, I’ll see if Rob has any comments on this script.

Do you want to show them to us ?

I wonder if you were either:

  1. defining a constant value within one ({...} - delimited) context or namespace), and then trying to reference it in another, or
  2. defining a constant value, and then trying to update the value attached to that constant name (in which case a let definition would work)

?


For example, writing:

const items

on its own, without a value, has no defined meaning in JavaScript, and the interpreter will error before the starting gun goes off.

const someValue = 1;

followed later by

const someValue = 2;

will, of course, also error.

1 Like

This was my original attempt at replacing var, which resulted in:

Error on line 7: SyntaxError: Unexpected token ‘;’. const declared variable ‘items’ must have an initializer.

const filePath = "~/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Focus.txt"; // Path to TaskPaper file
const appendItems = false; // If true, items will be added to the end rather than top of Inbox

function TaskPaperContextScript(editor, options) {
    const outline = editor.outline;
    const inbox = outline.evaluateItemPath('/project Now')[0];
    const items;
    if (options.text && options.text !== "") {
        items = ItemSerializer.deserializeItems(options.text, outline, ItemSerializer.TEXTMimeType);
    }

    if (!inbox) {
        inbox = outline.createItem("Now:");
        outline.root.insertChildrenBefore(inbox, outline.root.firstChild);
    }

    if (items) {
        if (options.append) {
            inbox.appendChildren(items);
        } else {
            inbox.insertChildrenBefore(items, inbox.firstChild);
        }
    }
}

const tp3 = Application("TaskPaper");

const strFullPath = ObjC.unwrap($(filePath).stringByExpandingTildeInPath);
const document = tp3.open(Path(strFullPath));

document.evaluate({
    script: TaskPaperContextScript.toString(),
    withOptions: {text: ("— Walk: @now"), append: appendItems}
});

document.save();

So, given that Jesse’s script meets my needs, and I am able to use const on everything but items—is that one instance worth changing?

After a brief test, this seems to work:

const filePath = "~/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Focus.txt"; // Path to TaskPaper file
const appendItems = false; // If true, items will be added to the end rather than top of Inbox

function TaskPaperContextScript(editor, options) {
    const outline = editor.outline;
    const root = outline.root;
    let items;
    if (options.text && options.text !== "") {
        items = ItemSerializer.deserializeItems(options.text, outline, ItemSerializer.TEXTMimeType);
    }

    if (items) {
        if (options.append) {
            root.appendChildren(items);
        } else {
            root.insertChildrenBefore(items, root.firstChild);
        }
    }
}

const tp3 = Application("TaskPaper");

const strFullPath = ObjC.unwrap($(filePath).stringByExpandingTildeInPath);
const document = tp3.open(Path(strFullPath));

document.evaluate({
    script: TaskPaperContextScript.toString(),
    withOptions: {text: ("— Walk: @now"), append: appendItems}
});

document.save();

So, given that Jesse’s script meets my needs, and I am able to use const on everything but items—is that one instance worth changing?

Of course – “whatever works” has been the guiding principle since Chapter 1 of the book of Genesis :slight_smile:

Using constant (rather than mutable) value names mainly yields a harvest when you are composing scripts in terms of nested expressions rather than sequential statements.

When the code is essentially an IO interaction, and the bytes are flying about at the interface between the computation and some broader context (invisible to the computation itself), it really doesn’t matter much.


The problem there was just that, in terms of JS syntax, you were:

  1. Declaring a constant name without any value attached to it. (as it happens, this has no meaning in the syntax or semantics of JS)
  2. trying to bind a value to that name between one pair of curly braces, and then expecting to find that name still defined between a separate set of braces later on.

{ ... } braces delimit a context, or name-space. They nest, but don’t transfer their name-bindings to later, separate, contexts.


You could, FWIW, have written something like:

Expand disclosure triangle to view JS source
function TaskPaperContextScript(editor, options) {
    const outline = editor.outline;
    const root = outline.root;
    const text = options.text;

    if ("string" === typeof text && text !== "") {
        const items = ItemSerializer.deserializeItems(
            options.text, outline, ItemSerializer.TEXTMimeType
        );

        return options.append
            ? (
                root.appendChildren(items),
                "Appended at end."
            )
            : (
                root.insertChildrenBefore(items, root.firstChild),
                "Inserted at start."
            );

    } else {
        return "empty or not a string: options.text";
    }
}

As in, for a version which returns a report, as well as producing an effect:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    const filePath = "~/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Focus.txt"; // Path to TaskPaper file
    // const filePath = "~/Desktop/Focus.taskpaper"


    // If true, items will be added to the end rather than top of Inbox
    const appendItems = true;

    function TaskPaperContextScript(editor, options) {
        const outline = editor.outline;
        const root = outline.root;
        const text = options.text;

        if ("string" === typeof text && text !== "") {
            const items = ItemSerializer.deserializeItems(
                options.text, outline, ItemSerializer.TEXTMimeType
            );

            return options.append
                ? (
                    root.appendChildren(items),
                    "Appended at end."
                )
                : (
                    root.insertChildrenBefore(items, root.firstChild),
                    "Inserted at start."
                );

        } else {
            return "empty or not a string: options.text";
        }
    }

    const tp3 = Application("TaskPaper");

    const strFullPath = ObjC.unwrap($(filePath).stringByExpandingTildeInPath);
    const document = tp3.open(Path(strFullPath));

    const result = document.evaluate({
        script: TaskPaperContextScript.toString(),
        withOptions: { text: "— Walk: @now", append: appendItems }
    });

    return (
        document.save(),
        result
    );
})();
2 Likes

Thank you both!

@complexpoint — I will study this tonight. Much appreciated!