A draft script and library function experimenting with:
- A single menu of all open documents and projects
- Using JavaScript for Automation’s Library object.
selectProject-002.zip (5.1 KB)
The zip contains two scripts, one of which (selectProject-002.scpt
) uses a menu provided by a library function in the other (projectMenu.scpt)
Before you can run selectProject-002.scpt, you must first copy projectMenu.scpt to the following folder:
~/Library/Script Libraries/
(See the section on using libraries in the JavaScript for Automation release notes).
JavaScript source of the two scripts:
selectProject-002.scpt
// CHOOSE FROM MENU OF ALL OPEN DOCUMENTS AND THEIR PROJECTS
// 1. ACTIVATES CHOSEN DOCUMENT
// 2. SELECTS CHOSEN PROJECT
// (ENSURING THAT THE PROJECT AND AT LEAST ITS IMMEDIATE CHILDREN ARE VISIBLE)
// Ver 0.02 Rob Trew @complexpoint 2016-04-18
//INSTALLATION:
// This script depends on another script for the all-document menu:
// REQUIRES A 'SCRIPT LIBRARY PATH; TO CONTAIN the projectMenu.scpt library script
// WHERE 'script library path' =
// ~/Library/Script Libraries/
// OR (El Capitan) to a location specified with the OSA_LIBRARY_PATH environment variable
// e.g. OSA_LIBRARY_PATH='/opt/local/Script Libraries:/usr/local/Script Libraries'
(function () {
'use strict';
// TASKPAPER CONTEXT
function TaskPaperContext(editor, options) {
// find :: (a -> Bool) -> [a] -> Maybe a
function find(f, xs) {
for (var i = 0, lng = xs.length; i < lng; i++) {
if (f(xs[i])) return xs[i];
}
return undefined;
}
var outlineID = options.outlineID,
outline = find(
function (o) {
return o.id === outlineID;
},
Outline.getOutlines()
);
if (outline) {
var ed = OutlineEditor.getOutlineEditorForOutline(outline),
project = outline.getItemForID(options.projectID),
strProject = project.bodyString;
!ed.isDisplayed(project) && ed.forceDisplayed(project);
project.hasChildren && ed.setExpanded(project);
ed.moveSelectionToItems(
project, 0,
project, strProject.length
);
return strProject;
}
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
// raiseWin :: Window -> Maybe Application -> ()
function raiseWin(oWin, appSE) {
(appSE || Application("System Events"))
.perform(oWin.actions.byName('AXRaise'));
oWin.attributes.byName('AXParent')
.value()
.frontmost = true;
}
// find :: (a -> Bool) -> [a] -> Maybe a
function find(f, xs) {
for (var i = 0, lng = xs.length; i < lng; i++) {
if (f(xs[i])) return xs[i];
}
return undefined;
}
// MAIN
// REQUIRES A 'SCRIPT LIBRARY PATH; TO CONTAIN the projectMenu.scpt library script
// WHERE 'script library path' =
// ~/Library/Script Libraries/
// OR (El Capitan) to a location specified with the OSA_LIBRARY_PATH environment variable
// e.g. OSA_LIBRARY_PATH='/opt/local/Script Libraries:/usr/local/Script Libraries'
var lib = Library('projectMenu'),
dctIds = lib.chooseDocOrProject(
'Jump to open documents and projects');
if (dctIds) {
// Either chosen doc or active doc
var tp = Application("TaskPaper"),
se = Application("System Events"),
strPath = dctIds.path,
strProjectID = dctIds.projectID,
// Purge any null to allow for withOptions use
strOutlineID = dctIds.outlineID || undefined,
dctDoc = find(
// using predicate function:
function (dct) {
var doc = dct.doc,
maybeFile = doc.file();
return maybeFile ? maybeFile
.toString() === strPath :
(strOutlineID === doc.evaluate({
script: 'function (e) {return e.outline.id}'
}));
},
// in list:
tp.documents()
.map(function (d, i) {
return {
index: i,
doc: d
};
})
);
tp.activate();
raiseWin(
se.applicationProcesses.where({
name: 'TaskPaper'
})[0].windows.at(dctDoc.index), se
);
// SELECT SPECIFIC PROJECT IF CHOSEN IN MENU
return strProjectID ? dctDoc.doc.evaluate({
script: TaskPaperContext.toString(),
withOptions: {
outlineID: strOutlineID || undefined,
projectID: strProjectID
}
}) : undefined;
}
})();
projectMenu.scpt
// Library file
// Provides a menu of all open documents and projects
// through the chooseDocOrProject(strMenuTitle) function
// INSTALLATION:
// Save as a .scpt file (File > Save > File Format > Script) to
// ~/Library/Script Libraries/
// Ver 0.02 Rob Trew @complexpoint 2016-04-18
// chooseDocOrProject :: String -> IO Menu ->
// Maybe {outlineID: String, projectID:String, path:String}
function chooseDocOrProject(strMenuTitle) {
'use strict';
var strTitle = strMenuTitle || 'Open documents and projects';
// TASKPAPER CONTEXT
// projList :: editor -> [{oIndex:Int, pIndex:Int, depth:Int,
// name:String, oID:String, pID: String}]
function projList(editor, options) {
// projectIndex :: [{id: name: projects: [id: name: depth:]}]
// -> [{outlineIndex:Int, projectIndex:Int depth:Int
// name:String outlineID:String projID:String }]
function projectIndex(lstOutlineProjects) {
var intIndex = 0;
return lstOutlineProjects
.reduce(function (a, dctOutline, iOutline, l) {
var indexOutline = iOutline + 1;
a.push({
oIndex: indexOutline,
pIndex: undefined,
depth: 0,
name: dctOutline.name,
path: dctOutline.path,
oID: dctOutline.id,
pID: undefined
});
return a.concat(dctOutline.projects
.map(function (dctProj, iProject) {
return {
oIndex: indexOutline,
pIndex: iProject + 1,
depth: dctProj.depth,
name: dctProj.name,
path:dctProj.path,
oID: dctOutline.id,
pID: dctProj.id
};
}));
}, []);
}
// projectsInAllOpenDocs() :: () ->
// [{id: String, path: String, Name: String, projects:
// [{ idProj: String, projName:String, depth:Int }]}]
function projectsInAllOpenDocs(blnOutlineSort, blnProjSort) {
function nameSort(a, b) {
var strA = a.name.toLowerCase(),
strB = b.name.toLowerCase();
return strA < strB ? -1 : (
strA > strB ? 1 : 0
);
}
function nonSort() {
return 0;
}
var rgxPathDelim = /\//;
// ALL OUTLINES OPEN IN TASKPAPER 3
return Outline.getOutlines()
.map(function (o) {
var strPath = o.getPath();
// KEY DETAILS AND PROJECTS OF THESE OUTLINES
return {
id: o.id,
path: strPath,
name: strPath ? strPath.split(rgxPathDelim)
.pop() : '<document not saved>',
// PROJECTS OF THIS OUTLINE (ID, TEXT)
projects: o.evaluateItemPath(
'//@type=project'
)
.map(function (p) {
return {
id: p.id,
name: p.bodyContentString,
path: strPath,
depth: p.depth
};
})
// THESE PROJECTS POSSIBLY SORTED BY NAME
.sort(blnProjSort ? nameSort : nonSort)
};
})
// THESE OUTLINES POSSIBLY SORTED BY NAME
.sort(blnOutlineSort ? nameSort : nonSort);
}
return projectIndex(
projectsInAllOpenDocs(
options.sortOutlines,
options.sortProjects
)
);
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
// projectChoice :: [{oIndex:Int, pIndex:Int, oID:String,
// pID:String, depth:Int, name:String}] ->
// {idOutline:String, idProject:String}
function projectChoice(lstProj) {
// menuChoiceIDs :: String ->
// [{oIndex:Int, pIndex:Int, oID:String, pID:String}] ->
// {idOutline:String, idProject:String}
function menuChoiceIDs(strMenuItem, lstProjects) {
var lstIndex = strMenuItem.split(' ')[0].trim()
.split(' ')[0].split('.'),
iOutline = parseInt(lstIndex[0], 10),
strProjIndex = lstIndex[1],
iProj = strProjIndex.length > 0 ? parseInt(
strProjIndex, 10
) : undefined,
dctProj = find(function (x) {
return (x.oIndex === iOutline) && (
x.pIndex === iProj
);
}, lstProjects);
return dctProj ? {
outlineID: dctProj.oID,
projectID: dctProj.pID,
path: dctProj.path
} : undefined;
}
// find :: (a -> Bool) -> [a] -> Maybe a
function find(f, xs) {
for (var i = 0, lng = xs.length; i < lng; i++) {
if (f(xs[i])) return xs[i];
}
return undefined;
}
if (lstProj && (lstProj.length > 0)) {
var lstMenu = lstProj.map(function (dctProj) {
var strProjNum = dctProj.pIndex || ' ';
return Array(dctProj.depth + 1)
.join('\t') +
dctProj.oIndex + '.' + strProjNum +
' ' + dctProj.name;
}),
ui = Application("com.apple.systemuiserver"),
sa = (ui.includeStandardAdditions = true, ui),
choice = sa.activate() && sa.chooseFromList(lstMenu, {
withTitle: strTitle,
withPrompt: 'Choose document or project:',
defaultItems: lstMenu[0],
okButtonName: 'OK',
cancelButtonName: 'Cancel',
multipleSelectionsAllowed: false,
emptySelectionAllowed: true
});
return choice ? menuChoiceIDs(
choice[0], lstProj
) : undefined;
}
}
// MAIN
var ds = Application("TaskPaper")
.documents,
d = ds.length ? ds[0] : undefined,
lstProj = d ? d.evaluate({
script: projList.toString(),
withOptions: {
sortOutlines: true,
sortProjects: false
}
}) : undefined,
dctProj = lstProj ? projectChoice(lstProj) : undefined;
return dctProj;
};