A script which imports OPML into OmniGraffle, using a specified template and shape size to create a nested diagram.
//Ver 0.02
//Import an OPML file to a nested OG6 diagram
// (JavaScript for Automation)
// Run from Script editor with language selected at top left set to JavaScript
// 1. Choose an OPML from the file dialog which appears
// 2. Confirm
// Draws a nested diagram using a specified:
// - Template
// - Width and height for node shapes
// Select alternative template name and/or shape size in last line of script
(function (strTemplate, lstSize) {
'use strict';
// for type strings see: Apple's 'System-Declared Uniform Type Identifiers'
// if strType is omitted, files of all types will be selectable
// String -> String -> String
function pathChoice(strPrompt, strType) {
var a = Application.currentApplication();
return (a.includeStandardAdditions = true, a)
.chooseFile({
withPrompt: strPrompt,
ofType: strType
}).toString();
}
// String -> String
function textFileContents(strPath) {
return $.NSString.stringWithContentsOfFile(strPath);
}
// String -> [dct]
function jsoFromOPML(strOPML) {
// [NSXMLElement] -> [jso]
function jsoNest(lst) {
return lst.map(function (oElem) {
var lstNest = ObjC.unwrap(oElem.children),
dctNode = lstNest ? {
text: '',
nest: jsoNest(lstNest)
} : {
text: ''
};
ObjC.unwrap(oElem.attributes)
.forEach(function (oAttrib) {
var strKey = ObjC.unwrap(oAttrib.name),
strVal = ObjC.unwrap(oAttrib.stringValue),
iKey = ['text', '_note'].indexOf(strKey);
if (iKey !== 0) {
if (iKey === 1) dctNode.note = strVal;
else {
if (!dctNode.tags) dctNode.tags = {};
dctNode.tags[strKey] = strVal;
}
} else dctNode.text = strVal;
});
return dctNode;
});
}
return jsoNest(
ObjC.unwrap(
$.NSXMLDocument.alloc.initWithXMLStringOptionsError(
strOPML, 0, null
).rootElement.childAtIndex(1).children
)
);
}
// [{}] -> String -> (Integer, Integer) -> OmniGraffle6Doc
function jsoToOG6(lstJSO, strTemplate, lstSize) {
function drawTree(graphics, shp, idShape, lstChiln) {
lstChiln.forEach(function (oNode) {
var shpChild = og.Shape({
text: oNode.text,
size: lstSize,
notes: oNode.note
}),
lstNest = oNode.nest,
dctTags = oNode.tags;
// add shape to canvas graphics
graphics.push(shpChild);
var idChild = shpChild.id();
// record link for later creation through AppleScript kludge
// (see connect bug below)
lstLinks.push([idShape, idChild]);
// BUG: It seems that the connect command may be broken in the JS interface
// (trips a type error)
// og.connect(shp(), {
// to: child()
// });
if (lstNest && lstNest.length) {
drawTree(graphics, shpChild, idChild, lstNest);
}
if (dctTags) addData(shpChild, dctTags);
});
};
function addData(shp, dctUser) {
var userData = shp.userDataItems;
Object.keys(dctUser).forEach(function (k) {
userData[k].value = dctUser[k];
});
}
if (lstJSO.length > 1) {
var og = Application("OmniGraffle"),
a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a),
blnTemplate = og.availableTemplates()
.map(function (x) {
return x.split("/")[1];
})
.indexOf(strTemplate) !== -1;
// Template found ?
if (!blnTemplate) {
sa.activate();
sa.displayDialog('Template not found:\n\n\t"' + strTemplate + '\"', {
withTitle: "Import OPML into OmniGraffle"
});
return;
}
var ds = og.documents,
d = ((function () {
return ds.push(
og.Document({
template: strTemplate
})
)
})() && ds[0]),
cnv = d ? d.canvases.at(0) : undefined,
oLayout = cnv ? cnv.layoutInfo() : undefined;
if (oLayout) {
var graphics = cnv.graphics,
layer = d.layers.at(0),
lstLinks = [];
// suspended during drawing
oLayout.automaticLayout = false;
if (graphics.length) og.delete(graphics);
lstJSO.forEach(function (oNode) {
var shp = og.Shape({
text: oNode.text,
size: lstSize,
notes: oNode.note
}),
lstChiln = oNode.nest,
dctTags = oNode.tags;
graphics.push(shp);
if (lstChiln) {
drawTree(graphics, shp, shp.id(), lstChiln);
}
if (dctTags) addData(shp, dctTags);
});
// FALL BACK TO APPLESCRIPT KLUDGE EMBEDDED IN JS TO CREATE PARENT-CHILD LINKS BETWEEN SHAPES
// (The JavaScript interface to doing this appears to be broken at the moment)
sa.doShellScript(
"osascript <<AS_END 2>/dev/null\ntell application \"OmniGraffle\" to tell front canvas of front document\n" +
"set recP to {line type: straight}\n" +
lstLinks.map(function (pair) {
return 'connect shape id ' + pair[0] + ' to shape id ' + pair[1] //+
//' with properties recP';
}).join('\n') + "\nend tell\nAS_END\n"
);
// restored
oLayout.automaticLayout = true;
cnv.layout(graphics);
og.activate();
}
}
}
// MAIN
jsoToOG6(
jsoFromOPML(
textFileContents(
pathChoice('OPML file:', 'public.xml')
)
),
strTemplate, lstSize
);
})('Hierarchical', [80, 80]);