I realise now that a “with properties” style-record pattern is already possible within the omniJS interface syntax, enabled by the standard Object.assign method in JavaScript (Safari 9 JS onwards, so available to Yosemite JS + in JXA or omniJS).
// First selected graphic, or a default new circle
shpSeln = graphics.length > 0 ? graphics[0] : (
Object.assign(
canvas.newShape(), {
shape: "Circle",
geometry: new Rect(100, 50, 50, 50),
shadowColor: null,
strokeColor: Color.RGB(0.5, 0.5, 0.5)
}
)
)
Given that this pattern:
- corresponds to the (possibly more familiar ?) pattern of the existing AppleScript/JXA interface to OmniGraffle
- enables a convenient single-record assignment of the same pre-defined style to several shapes
Perhaps it might make a good default for tutorial code on Omni-Automation ?
( I don’t have a strong view on this, pedagogically, but it is certainly a style which I will personally use, and which feels very familiar from the AppleScripting past :-)
For example, if we take the very nice selection to linked polygon examples in Sal’s sample plugin, which use a sequence of individual property assignments to set an arrow style:
[details=Series of individual in-line property assignments:]
// connect lines function
function connectObjects(startObj,endObj){
cnvs = document.windows[0].selection.canvas
aLine = cnvs.connect(startObj,endObj)
aLine.lineType = LineType.Curved
aLine.strokeColor = Color.black
aLine.shadowColor = null
aLine.strokeThickness = 4
aLine.strokeType = StrokeType.Single
aLine.headType = 'Arrow'
aLine.tailType = 'None'
}
```[/details]
We could alternatively define a style as a single record of key-value pairs, and pass that record (or alternative style records), to functions which create links and shapes:
```javascript
// STYLING AN OBJECT BY ASSIGNING A STYLE RECORD (an OG AS analogy)
// dctLineStyle :: Style for arrows between replicated shapes
var dctLineStyle = {
lineType: LineType.Curved,
strokeColor: Color.red,
shadowColor: null,
strokeThickness: 4,
strokeType: StrokeType.Single,
headType: 'Arrow',
tailType: 'None'
};
// styledLink :: Style Dictionary -> Canvas -> Shape -> Shape -> Line
function styledLink(dctStyle, oCanvas, shapeFrom, shapeTo) {
return Object.assign(
oCanvas.connect(shapeFrom, shapeTo), // New link line,
dctStyle // with assigned style
);
}

Full example of creating shapes in a 'with properties' style
(function () {
'use strict';
// STYLING AN OBJECT BY ASSIGNING A STYLE RECORD (an OG AS analogy)
// dctLineStyle :: Style for arrows between replicated shapes
var dctLineStyle = {
lineType: LineType.Curved,
strokeColor: Color.red,
shadowColor: null,
strokeThickness: 4,
strokeType: StrokeType.Single,
headType: 'Arrow',
tailType: 'None'
};
// styledLink :: Style Dictionary -> Canvas -> Shape -> Shape -> Line
function styledLink(dctStyle, oCanvas, shapeFrom, shapeTo) {
return Object.assign(
oCanvas.connect(shapeFrom, shapeTo), // New link line,
dctStyle // with assigned style
);
}
// MAIN --------------------------------------------------------------
// Number of polygon sides
var intSides = 6,
seln = document.windows[0].selection,
cnv = seln.canvas,
graphics = seln.graphics,
// First selected graphic, or a default new circle
shpSeln = graphics.length > 0 ? graphics[0] : (
Object.assign(
cnv.newShape(), {
shape: "Circle",
geometry: new Rect(100, 50, 50, 50),
shadowColor: null,
strokeColor: Color.RGB(0.5, 0.5, 0.5)
}
)
),
objGeo = shpSeln.geometry,
objC = objGeo.center,
objH = objGeo.height,
objW = objGeo.width,
// Diameter and radius of a circle intersection selection
diameter = objH * (intSides + 1),
radius = diameter / 2,
// the center of this circle.
centerX = objC.x,
centerY = objC.y + radius;
// New large background circle
var shpCircle = cnv.addShape(
'Circle',
new Rect(objGeo.midX - radius, objGeo.midY, diameter, diameter)
)
.orderBelow(shpSeln);
// A fold / reduce over an array [1 .. intSides-1]
// Initialising the reduce accumulator to the selected shape,
// and:
// 1. Updating the accumulator at each step to hold the previous shape,
// 2. creating a styled link between previous shape and new shape.
var shpFinal = enumFromTo(1, intSides - 1)
.reduce(
function (prev, n) {
var next = shpSeln.duplicateTo(
topLeftFromCenter( // X, Y of
objH, objW,
pointFromRadialVector( // next shape on circle.
centerX, centerY,
((360 / intSides) * n) - 90,
radius
)
)
);
return (
// Styled link created,
styledLink(dctLineStyle, cnv, prev, next),
// And the accumulator updated to hold this new shape
// for the next step of the fold / reduce:
next
);
},
shpSeln // Initial value of the accumulator.
);
// Link between the final shape and the original shape,
// closing the polygon.
var linkFinal = styledLink(dctLineStyle, cnv, shpFinal, shpSeln);
// (done) ...
// GENERIC FUNCTIONS --------------------------------------------------
// enumFromTo :: Int -> Int -> Maybe Int -> [Int]
function enumFromTo(m, n, step) {
var d = (step || 1) * (n >= m ? 1 : -1);
return Array.from({
length: Math.floor((n - m) / d) + 1
}, function (_, i) {
return m + (i * d)
});
}
// pointFromRadialVector :: Real -> Real -> Real -> Real -> Point
function pointFromRadialVector(xOrigin, yOrigin, angle, distance) {
function delta(f, from) {
return f(angle * Math.PI / 180) * distance + from;
}
return new Point(
delta(Math.cos, xOrigin),
delta(Math.sin, yOrigin)
);
}
// Width -> Height -> center X -> center Y -> Point
// topLeftFromCenter :: Real -> Real -> Point -> Point
function topLeftFromCenter(width, height, center) {
return new Point(
center.x - (width / 2),
center.y - (height / 2)
);
}
})();