I usually work in JS, which lacks a full implementation of the OSA location specifiers, but if you are working in AppleScript, I think you may be able to use more delicate import targeting, with semantics like to after theRow
(followed by a delete of theRow)
Here, FWIW is a JS example of transforming and reintroducing an XML subset of the document.
(A script which toggles the AZ ⇄ ZA sorting of the children of the selected row)
Expand disclosure triangle to view JS source
(() => {
"use strict";
// Second version of a `Sort children of selected line`
// for Bike. (Uses XQuery)
// RobTrew @2022
// Ver 0.14
// Toggles between ascending and descending
// alphabetic sort of the children of the
// item selected in the front Bike document.
// -------------- SORTED BIKE SIBLINGS ---------------
// main :: IO ()
const main = () => {
const
start = new Date(),
bike = Application("Bike"),
doc = bike.documents.at(0);
return either(
alert("Sorting children of selected row.")
)(
report => `${report} (${elapsed(start)}s)`
)(
doc.exists() ? (
toggleSortedSelectionChildrenLR(bike)(doc)
) : Left("No documents open in Bike.")
);
};
// --------------------- SORTING ---------------------
// sortedChildNodesLR :: Bool -> Bike Document ->
// Bike Row -> Either String [XMLNode]
const sortedChildNodesLR = isZA =>
// Either a message or a list of sorted nodes.
doc => parentRow => {
const
direction = isZA ? (
"descending"
) : "ascending",
xquery = [
"for $li in /html/body/ul/li/ul/li",
"let $pText := upper-case(string($li/p))",
`order by $pText ${direction}`,
"return $li"
].join("\n");
return xQueryLR(xquery)(
doc.export({
from: parentRow,
as: "bike format",
all: true
})
);
};
// toggleSortedSelectionChildrenLR :: Application ->
// Bike Doc -> Either IO String IO String
const toggleSortedSelectionChildrenLR = app =>
// Either a message or an update to the
// specfied document.
// Children of selected row re-sorted (ASC or DESC).
doc => {
const
selectedRows = doc.rows.where({
selected: true
}),
parentRow = selectedRows.at(0),
hasChildren = parentRow.exists() && (
0 < parentRow.rows.length
);
return hasChildren ? (() => {
const
children = parentRow.rows,
toZA = toUpper(children.at(0).name()) < (
toUpper(children.at(-1).name())
);
return bindLR(
sortedChildNodesLR(toZA)(doc)(parentRow)
)(
updatedDocumentLR(toZA)(app)(doc)(
parentRow
)(children)
);
})() : Left("No row with children selected.");
};
// updatedDocumentLR :: Bike Application ->
// Bike Document -> Bike Row ->
// [XMLNode] -> Either IO String IO String
const updatedDocumentLR = isZA =>
// Children of selected row replaced
// by sorted copies.
app => doc => parentRow => children => nodes => {
const
uw = ObjC.unwrap,
sortedXML = nodes.map(
li => uw(li.XMLString)
)
.join("\n"),
h = [
// eslint-disable-next-line quotes
'<?xml version="1.0" encoding="UTF-8"?>',
"<html>",
"<head><meta charset=\"utf-8\"/></head>",
`<body><ul id="${doc.id()}">`
]
.join("\n"),
t = "</ul></body></html>",
nNodes = nodes.length,
n = children.length,
k = parentRow.name(),
direction = isZA ? (
"Z-A descending"
) : "A-Z ascending";
return nNodes !== n ? Left([
`Children of '${k}' not sorted.`,
"Unexpected count of sorted nodes:",
`Expected ${n}, saw ${nNodes}`
].join("\n")) : (
// Existing children deleted, and
app.delete(children),
// sorted copies imported.
doc.import({
from: `${h}\n${sortedXML}\n${t}`,
to: parentRow,
as: "bike format"
}),
Right(
[
`Sorted the ${n} children of '${k}'.`,
`(${direction})`
].join("\n")
)
);
};
// --------------------- XQUERY ----------------------
// xQueryLR :: String ->
// String -> Either String [XMLNode]
const xQueryLR = xquery =>
// Either a message or a list of XMLNode objects
// defined by application of the XQuery over the XML.
xml => {
const
uw = ObjC.unwrap,
error = $(),
node = $.NSXMLDocument.alloc
.initWithXMLStringOptionsError(
xml, 0, error
);
return bindLR(
node.isNil() ? (
Left(uw(error.localizedDescription))
) : Right(node)
)(
oNode => {
const
err = $(),
xs = oNode.objectsForXQueryError(
xquery, err
);
return xs.isNil() ? (
Left(uw(err.localizedDescription))
) : Right(uw(xs));
}
);
};
// ----------------------- JXA -----------------------
// alert :: String => String -> IO String
const alert = title =>
// Display of a given title and message.
s => {
const sa = Object.assign(
Application("System Events"), {
includeStandardAdditions: true
});
return (
sa.activate(),
sa.displayDialog(s, {
withTitle: title,
buttons: ["OK"],
defaultButton: "OK"
}),
s
);
};
// --------------------- GENERIC ---------------------
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
// Application of the function fl to the
// contents of any Left value in e, or
// the application of fr to its Right value.
fr => e => e.Left ? (
fl(e.Left)
) : fr(e.Right);
// elapsed :: Date -> String
const elapsed = start =>
// Single digit precision string showing
// rough time elapsed since start, in seconds.
((new Date() - start) / 1000)
.toFixed(1);
// toUpper :: String -> String
const toUpper = s =>
s.toLocaleUpperCase();
// MAIN ---
return main();
})();