├── utilities ├── FT2CSV.scpt ├── CopyFTasTP.scpt ├── CopyTPasFT.scpt ├── ReadProps.scpt ├── lastLink.scpt ├── MakeTestData.scpt ├── ToggleEmpty.scpt ├── listCommands.scpt ├── Italic.kmmacros.zip ├── SafariClippings.scpt ├── MakeTestData-002.scpt ├── OpenFTDocinMarked.scpt ├── FTDueAppTimedSprint.scpt ├── GetDisplayResolution.scpt ├── SelectedMailToFTInbox.scpt ├── OpeniThoughtsMapinMarked.scpt ├── FTDueAppTimedSprint.applescript ├── GetDisplayResolution.applescript ├── CopyTPasFT.applescript ├── CopyFTasTP.applescript ├── emotime.sh ├── emomoon.sh ├── Italic.kmmacros ├── SelectedMailToFTInbox.applescript ├── OpeniThoughtsMapinMarked.applescript ├── OpenFTDocinMarked.applescript ├── FT2CSV.applescript ├── cliptextcolors.sh ├── SafariClippings.applescript └── MakeTestData.applescript ├── copy as Markdown ├── KMAction.png ├── safariCopyAsHTML.scpt ├── safariCopyAsMDjs.scpt ├── SaveAsJavasScript-scpt.png ├── readme.md ├── safariCopyAsHTML.applescript ├── safariCopyAsMDjs.applescript └── Copy as Markdown.kmmacros ├── structure editing ├── MoveSelnToSection.scpt ├── MoveSelnToSectionJS.scpt ├── TP3MoveSelnToSection.scpt ├── MoveSelnToSection.applescript └── MoveSelnToSectionJS.applescript ├── ftdoc3 url scheme ├── OpenFT3DocAtLine.app.zip ├── Copy ft3doc URL for selected line in FoldingText 3.kmmacros.zip ├── Source and info.plist for OpenFT3DocAtLine │ ├── OpenFT3DocAtLine.applescript │ └── Info.plist └── README.md ├── Yosemite Javascript XQuery demo ├── QueryMenu.png ├── xquery01.png ├── LinkingBackToSource.png ├── GroupedByDaysAndTimes.png ├── SampleFolder │ ├── sortByStringLength.scpt │ └── workProject.txt ├── readme.md └── QueryAFolderOfOPMLFiles.applescript ├── filtering in the editor ├── ArchiveTagsMenu.scpt ├── FilterOnTagsMenu.scpt ├── FilterOnAllSelectedTags.scpt ├── tagsToggleHideFocus-js.scpt ├── tagsToggleHideFocus-minjs.scpt ├── tagsToggleHideFocus-js.applescript ├── tagsToggleHideFocus-minjs.applescript ├── FilterOnTagsMenu.applescript ├── ArchiveTagsMenu.applescript └── FilterOnAllSelectedTags.applescript ├── import and export formats ├── FT2ImportOPML.scpt ├── FTSaveAsOPML-js.scpt ├── FT2ImportOPML.applescript └── FTSaveAsOPML-js.applescript ├── toggle hide done items.ftplugin ├── unfocus.scpt ├── package.json ├── HideDone-002.applescript ├── spec.js └── main.js ├── ftdoc url scheme and FTCopyAsURL ├── FTCopyAsURL.scpt ├── Source and info.plist for OpenFTDocAtLine │ ├── Info.plist │ └── OpenFTDocAtLine.applescript ├── FTCopyAsURL.applescript └── README.md ├── perspectives.ftplugin ├── SingleFilePerspectives.scpt ├── package.json ├── Example TXTQUERY.sh from KM.kmmacros ├── README.md └── ViewMenu.json ├── tp3doc url scheme and TP3CopyAsURL ├── TP3CopyAsURL.scpt ├── Source and info.plist for OpenTP3DocAtLine │ ├── Info.plist │ └── OpenTP3DocAtLine.applescript ├── TP3CopyAsURL.applescript └── README.md ├── relative dates and date adjustments.ftplugin ├── E Adjust.png ├── C AfterEntry.png ├── F AfterAdjust.png ├── D AutoSelectDateTime.png ├── A PlaceCursorInOrNextToTag.png ├── B InformalPhraseTranslatedLive.png ├── package.json ├── style.less ├── README.md ├── TaskPaper.textexpander ├── FoldingTextOLD.textexpander └── main.js ├── links between plain text and Reminders ├── CopyReminderAsMD.png ├── ReminderUpdated.png ├── x-apple-reminder.png ├── CopyReminderAsLink.png ├── CopyReminderAsLink.scpt ├── SelectLineWithAlert.png ├── FTMakeOrUpdateReminder.png ├── LinkBackFromReminder.png ├── PlainTextToReminders.png ├── FTMakeOrUpdateReminder.scpt ├── Icon and time normalized.png ├── CopyReminderAsLink.applescript ├── CopyReminderAsTaskPaperorFT.png ├── Edits to date and priority.png ├── CopyReminderAsTaskPaperOrFT.scpt ├── FTToggleDoneUpdateReminders.scpt ├── FTMakeOrUpdateReminder.applescript ├── FTPullDetailsFROMLinkedReminder.scpt ├── CopyReminderAsTaskPaperOrFT.applescript ├── FTToggleDoneUpdateReminders.applescript ├── FTPullDetailsFROMLinkedReminder.applescript ├── FTMakeOrUpdateReminder.workflow │ └── Contents │ │ ├── QuickLook │ │ └── Thumbnail.png │ │ └── Info.plist └── README.md ├── Copy paste and Open Save As between FT MD and FTML ├── FTML2MD.scpt ├── FTML2MD-006.scpt ├── FTML2MD-007.scpt ├── storeQuery.scpt ├── FTSaveAsFTML.scpt ├── FTTextPasteAsFTML.scpt ├── archived BML versions │ ├── BML2MD.scpt │ └── FTSaveAsBML.scpt └── readme.md ├── smalltime.ftplugin └── package.json ├── reminder tools.ftplugin ├── package.json ├── updatelink.js └── main.js └── README.md /utilities/FT2CSV.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/FT2CSV.scpt -------------------------------------------------------------------------------- /utilities/CopyFTasTP.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/CopyFTasTP.scpt -------------------------------------------------------------------------------- /utilities/CopyTPasFT.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/CopyTPasFT.scpt -------------------------------------------------------------------------------- /utilities/ReadProps.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/ReadProps.scpt -------------------------------------------------------------------------------- /utilities/lastLink.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/lastLink.scpt -------------------------------------------------------------------------------- /utilities/MakeTestData.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/MakeTestData.scpt -------------------------------------------------------------------------------- /utilities/ToggleEmpty.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/ToggleEmpty.scpt -------------------------------------------------------------------------------- /utilities/listCommands.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/listCommands.scpt -------------------------------------------------------------------------------- /copy as Markdown/KMAction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/copy as Markdown/KMAction.png -------------------------------------------------------------------------------- /utilities/Italic.kmmacros.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/Italic.kmmacros.zip -------------------------------------------------------------------------------- /utilities/SafariClippings.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/SafariClippings.scpt -------------------------------------------------------------------------------- /utilities/MakeTestData-002.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/MakeTestData-002.scpt -------------------------------------------------------------------------------- /utilities/OpenFTDocinMarked.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/OpenFTDocinMarked.scpt -------------------------------------------------------------------------------- /utilities/FTDueAppTimedSprint.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/FTDueAppTimedSprint.scpt -------------------------------------------------------------------------------- /utilities/GetDisplayResolution.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/GetDisplayResolution.scpt -------------------------------------------------------------------------------- /utilities/SelectedMailToFTInbox.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/SelectedMailToFTInbox.scpt -------------------------------------------------------------------------------- /copy as Markdown/safariCopyAsHTML.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/copy as Markdown/safariCopyAsHTML.scpt -------------------------------------------------------------------------------- /copy as Markdown/safariCopyAsMDjs.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/copy as Markdown/safariCopyAsMDjs.scpt -------------------------------------------------------------------------------- /structure editing/MoveSelnToSection.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/structure editing/MoveSelnToSection.scpt -------------------------------------------------------------------------------- /utilities/OpeniThoughtsMapinMarked.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/OpeniThoughtsMapinMarked.scpt -------------------------------------------------------------------------------- /ftdoc3 url scheme/OpenFT3DocAtLine.app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/ftdoc3 url scheme/OpenFT3DocAtLine.app.zip -------------------------------------------------------------------------------- /structure editing/MoveSelnToSectionJS.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/structure editing/MoveSelnToSectionJS.scpt -------------------------------------------------------------------------------- /utilities/FTDueAppTimedSprint.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/utilities/FTDueAppTimedSprint.applescript -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/QueryMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Yosemite Javascript XQuery demo/QueryMenu.png -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/xquery01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Yosemite Javascript XQuery demo/xquery01.png -------------------------------------------------------------------------------- /copy as Markdown/SaveAsJavasScript-scpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/copy as Markdown/SaveAsJavasScript-scpt.png -------------------------------------------------------------------------------- /filtering in the editor/ArchiveTagsMenu.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/filtering in the editor/ArchiveTagsMenu.scpt -------------------------------------------------------------------------------- /filtering in the editor/FilterOnTagsMenu.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/filtering in the editor/FilterOnTagsMenu.scpt -------------------------------------------------------------------------------- /import and export formats/FT2ImportOPML.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/import and export formats/FT2ImportOPML.scpt -------------------------------------------------------------------------------- /structure editing/TP3MoveSelnToSection.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/structure editing/TP3MoveSelnToSection.scpt -------------------------------------------------------------------------------- /toggle hide done items.ftplugin/unfocus.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/toggle hide done items.ftplugin/unfocus.scpt -------------------------------------------------------------------------------- /import and export formats/FTSaveAsOPML-js.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/import and export formats/FTSaveAsOPML-js.scpt -------------------------------------------------------------------------------- /structure editing/MoveSelnToSection.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/structure editing/MoveSelnToSection.applescript -------------------------------------------------------------------------------- /ftdoc url scheme and FTCopyAsURL/FTCopyAsURL.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/ftdoc url scheme and FTCopyAsURL/FTCopyAsURL.scpt -------------------------------------------------------------------------------- /perspectives.ftplugin/SingleFilePerspectives.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/perspectives.ftplugin/SingleFilePerspectives.scpt -------------------------------------------------------------------------------- /filtering in the editor/FilterOnAllSelectedTags.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/filtering in the editor/FilterOnAllSelectedTags.scpt -------------------------------------------------------------------------------- /filtering in the editor/tagsToggleHideFocus-js.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/filtering in the editor/tagsToggleHideFocus-js.scpt -------------------------------------------------------------------------------- /import and export formats/FT2ImportOPML.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/import and export formats/FT2ImportOPML.applescript -------------------------------------------------------------------------------- /tp3doc url scheme and TP3CopyAsURL/TP3CopyAsURL.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/tp3doc url scheme and TP3CopyAsURL/TP3CopyAsURL.scpt -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/LinkingBackToSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Yosemite Javascript XQuery demo/LinkingBackToSource.png -------------------------------------------------------------------------------- /filtering in the editor/tagsToggleHideFocus-minjs.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/filtering in the editor/tagsToggleHideFocus-minjs.scpt -------------------------------------------------------------------------------- /import and export formats/FTSaveAsOPML-js.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/import and export formats/FTSaveAsOPML-js.applescript -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/GroupedByDaysAndTimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Yosemite Javascript XQuery demo/GroupedByDaysAndTimes.png -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/E Adjust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/relative dates and date adjustments.ftplugin/E Adjust.png -------------------------------------------------------------------------------- /filtering in the editor/tagsToggleHideFocus-js.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/filtering in the editor/tagsToggleHideFocus-js.applescript -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsMD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsMD.png -------------------------------------------------------------------------------- /links between plain text and Reminders/ReminderUpdated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/ReminderUpdated.png -------------------------------------------------------------------------------- /links between plain text and Reminders/x-apple-reminder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/x-apple-reminder.png -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsLink.png -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsLink.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsLink.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/SelectLineWithAlert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/SelectLineWithAlert.png -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/C AfterEntry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/relative dates and date adjustments.ftplugin/C AfterEntry.png -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/F AfterAdjust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/relative dates and date adjustments.ftplugin/F AfterAdjust.png -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/FTML2MD.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/FTML2MD.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/FTMakeOrUpdateReminder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTMakeOrUpdateReminder.png -------------------------------------------------------------------------------- /links between plain text and Reminders/LinkBackFromReminder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/LinkBackFromReminder.png -------------------------------------------------------------------------------- /links between plain text and Reminders/PlainTextToReminders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/PlainTextToReminders.png -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/FTML2MD-006.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/FTML2MD-006.scpt -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/FTML2MD-007.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/FTML2MD-007.scpt -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/storeQuery.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/storeQuery.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/FTMakeOrUpdateReminder.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTMakeOrUpdateReminder.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/Icon and time normalized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/Icon and time normalized.png -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/FTSaveAsFTML.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/FTSaveAsFTML.scpt -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/SampleFolder/sortByStringLength.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Yosemite Javascript XQuery demo/SampleFolder/sortByStringLength.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsLink.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsLink.applescript -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsTaskPaperorFT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsTaskPaperorFT.png -------------------------------------------------------------------------------- /links between plain text and Reminders/Edits to date and priority.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/Edits to date and priority.png -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/D AutoSelectDateTime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/relative dates and date adjustments.ftplugin/D AutoSelectDateTime.png -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsTaskPaperOrFT.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsTaskPaperOrFT.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/FTToggleDoneUpdateReminders.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTToggleDoneUpdateReminders.scpt -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/FTTextPasteAsFTML.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/FTTextPasteAsFTML.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/FTMakeOrUpdateReminder.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTMakeOrUpdateReminder.applescript -------------------------------------------------------------------------------- /links between plain text and Reminders/FTPullDetailsFROMLinkedReminder.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTPullDetailsFROMLinkedReminder.scpt -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/A PlaceCursorInOrNextToTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/relative dates and date adjustments.ftplugin/A PlaceCursorInOrNextToTag.png -------------------------------------------------------------------------------- /links between plain text and Reminders/CopyReminderAsTaskPaperOrFT.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/CopyReminderAsTaskPaperOrFT.applescript -------------------------------------------------------------------------------- /links between plain text and Reminders/FTToggleDoneUpdateReminders.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTToggleDoneUpdateReminders.applescript -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/B InformalPhraseTranslatedLive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/relative dates and date adjustments.ftplugin/B InformalPhraseTranslatedLive.png -------------------------------------------------------------------------------- /ftdoc3 url scheme/Copy ft3doc URL for selected line in FoldingText 3.kmmacros.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/ftdoc3 url scheme/Copy ft3doc URL for selected line in FoldingText 3.kmmacros.zip -------------------------------------------------------------------------------- /links between plain text and Reminders/FTPullDetailsFROMLinkedReminder.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTPullDetailsFROMLinkedReminder.applescript -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/archived BML versions/BML2MD.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/archived BML versions/BML2MD.scpt -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/archived BML versions/FTSaveAsBML.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/Copy paste and Open Save As between FT MD and FTML/archived BML versions/FTSaveAsBML.scpt -------------------------------------------------------------------------------- /links between plain text and Reminders/FTMakeOrUpdateReminder.workflow/Contents/QuickLook/Thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTrew/txtquery-tools/HEAD/links between plain text and Reminders/FTMakeOrUpdateReminder.workflow/Contents/QuickLook/Thumbnail.png -------------------------------------------------------------------------------- /links between plain text and Reminders/FTMakeOrUpdateReminder.workflow/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | FTMakeOrUpdateReminder 7 | 8 | 9 | -------------------------------------------------------------------------------- /perspectives.ftplugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Custom perspectives", 3 | "version": "1.2.0", 4 | "description": "Plain text reports grouped and sorted by @tag(values)", 5 | "engines": { 6 | "foldingtext": ">=2.0.0", 7 | "taskpaper": ">=3.0.0" 8 | }, 9 | "homepage": "https://github.com/RobTrew/tree-tools" 10 | } 11 | 12 | -------------------------------------------------------------------------------- /toggle hide done items.ftplugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Toggle Hide Done Items", 3 | "version": "1.0.2", 4 | "description": "Toggles visibility of @done items on and off. See Edit > Run Command", 5 | "homepage": "https://github.com/RobTrew/txtquery-tools", 6 | "engines": { 7 | "foldingtext": ">2.0.0", 8 | "taskpaper": ">3.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Live translation of relative and informal dates to ISO", 3 | "version": "1.3.1", 4 | "description": "Uses JK Panel", 5 | "engines": { 6 | "foldingtext": ">=2.0.0", 7 | "taskpaper": ">=3.0.0" 8 | }, 9 | "homepage": "https://github.com/RobTrew/tree-tools" 10 | } 11 | 12 | -------------------------------------------------------------------------------- /smalltime.ftplugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Exports informal/relative date phrase translation functions", 3 | "version": "1.3.1", 4 | "description": "Library functions used by other plugins", 5 | "engines": { 6 | "foldingtext": ">=2.0.0", 7 | "taskpaper": ">=3.0.0" 8 | }, 9 | "homepage": "https://github.com/RobTrew/tree-tools" 10 | } 11 | 12 | -------------------------------------------------------------------------------- /reminder tools.ftplugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Functions for sharing data with Reminders.app entries", 3 | "version": "1.3.1", 4 | "description": "Manages links and date tag reading and writing", 5 | "engines": { 6 | "foldingtext": ">=2.0.0", 7 | "taskpaper": ">=3.0.0" 8 | }, 9 | "homepage": "https://github.com/RobTrew/tree-tools" 10 | } 11 | 12 | -------------------------------------------------------------------------------- /reminder tools.ftplugin/updatelink.js: -------------------------------------------------------------------------------- 1 | function(editor, options) { 2 | 'use strict'; 3 | var tree = editor.tree(), 4 | node = editor.selectedRange().startNode, strText=node.text(), strUUID=options.uuid, rgxLink, strUpdated; 5 | rgxLink= new RegExp('\\[.*\\](' + strUUID + ')'); 6 | debugger; 7 | strUpdated = strText.replace(rgxLink, '[' + options.linkname + '](' +strUUID + ')'); 8 | tree.beginUpdates(); 9 | node.setText(strUpdated); 10 | tree.endUpdates(); 11 | tree.ensureClassified(); 12 | } 13 | -------------------------------------------------------------------------------- /ftdoc3 url scheme/Source and info.plist for OpenFT3DocAtLine/OpenFT3DocAtLine.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Register and handle ft3doc:// url scheme" 2 | property pVer : "0.01" 3 | property pAuthor : "Rob Trew @complexpoint" 4 | property pDescription : " 5 | 6 | Use in conjunction with Atom > Edit > Copy Path (Ctrl Shift C) to copy 7 | a URL which opens the specified FoldingText 3 document, selecting the line at which the path was copied. 8 | https://github.com/FoldingText/foldingtext-for-atom 9 | 10 | " 11 | on open location strFT3URL 12 | do shell script "/usr/local/bin/atom file" & (text 7 thru -1 of strFT3URL) 13 | end open location 14 | -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/style.less: -------------------------------------------------------------------------------- 1 | // Jamie Kowalki's LESS code adapted for a date panel 2 | 3 | @panel-width: 800px; 4 | @panel-height: 1.9rem; 5 | 6 | .RTDatePanel { 7 | position: fixed; 8 | z-index: 10; 9 | left: 25px; 10 | top: 8px; 11 | width: @panel-width; 12 | height: @panel-height; 13 | background-color: white; 14 | border-radius: 6px; 15 | box-shadow: 1px 3px 10px #999; 16 | } 17 | .RTDatePanel input { 18 | margin: 5px 10px; 19 | width: @panel-width - 20px; 20 | height: @panel-height - 10px; 21 | background: inherit; 22 | border: none; 23 | font-size: 1em; 24 | font-family: inherit; 25 | } 26 | .RTDatePanel input:focus { 27 | outline-width: 0; 28 | } 29 | -------------------------------------------------------------------------------- /utilities/GetDisplayResolution.applescript: -------------------------------------------------------------------------------- 1 | -- properties persist across sessions in compiled .scpt files 2 | -- so subsequent runs will be faster 3 | property pX : missing value 4 | property pY : missing value 5 | 6 | 7 | on run {} 8 | displayResoln() 9 | end run 10 | 11 | on displayResoln() 12 | if (pX is missing value) or (pY is missing value) then 13 | set {dlm, my text item delimiters} to {my text item delimiters, "Resolution"} 14 | set lstDisplays to text items of (do shell script "system_profiler SPDisplaysDataType") 15 | 16 | repeat with i from 2 to length of lstDisplays 17 | set strLine to item i of lstDisplays 18 | if strLine contains "Main Display: Yes" then exit repeat 19 | end repeat 20 | set my text item delimiters to space 21 | set lstParts to text items of strLine 22 | set my text item delimiters to dlm 23 | set {strX, strY} to {item 2, item 4} of lstParts 24 | set {pX, pY} to {strX as integer, strY as integer} 25 | end if 26 | return {pX , pY} 27 | end displayResoln -------------------------------------------------------------------------------- /Copy paste and Open Save As between FT MD and FTML/readme.md: -------------------------------------------------------------------------------- 1 | #### FoldingText MD ⇄ FoldingText for Atom HTML (.ftml) outlines 2 | 3 | A couple of **Yosemite Javascript for Applications** Scripts which can be run from Script Editor or assigned to keystrokes with something like Keyboard Maestro. 4 | 5 | 1. [FoldingText / MD --> FTML outline](./FTSaveAsFTML.applescript) 6 | 3. [FTML outline --> FoldingText MD format](./FTML2MD.applescript) 7 | 8 | Both can be used either for clipboard copy paste or for file system Open and Save As 9 | 10 | (Probably makes sense to save two versions of each, adjusting the options switches at the top of each script to 0 or 1 values to select between clipboard and file operations) 11 | 12 | Note: Ver 12 and above of the FoldingText Save As FTML script use CommonMark (rather than FoldingText) line type names. 13 | 14 | **Installation** - paste the code into Yosemite Script Editor, setting the editor language option to **Javascript** (pull-down option at top left), and save as a compiled .scpt file 15 | -------------------------------------------------------------------------------- /toggle hide done items.ftplugin/HideDone-002.applescript: -------------------------------------------------------------------------------- 1 | -- ver 1.0.2 toggles visibility of @done without losing any existing focus property pstrJS : " 2 | 3 | function(editor) { 4 | var strActivePath = editor.nodePath().nodePathString, 5 | lstNodes, 6 | strExceptDone = ' except //@done', lngChars=strExceptDone.length, 7 | strToggledPath, lngStart; 8 | 9 | switch (strActivePath) { 10 | case '///*': 11 | strToggledPath = '//not @done'; 12 | break; 13 | case '//not @done': 14 | strToggledPath = '///*'; 15 | break; 16 | default : 17 | lngStart = strActivePath.length-lngChars; 18 | if (strActivePath.indexOf(' except //@done', lngStart) == -1) 19 | strToggledPath = strActivePath + strExceptDone; 20 | else 21 | strToggledPath = strActivePath.substring(0, lngStart); 22 | break; 23 | } 24 | editor.setNodePath(strToggledPath); 25 | } 26 | 27 | " tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then tell item 1 of lstDocs to (evaluate script pstrJS) end if end tell -------------------------------------------------------------------------------- /toggle hide done items.ftplugin/spec.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | describe('toggle hide done items', function () { 5 | var MarkdownTaxonomy = require('ft/taxonomy/markdowntaxonomy').MarkdownTaxonomy, 6 | Taxonomies = require('ft/core/taxonomies'), 7 | Editor = require('ft/editor/editor').Editor, 8 | taxonomy = Taxonomies.taxonomy({ 9 | foldingtext: true, 10 | multimarkdown: true, 11 | gitmarkdown: true, 12 | criticMarkup: true 13 | }, 'markdown'), 14 | editor; 15 | 16 | beforeEach(function () { 17 | editor = new Editor('', taxonomy); 18 | }); 19 | 20 | afterEach(function () { 21 | editor.removeAndCleanupForCollection(); 22 | }); 23 | 24 | it('should toggle path between //not @done and ///*', function () { 25 | editor.setNodePath('///*'); 26 | editor.performCommand('toggle hide done items'); 27 | expect(editor.nodePath().nodePathString).toEqual('//not @done'); 28 | editor.performCommand('toggle hide done items'); 29 | expect(editor.nodePath().nodePathString).toEqual('///*'); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /toggle hide done items.ftplugin/main.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var Extensions = require('ft/core/extensions').Extensions; 3 | 4 | Extensions.addCommand({ 5 | name: 'toggle hide done items', 6 | description: 'Toggles between hiding @done items and showing all lines', 7 | performCommand: function(editor) { 8 | var strActivePath = editor.nodePath().nodePathString, 9 | lstNodes, 10 | strExceptDone = ' except //@done', 11 | lngChars=strExceptDone.length, 12 | strToggledPath, lngStart; 13 | 14 | switch (strActivePath) { 15 | case '///*': 16 | strToggledPath = '//not @done'; 17 | break; 18 | case '//not @done': 19 | strToggledPath = '///*'; 20 | break; 21 | default : 22 | lngStart = strActivePath.length-lngChars; 23 | if (strActivePath.indexOf( 24 | ' except //@done', lngStart) == -1) 25 | strToggledPath = strActivePath + strExceptDone; 26 | else 27 | strToggledPath = strActivePath.substring(0, lngStart); 28 | break; 29 | } 30 | editor.setNodePath(strToggledPath); 31 | } 32 | }); 33 | 34 | Extensions.addInit(function (editor) { 35 | editor.addKeyMap({ 36 | 'Cmd-Ctrl-Alt-H':'toggle hide done items' 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /copy as Markdown/readme.md: -------------------------------------------------------------------------------- 1 | ## Copy as MD for Safari (Yosemite) 2 | 3 | ### Use 4 | - Select some paragraphs in Safari, 5 | - run the script from a keyboard assignment, 6 | - and paste the paragraphs somewhere as [Markdown-formatted](http://daringfireball.net/projects/markdown/syntax) text. 7 | 8 | ### Requirements 9 | 1. OS X 10.10 (Yosemite) 10 | 2. [safariCopyAsMDjs.applescript](./safariCopyAsMDjs.applescript) 11 | (Written in Yosemite JXA - Javascript for Automation) 12 | 3. A copy of the [html2text.py](https://github.com/aaronsw/html2text) Python text file 13 | (place it in the same folder as [safariCopyAsMDjs.applescript](./safariCopyAsMDjs.applescript)) 14 | 15 | ### Installation 16 | - Use OS X Yosemite Script Editor to save a copy of [safariCopyAsMDjs.applescript](./safariCopyAsMDjs.applescript) as a compiled Javascript .scpt file 17 | 18 | ![](./SaveAsJavasScript-scpt.png) 19 | 20 | - Place a copy of the [html2text.py](https://github.com/aaronsw/html2text) text file in the same folder as this script. 21 | 22 | - Assign a keyboard shortcut to safariCopyAsMDjs.scpt 23 | - with something like [Keyboard Maestro](http://www.keyboardmaestro.com) 24 | 25 | ![](./KMAction.png) 26 | 27 | - or [Fastscripts](http://www.red-sweater.com/fastscripts/) 28 | -------------------------------------------------------------------------------- /ftdoc url scheme and FTCopyAsURL/Source and info.plist for OpenFTDocAtLine/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleAllowMixedLocalizations 6 | 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | applet 11 | CFBundleIconFile 12 | applet 13 | CFBundleIdentifier 14 | com.complexpoint.OpenFTDocAtLine 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | OpenFTDocAtLine 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | aplt 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleURLName 27 | Links to FoldingText docs 28 | CFBundleURLSchemes 29 | 30 | ftdoc 31 | 32 | 33 | 34 | LSMinimumSystemVersionByArchitecture 35 | 36 | x86_64 37 | 10.6 38 | 39 | LSRequiresCarbon 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tp3doc url scheme and TP3CopyAsURL/Source and info.plist for OpenTP3DocAtLine/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleAllowMixedLocalizations 6 | 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | applet 11 | CFBundleIconFile 12 | applet 13 | CFBundleIdentifier 14 | com.complexpoint.OpenTP3DocAtLine 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | OpenTP3DocAtLine 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | aplt 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleURLName 27 | Links to TaskPaper3 docs 28 | CFBundleURLSchemes 29 | 30 | tp3doc 31 | 32 | 33 | 34 | LSMinimumSystemVersionByArchitecture 35 | 36 | x86_64 37 | 10.6 38 | 39 | LSRequiresCarbon 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | txtquery-tools 2 | ========== 3 | 4 | Various scripts for generating grouped, sorted and hyperlinked custom reports and perspectives from _key@value_ tagged text files in the [FoldingText](http://www.foldingtext.com) and [TaskPaper](http://www.hogbaysoftware.com) formats. 5 | 6 | License for the code in this repository: 7 | 8 | Copyright © 2014 Robin Trew 9 | -- 10 | 11 | Permission is hereby granted, free of charge, 12 | to any person obtaining a copy of this software 13 | and associated documentation files (the "Software"), 14 | to deal in the Software without restriction, 15 | including without limitation the rights to use, copy, 16 | modify, merge, publish, distribute, sublicense, 17 | and/or sell copies of the Software, and to permit persons 18 | to whom the Software is furnished to do so, 19 | subject to the following conditions: 20 | 21 | ******* 22 | The above copyright notice and this permission notice 23 | shall be included in ALL copies 24 | or substantial portions of the Software. 25 | ******* 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 29 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 30 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 31 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 32 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 33 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | -------------------------------------------------------------------------------- /utilities/CopyTPasFT.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Copy from TaskPaper in FOLDINGTEXT format" property pVer : "0.1" property pAuthor : "Rob Trew @complexpoint on Twitter" property pRepo : "https://github.com/RobTrew/txtquery-tools" property pLicense : "MIT" property pstrJS : " 2 | 3 | 4 | function(editor, options) { 5 | 6 | // HOW MANY PRECEDING TABS OR HASHES FOR THIS LINE IN FT ? 7 | function FTPrefix(oNode) { 8 | var oParent=oNode.parent, lngLevel=1, lngProjLevel=1, blnFound=false, 9 | strType=oNode.type(), blnProj = (strType == 'project'), strPrefix; 10 | 11 | blnFound = blnProj; 12 | while (oParent) { 13 | lngLevel++; 14 | if (blnFound) lngProjLevel ++; 15 | else blnFound = (oParent.type() == 'project'); 16 | oParent = oParent.parent; 17 | } 18 | if (blnProj) strPrefix = '\\n' + Array(lngLevel).join('#') + ' '; 19 | else strPrefix = Array(lngLevel-lngProjLevel).join('\\t'); 20 | 21 | return strPrefix; 22 | } 23 | 24 | // GET THE SELECTED LINES 25 | var lstNodes = editor.selectedRange().nodesInRange(), 26 | lstLines=[], varNode, strLine, rgxEndColon = /^(.*):(.*?)$/; 27 | 28 | // AND GIVE AN FT PREFIX (HASHES OR TABS) TO EACH ONE 29 | lstNodes.forEach(function (oNode) { 30 | strLine = oNode.line().trim(); 31 | 32 | // REMOVING THE COLON FROM PROJECTS (BUT LEAVING TRAILLING TAGS) 33 | if (oNode.type() == 'project') 34 | strLine=strLine.replace(rgxEndColon, '$1$2'); 35 | lstLines.push([FTPrefix(oNode),strLine].join('')); 36 | }); 37 | 38 | return lstLines.join('\\n'); 39 | } 40 | 41 | " on run set varResult to missing value tell application "TaskPaper" set lstDocs to documents if lstDocs ≠ {} then tell item 1 of lstDocs to set varResult to (evaluate script pstrJS) set the clipboard to varResult end if end tell return varResult end run -------------------------------------------------------------------------------- /ftdoc3 url scheme/Source and info.plist for OpenFT3DocAtLine/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleAllowMixedLocalizations 6 | 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | applet 11 | CFBundleIconFile 12 | applet 13 | CFBundleIdentifier 14 | com.complexpoint.OpenFT3DocAtLine 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | OpenFTDoc3AtLine 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | aplt 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleURLName 27 | Links to FoldingText3 docs 28 | CFBundleURLSchemes 29 | 30 | ft3doc 31 | 32 | 33 | 34 | LSMinimumSystemVersionByArchitecture 35 | 36 | x86_64 37 | 10.6 38 | 39 | LSRequiresCarbon 40 | 41 | WindowState 42 | 43 | bundleDividerCollapsed 44 | 45 | bundlePositionOfDivider 46 | 0.0 47 | dividerCollapsed 48 | 49 | eventLogLevel 50 | 2 51 | name 52 | ScriptWindowState 53 | positionOfDivider 54 | 1183 55 | savedFrame 56 | 211 53 952 1434 0 0 2560 1577 57 | selectedTab 58 | result 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /utilities/CopyFTasTP.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Copy from FoldingText in TASKPAPER format" property pVer : "0.1" property pAuthor : "Rob Trew @complexpoint on Twitter" property pRepo : "https://github.com/RobTrew/txtquery-tools" property pLicense : "MIT" property pstrJS : " 2 | 3 | 4 | function(editor, options) { 5 | 6 | // HOW MANY TABS WILL TASKPAPER NEED FOR THIS LINE ? 7 | function nestLevel(oNode) { 8 | var lngLevel=0; 9 | while (oNode.parent) { 10 | lngLevel++; 11 | oNode = oNode.parent; 12 | } 13 | return lngLevel; 14 | } 15 | 16 | // GET THE SELECTED LINES 17 | var lstNodes = editor.selectedRange().nodesInRange(), 18 | lstLines=[], varNode, strLine, dctTags, lstTags, varTag, strValue; 19 | 20 | // AND ADJUST TAB PREFIXES AND COLON SUFFIXES/INFIXES (BEFORE TAGS) 21 | lstNodes.forEach(function (varNode) { 22 | if (varNode.type() !== 'heading') 23 | strLine = varNode.line().trim(); 24 | else { 25 | // INSERT A COLON (BEFORE ANY TAGS) TO MARK EACH HASH HEADING AS A TP PROJECT 26 | strLine = varNode.text() + ': '; 27 | dctTags = varNode.tags(); lstTags = []; 28 | for (varTag in dctTags) { 29 | strValue = dctTags[varTag]; 30 | if (strValue) lstTags.push(['@',varTag,'(',strValue,')'].join('')); 31 | else lstTags.push('@' + varTag); 32 | } 33 | if (lstTags.length) strLine += lstTags.join(' '); 34 | } 35 | 36 | // PREPEND EACH LINE WITH THE NUMBER OF TABS THAT MATCHES THE NESTING LEVEL 37 | lstLines.push([Array(nestLevel(varNode)).join('\\t'), strLine].join('')); 38 | }); 39 | return lstLines.join('\\n'); 40 | } 41 | 42 | " on run set varResult to missing value tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then tell item 1 of lstDocs to set varResult to (evaluate script pstrJS) set the clipboard to varResult end if end tell return varResult end run -------------------------------------------------------------------------------- /utilities/emotime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: Rob Trew, https://github.com/RobTrew/txtquery-tools 3 | # Ver: 0.2 4 | # Gets emoji clockface (stdout|clipboard) with specified time (to nearest preceding half hour) 5 | # Call at command line with *on or two* integer parameters ($1 for hours, $2 for minutes) 6 | # e.g. chmod +x emotime.sh; emotime.sh 16 30 7 | 8 | # As this is a lossy representation, and optimism can prove double-edged, it operates in two modes: 9 | # Specified time: (to nearest *preceding* half hour) (don't show appointments as later than they are) 10 | # System time: (to nearest *following* half hour) (don't overstate how much sand remains in the glass) 11 | 12 | HRS=$1; MINS=$2 13 | ERRFORWARD=false #(if current time err forward, if scheduling, err earlier) 14 | if [[ -z $HRS ]]; then HRS=$(date +"%I"); ERRFORWARD=true; fi 15 | if [[ -z $MINS ]]; then MINS=$(date +"%M"); ERRFORWARD=true; fi 16 | JSC="/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc" 17 | EMOTIME=$("$JSC" -e " 18 | function emoTime(dteJS) { 19 | var lngBase=0x1F54F, lngHour=(dteJS.getHours() % 12), lngMins=dteJS.getMinutes(), 20 | iHourCode, iFullCode; 21 | if (lngHour) iHourCode=lngBase+lngHour; else iHourCode=lngBase+12; 22 | 23 | // Note: any second offset of 12 takes us to the block of corresponding half hour icons 24 | if ($ERRFORWARD) { 25 | if (lngMins >= 45) { // move on at quarter to and quarter past 26 | iFullCode=iHourCode+1; 27 | } else if (lngMins >= 15) { 28 | iFullCode=iHourCode+12; // jump to matching half hour glyph 29 | } else iFullCode=iHourCode; 30 | } else { 31 | // only move on at the full half hour 32 | if (lngMins >= 30) iFullCode=iHourCode+12; else iFullCode=iHourCode; 33 | } 34 | return asUnicode(iFullCode); 35 | } 36 | 37 | function asUnicode(c) { 38 | var lngClear = c - 0x10000; 39 | return String.fromCharCode( (lngClear >> 10) + 0xD800) + 40 | String.fromCharCode( (lngClear & 0x3FF) + 0xDC00); 41 | } 42 | dteJS = new Date(); dteJS.setHours($HRS, $MINS); 43 | print(emoTime(dteJS));") 44 | echo "$EMOTIME" 45 | echo "$EMOTIME" | pbcopy 46 | -------------------------------------------------------------------------------- /copy as Markdown/safariCopyAsHTML.applescript: -------------------------------------------------------------------------------- 1 | // Yosemite JXA (Javascript for Automation) Copy As MD for Safari 2 | // Requires a copy **in the same folder as this script** 3 | // of html2text.py, originally contributed by Aaron Swartz z''l 4 | // [html2text.py](https://github.com/aaronsw/html2text) 5 | // 6 | function run() { 7 | /*jshint multistr: true */ 8 | 9 | //var dct = { 10 | // title: "Copy as Markdown (for Safari)", 11 | // ver: "0.2", 12 | // description: "Runs HTML of Safari selection through html2text.py", 13 | // author: "RobTrew copyright 2014", 14 | // license: "MIT", 15 | // site: "https://github.com/RobTrew/txtquery-tools" 16 | //}; 17 | 18 | // Compacted string of simple .js code for copying Safari selection as HTML 19 | var strFnHTMLSeln = "(function (){var c=window.getSelection(),\ 20 | d=c.rangeCount,a;if(d){a=document.createElement('div');\ 21 | for(var b=0;b> 10) + 0xD800) + 41 | String.fromCharCode((lngClear & 0x3FF) + 0xDC00); 42 | } 43 | dteJS = new Date($YEAR, $(($MONTH-1)), $DAY); 44 | print(emoMoon(dteJS));") 45 | echo "$EMOMOON" 46 | echo "$EMOMOON" | pbcopy -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/readme.md: -------------------------------------------------------------------------------- 1 | ### Custom perspectives across several files using NSXML XQuery from Javascript 2 | #### ( Simple demo ) 3 | 4 | XQuery is now directly accessible to OS X 10.10 scripting. 5 | 6 | It provides a very flexible way of generating grouped and sub-grouped reports across a set of text files in HTML OPML or other XML formats, either generated automatically through a Hazel process from tagged MD or TaskPaper files, or as the native format of a preferred outlining and tagging application. 7 | 8 | [This script](./QueryAFolderOfOPMLFiles.applescript) (with the [folder of sample OPML files](./SampleFolder) on which it runs) provides a simple demo of a generic approach. 9 | 10 | ##### Installation and use 11 | - Copy the sample OPML files in SampleFolder to a folder on your system, 12 | - launch Brett Terpstra's [Marked 2](http://marked2app.com), 13 | - and run the QueryAFolderOfOPMLFiles script. 14 | 15 | > Note that it's a JXA Javascript. 16 | > In Yosemite Script Editor you will need to choose 'Javascript' rather than 'Applescript' from the top left pull-down 17 | 18 | 19 | When the script throws up a `choose folder` dialog, choose the folder containing the set of tagged OPML files 20 | 21 | ( These files could be exported from FoldingText @key(value) MD, or from OO3 columnar outlines, perhaps automatically by something like Hazel ) 22 | 23 | ##### Output 24 | You will be offered a menu of custom perspectives, defined in the syntax of OS X 10.10's native NSXML XQuery 1.0. 25 | 26 | ![Menu of perspectives](./QueryMenu.png) 27 | 28 | The results will be displayed in [Marked 2](http://marked2app.com). 29 | 30 | ![Notes grouped by days and times](./GroupedByDaysAndTimes.png) 31 | 32 | ![Linking back to the file containing a note](./LinkingBackToSource.png) 33 | 34 | 35 | ##### References 36 | 37 | - [XQuery/FLWOR Expression](http://en.wikibooks.org/wiki/XQuery/FLWOR_Expression) 38 | - [XQuery – Search Across a Variety of XML Data](http://shop.oreilly.com/product/9780596006341.do) 39 | - [XQuery 1.0](http://www.w3.org/TR/xquery/) 40 | 41 | - [NSXMLNode.objectsForQuery](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSXMLNode_Class/index.html#//apple_ref/occ/instm/NSXMLNode/objectsForXQuery:error:) 42 | 43 | 44 | -------------------------------------------------------------------------------- /filtering in the editor/tagsToggleHideFocus-minjs.applescript: -------------------------------------------------------------------------------- 1 | function run(){function v(e,x){var f=e.tree(),w=f.tags().sort(),b,a=[],h,c,d,m=0;b=0;for(var k=x.filtered,l=w.length,g=0;g 2) { 46 | strURL += ('?find=' + strText); 47 | } 48 | 49 | if (lngLine) { 50 | strURL += ('?line=' + lngLine.toString()); 51 | } 52 | 53 | if (lngStartOffset) { 54 | if (lngEndOffset) { 55 | if (lngStartOffset !== lngEndOffset) { 56 | strURL += ('?startoffset=' + lngStartOffset.toString()); 57 | strURL += ('?endoffset=' + lngEndOffset.toString()); 58 | } 59 | } 60 | 61 | } 62 | 63 | return encodeURI(strURL); 64 | } 65 | 66 | " on run set varResult to missing value tell application "TaskPaper" set lstDocs to documents if lstDocs ≠ {} then set oDoc to item 1 of lstDocs tell oDoc set strPath to POSIX path of ((file of it) as alias) set strURL to (evaluate script pstrJS with options {docpath:strPath}) set the clipboard to strURL display notification "tp3doc:// link copied ..." end tell end if end tell return strURL end run -------------------------------------------------------------------------------- /filtering in the editor/FilterOnTagsMenu.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Filter FT on chosen tags" property pVer : "0.1" property pAuthor : "Copyright (c) 2014 Robin Trew" property pLicense : "MIT - see full text to be included in ALL copies at https://github.com/RobTrew/txtquery-tools 2 | 3 | (FoldingText is Copyright (c) 2014 Jesse Grosjean) 4 | " property pUse : " 5 | 6 | Filters on all tags chosen from a menu. 7 | 8 | (For multiple selections in the menu, hold down the ⌘ command key) 9 | 10 | To include ancestors of the tagged lines: 11 | edit precOptions below to {axis:'///'} 12 | 13 | To exclude ancestors: 14 | edit precOptions below to {axis:'//'} 15 | " property precOptions : {axis:"//"} -- or axis {"///"} to include ancestors of tagged lines property pstrJS : " 16 | function(editor, options) { 17 | 18 | var lstSeldTags = options.tagset, 19 | strPath = '///*', 20 | lngTags, i; 21 | 22 | lngTags = lstSeldTags.length; 23 | if (lngTags) { 24 | strPath = options.axis 25 | if (lngTags < 2) 26 | strPath += ('@' + lstSeldTags[0]); 27 | else { 28 | strPath += '('; 29 | for (i=lngTags; i--;) { 30 | strPath += ('@' + lstSeldTags[i] + ' or '); 31 | } 32 | strPath = strPath.substr(0, strPath.length -4) + ')'; 33 | } 34 | } 35 | editor.setNodePath(strPath); 36 | return strPath; 37 | } 38 | 39 | " on run set varResult to missing value tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then tell item 1 of lstDocs set lstTags to my ChooseTags(it) if lstTags ≠ missing value then set varResult to (evaluate script pstrJS with options precOptions & {tagset:lstTags}) else evaluate script "function (editor) {editor.setNodePath('///*')}" end if end tell end if end tell return varResult end run on ChooseTags(oDoc) tell application "FoldingText" tell oDoc to set lstTags to evaluate script "function(editor) {var lstTags = editor.tree().tags(false); lstTags.sort(); return lstTags;}" activate if lstTags ≠ {} then set varChoice to choose from list lstTags with title pTitle & tab & pVer with prompt ¬ "Hold down ⌘ for multiple selections" & linefeed & linefeed & "Choose tags: " default items {item 1 of lstTags} ¬ OK button name "OK" cancel button name "Cancel" with empty selection allowed and multiple selections allowed if varChoice = false then return missing value return varChoice else return {} end if end tell end ChooseTags -------------------------------------------------------------------------------- /utilities/Italic.kmmacros: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Activate 7 | Normal 8 | IsActive 9 | 10 | Macros 11 | 12 | 13 | Actions 14 | 15 | 16 | DisplayKind 17 | None 18 | IsActive 19 | 20 | IsDisclosed 21 | 22 | MacroActionType 23 | ExecuteAppleScript 24 | Path 25 | 26 | Text 27 | property pstrJS : " 28 | function(editor) { 29 | editor.toggleSelectionFormatting(editor, '*'); 30 | } 31 | " 32 | tell application "FoldingText" 33 | set lstDocs to documents 34 | if lstDocs ≠ {} then tell item 1 of lstDocs to (evaluate script pstrJS) 35 | end tell 36 | TimeOutAbortsMacro 37 | 38 | TrimResults 39 | 40 | TrimResultsNew 41 | 42 | UseText 43 | 44 | 45 | 46 | IsActive 47 | 48 | ModificationDate 49 | 428973401.487064 50 | Name 51 | Italic 52 | Triggers 53 | 54 | 55 | FireType 56 | Pressed 57 | KeyCode 58 | 34 59 | MacroTriggerType 60 | HotKey 61 | Modifiers 62 | 256 63 | 64 | 65 | UID 66 | 25CB5F49-F559-4B82-9F28-533CA65C8BBC 67 | 68 | 69 | Name 70 | FT 2 71 | Targeting 72 | 73 | Targeting 74 | Included 75 | TargetingApps 76 | 77 | 78 | BundleIdentifier 79 | com.foldingtext.paddle.FoldingText 80 | Name 81 | FoldingText 82 | NewFile 83 | /Applications/FoldingText.app 84 | 85 | 86 | 87 | UID 88 | ABCDA594-1101-4BB2-B211-DE343A76E2D5 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ftdoc url scheme and FTCopyAsURL/FTCopyAsURL.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Copy FT selection as ftdoc:// url" property pVer : "0.04" property pAuthor : "Rob Trew" property pDescription : " 2 | 3 | Copies the selected text in FoldingText as an ftdoc:// URL 4 | linking back to the current document, filter state, 5 | and (if still identifiable by nodepath, search string or line number), selection. 6 | 7 | (Uses the ftdoc:// url-scheme - registered and handled by the OpenFTDocAtLine.app applescript app bundle) 8 | 9 | " property pstrJS : " 10 | 11 | function(editor, options) { 12 | 13 | var libNodePath = require('ft/core/nodepath').NodePath, 14 | libPasteboard = require('ft/system/pasteboard').Pasteboard, 15 | libNotification = require('ft/system/notificationcenter').NotificationCenter, 16 | tree=editor.tree(), 17 | 18 | rngSeln = editor.selectedRange(), 19 | oFirstNode = rngSeln.startNode, 20 | dctStartOffset = rngSeln.startLineCh(), 21 | dctEndOffset = rngSeln.endLineCh(), 22 | 23 | strNodePath = editor.nodePath().toString(), 24 | strSelnPath = libNodePath.calculateMinNodePath(oFirstNode), 25 | strDocPath=options.docpath, 26 | strURL='', strText, 27 | strEncoded, 28 | 29 | lngLine = dctStartOffset.line, 30 | lngStartOffset=dctStartOffset.ch, 31 | lngEndOffset=-1, 32 | lnPosn; 33 | 34 | 35 | 36 | if (dctEndOffset.line === lngLine) { 37 | lngEndOffset = dctEndOffset.ch; 38 | } 39 | strURL='ftdoc://' + strDocPath; 40 | 41 | if (strNodePath !== '///*') { 42 | strURL += ('?nodepath=' + strNodePath); 43 | } 44 | if (strSelnPath.indexOf('@id') < 0) { 45 | strURL += ('?selnpath=' + strSelnPath); 46 | } 47 | 48 | strText = oFirstNode.text(); 49 | if (strText.length > 2) { 50 | strURL += ('?find=' + strText); 51 | } 52 | 53 | if (lngLine) { 54 | strURL += ('?line=' + lngLine.toString()); 55 | } 56 | 57 | if (lngStartOffset) { 58 | if (lngEndOffset) { 59 | if (lngStartOffset !== lngEndOffset) { 60 | strURL += ('?startoffset=' + lngStartOffset.toString()); 61 | strURL += ('?endoffset=' + lngEndOffset.toString()); 62 | } 63 | } 64 | 65 | } 66 | 67 | strEncoded=encodeURI(strURL); 68 | libPasteboard.writeString(strEncoded); 69 | libNotification.deliverNotification('ftdoc:// link copied', 70 | '(for current selection and filter state)', oFirstNode.text()); 71 | return strEncoded; 72 | } 73 | 74 | " on run set varResult to missing value tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then set oDoc to item 1 of lstDocs tell oDoc set strPath to POSIX path of ((file of it) as alias) set strURL to (evaluate script pstrJS with options {docpath:strPath}) end tell end if end tell return strURL end run -------------------------------------------------------------------------------- /perspectives.ftplugin/Example TXTQUERY.sh from KM.kmmacros: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Activate 7 | Normal 8 | IsActive 9 | 10 | Macros 11 | 12 | 13 | Actions 14 | 15 | 16 | IsActive 17 | 18 | IsDisclosed 19 | 20 | MacroActionType 21 | SetVariableToText 22 | Text 23 | -m 24 | Variable 25 | TXTQUERY_SWITCHES 26 | 27 | 28 | DisplayKind 29 | Window 30 | IsActive 31 | 32 | IsDisclosed 33 | 34 | MacroActionType 35 | ExecuteShellScript 36 | Path 37 | /Users/robintrew/Desktop/txtquery025.sh 38 | Text 39 | #!/bin/bash 40 | 41 | # A shell run by tools like KeyBoard Maestro, LaunchBar, Alfred etc may not pick up the user's bash paths 42 | if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then 43 | export PATH=$PATH:/usr/local/bin 44 | fi 45 | 46 | # Also ensure that the locale is a UTF-8 setting 47 | if [[ "$LC_CTYPE" != *"UTF-8"* ]]; then 48 | export LC_CTYPE="UTF-8" 49 | fi 50 | 51 | TXTQUERYPATH="$HOME/Library/Application Support/FoldingText/Plug-Ins/perspectives.ftplugin/txtquery.sh" 52 | cd "$(dirname "$TXTQUERYPATH")" 53 | chmod +x "$TXTQUERYPATH" #only needed once, if at all 54 | 55 | 56 | echo "$("$TXTQUERYPATH" $KMVAR_TXTQUERY_SWITCHES)" 57 | TimeOutAbortsMacro 58 | 59 | TrimResults 60 | 61 | TrimResultsNew 62 | 63 | UseText 64 | 65 | 66 | 67 | IsActive 68 | 69 | ModificationDate 70 | 429232747.86864299 71 | Name 72 | Example: TXTQUERY.sh from KM 73 | Triggers 74 | 75 | 76 | FireType 77 | Pressed 78 | KeyCode 79 | 17 80 | MacroTriggerType 81 | HotKey 82 | Modifiers 83 | 6400 84 | 85 | 86 | UID 87 | 2C37DC07-3D11-45B9-84E3-ED76AA1E9A15 88 | 89 | 90 | Name 91 | Global Macro Group 92 | UID 93 | B8D29C96-172A-44B1-A9DD-75E509D56722 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /utilities/SelectedMailToFTInbox.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Selected Mail.app msg to FT Inbox" property pVer : "0.2" property pAuthor : "Rob Trew Twitter: @complexpoint" -- EDIT THE FOLLOWING DETAILS TO SET THE BEHAVIOUR OF THE SCRIPT property pblnAppendToFile : false -- (set to true if you simply want to append to the end of a named text file) property pblnAddToTop : true -- (if adding to # Inbox section, add at top or end ?) property pstrFilePath : "$HOME/Library/Application Support/Notational Velocity/Inbox.txt" property pstrNodePath : "/Inbox" -- (Assumes that inbox is a top level heading, if it exists) property precOptions : {inboxpath:pstrNodePath, top:pblnAddToTop} property pstrJS : " 2 | 3 | function(editor, options) { 4 | var oTree = editor.tree(), 5 | lstInbox = oTree.evaluateNodePath(options.inboxpath), oInbox, 6 | strText, lstChiln, strMsg=options.msg, lstLines = strMsg.split('\\n'), 7 | lngLines = lstLines.length, i, oFirstChild=null, blnTop = options.top; 8 | 9 | if (lngLines) { 10 | // CHECK THAT WE HAVE AN INBOX (CREATING ONE IF NECESSARY) 11 | if (lstInbox.length) { 12 | oInbox = lstInbox[0]; 13 | } else { 14 | oInbox = oTree.createNode('# Inbox'); 15 | oTree.appendNode(oInbox); 16 | } 17 | oTree.ensureClassified(); 18 | if (oInbox.hasChildren()) { 19 | oFirstChild = oInbox.children()[0]; 20 | } 21 | // ADD NEW LINES EITHER AT START OR END OF INBOX 22 | if (blnTop && oFirstChild) { 23 | for (i=0; i> " & quoted form of strPath do shell script strCMD else -- OR OPEN AS DOC IN FT AND ADD TO AN INBOX SECTION (IF property pblnAppendToFile : FALSE) set recOptions to {msg:strMD} & precOptions tell application "FoldingText" set oDoc to open strPath tell oDoc to set varResult to (evaluate script pstrJS with options recOptions) end tell end if end if end run on MailSelnAsMd() tell application "Mail" activate set lstText to {} repeat with refMsg in (selection as list) tell (contents of refMsg) set strLine to "- [" & sender & "]() [" & subject set end of lstText to strLine & "](message://%3c" & message id & "%3e)" end tell end repeat set {dlm, my text item delimiters} to {my text item delimiters, linefeed} set strTxt to lstText as string set my text item delimiters to dlm return strTxt end tell end MailSelnAsMd -------------------------------------------------------------------------------- /utilities/OpeniThoughtsMapinMarked.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Open active iThoughtsX map in Marked 2" property pVer : "0.5" property pblnPositionWindows : true -- Set this to false to disable the window positioning at the end of the script property pstrMarked : "Marked" property pstrThoughts : "iThoughtsX" property rLeftProportion : 1 / 4 -- what horizontal proportion of the screen for the app to the left ? property plstApps : {pstrMarked, pstrThoughts} -- (first app to left, swap order to swap app positions) -- Applescript properties persist between calls in compiled .scpt files -- This makes the script faster after the first use (it can skip display resolution detection) -- (but recompile from text and resave if you switch to a display with a different resolution) property pX : missing value property pY : missing value tell application "System Events" -- OPEN CURRENT iThoughtsX MAP IN MARKED 2 set lstProc to application processes where name contains pstrThoughts if lstProc is not equal to {} then try set winThoughts to front window of item 1 of lstProc on error return end try set strURL to value of (attribute "AXDocument" of winThoughts) do shell script "open -a Marked\\ 2 " & quoted form of strURL -- RESIZE AND REPOSITION WINDOWS, IF REQUIRED if pblnPositionWindows then -- ( Edit boolean value at start of script ) -- AVAILABLE CANVAS set {lngWidth, lngHeight} to my displayResoln() set lngAppHeight to lngHeight - 22 -- POSITION OF EDGE BETWEEN APPS (MEASURED FROM LEFT OF SCREEN) set lngLeft to (lngWidth * rLeftProportion) as integer -- LEFT APP WINDOW set lstLeftProc to {} set lstLeftProc to application processes where name contains (item 1 of plstApps) if lstLeftProc is not equal to {} then try set winLeft to front window of (item 1 of lstLeftProc) tell winLeft to set {position, size} to {{0, 22}, {lngLeft, lngAppHeight}} end try end if -- RIGHT APP WINDOW set lstRightProc to {} set lstRightProc to application processes where name contains (item 2 of plstApps) if lstRightProc is not equal to {} then try set winRight to front window of (item 1 of lstRightProc) tell winRight to set {position, size} to {{lngLeft, 22}, {(lngWidth - lngLeft), lngAppHeight}} end try end if end if tell application "Marked 2" to activate tell application "iThoughtsX" to activate end if end tell on displayResoln() if (pX is missing value) or (pY is missing value) then set {dlm, my text item delimiters} to {my text item delimiters, "Resolution"} set lstDisplays to text items of (do shell script "system_profiler SPDisplaysDataType") repeat with i from 2 to length of lstDisplays set strLine to item i of lstDisplays if strLine contains "Main Display: Yes" then exit repeat end repeat set my text item delimiters to space set lstParts to text items of strLine set my text item delimiters to dlm set {strX, strY} to {item 2, item 4} of lstParts set {pX, pY} to {strX as integer, strY as integer} end if return {pX, pY} end displayResoln -------------------------------------------------------------------------------- /utilities/OpenFTDocinMarked.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Open active FoldingText document in Marked 2" 2 | property pVer : "0.5" 3 | 4 | property pblnPositionWindows : true -- Set this to false to disable the window positioning at the end of the script 5 | 6 | -- Applescript properties persist between calls in compiled .scpt files 7 | -- This makes the script faster after the first use (it can skip display resolution detection) 8 | -- (but recompile from text and resave if you switch to a display with a different resolution) 9 | property pX : missing value 10 | property pY : missing value 11 | 12 | property pstrMarked : "Marked" 13 | property pstrFT : "FoldingText" 14 | 15 | property rLeftProportion : 1 / 4 -- what horizontal proportion of the screen for the app to the left ? 16 | property plstApps : {pstrMarked, pstrFT} -- (first app to left, second to right: adjust as preferred) 17 | 18 | tell application "System Events" 19 | -- OPEN CURRENT DOC IN MARKED 2 20 | set lstProc to application processes where name contains pstrFT 21 | if lstProc is not equal to {} then 22 | try 23 | set winFT to front window of item 1 of lstProc 24 | on error 25 | return 26 | end try 27 | set strURL to value of (attribute "AXDocument" of winFT) 28 | 29 | do shell script "open -a Marked\\ 2 " & quoted form of strURL 30 | 31 | -- RESIZE AND REPOSITION WINDOWS, IF REQUIRED 32 | 33 | if pblnPositionWindows then -- ( Edit boolean value at start of script ) 34 | 35 | -- AVAILABLE CANVAS 36 | set {lngWidth, lngHeight} to my displayResoln() 37 | set lngAppHeight to lngHeight - 22 38 | 39 | -- POSITION OF EDGE BETWEEN APPS (MEASURED FROM LEFT OF SCREEN) 40 | set lngLeft to (lngWidth * rLeftProportion) as integer 41 | 42 | -- LEFT APP WINDOW 43 | set lstLeftProc to {} 44 | set lstLeftProc to application processes where name contains (item 1 of plstApps) 45 | if lstLeftProc is not equal to {} then 46 | try 47 | set winLeft to front window of (item 1 of lstLeftProc) 48 | tell winLeft to set {position, size} to {{0, 22}, {lngLeft, lngAppHeight}} 49 | end try 50 | end if 51 | 52 | -- RIGHT APP WINDOW 53 | set lstRightProc to {} 54 | set lstRightProc to application processes where name contains (item 2 of plstApps) 55 | if lstRightProc is not equal to {} then 56 | try 57 | set winRight to front window of (item 1 of lstRightProc) 58 | tell winRight to set {position, size} to {{lngLeft, 22}, {lngWidth - lngLeft, lngAppHeight}} 59 | end try 60 | end if 61 | end if 62 | end if 63 | end tell 64 | 65 | on displayResoln() 66 | if (pX is missing value) or (pY is missing value) then 67 | set {dlm, my text item delimiters} to {my text item delimiters, "Resolution"} 68 | set lstDisplays to text items of (do shell script "system_profiler SPDisplaysDataType") 69 | 70 | repeat with i from 2 to length of lstDisplays 71 | set strLine to item i of lstDisplays 72 | if strLine contains "Main Display: Yes" then exit repeat 73 | end repeat 74 | set my text item delimiters to space 75 | set lstParts to text items of strLine 76 | set my text item delimiters to dlm 77 | set {strX, strY} to {item 2, item 4} of lstParts 78 | set {pX, pY} to {strX as integer, strY as integer} 79 | end if 80 | return {pX, pY} 81 | end displayResoln -------------------------------------------------------------------------------- /utilities/FT2CSV.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Copy front FoldingText 2 document as CSV or TSV" property pVer : "0.2" property pAuthor : "Rob Trew Twitter: @complexpoint" property pDescription : " 2 | 3 | Copies contents of front FoldingText document to the clipboard as CSV or TSV 4 | (tab separated values) 5 | 6 | Edit pstrDelim below to specify commas or tabs 7 | 8 | (NOTE: a tab-delimited TSV version can be pasted straight into Excel, 9 | CSV needs to be pasted into a text file, and then opened in Excel) 10 | 11 | FORMAT: 12 | One spreadsheet column for each level of outline indentation, and 13 | One spreadsheet column for each type of @key(value) tag in the document 14 | 15 | The key of @key(value) is used as the column heading, 16 | and the value is placed in the spreadsheet cells 17 | 18 | DATES: 19 | Excel automatically recognises yyyy-mm-dd 20 | and yyyy-mm-dd hh:mm as datetime strings, and converts them accordingly. 21 | 22 | @due(2015-06-01 14:00) will become an excel date in a column with header 'Due' 23 | 24 | " property pstrTab : tab property pstrComma : "," -- SPECIFY FIELD DELIMITER BY EDITING THE VALUE OF PSTRDELIM HERE property pstrDelim : pstrTab -- VALUE TO EXPORT FOR SIMPLE IMPLICIT BOOLEANS LIKE @waiting WHICH DONT HAVE A BRACKETED VALUE property pstrBoolTrue : "1" property precOptions : {delimiter:pstrDelim, booltrue:pstrBoolTrue} -- NOTE: FIELDS CONTAINING THE DELIMITER WILL BE QUOTED property pstrJS : " 25 | function(editor, options) { 26 | 27 | function maxdepth(node) { 28 | // DEEPEST LEVEL IN THE FOLDINGTEXT OUTLINE 29 | var lngChiln = 0, lstChiln=[], 30 | lngMax = 0, lngDepth = 0; 31 | 32 | if (node.hasChildren()) { 33 | lstChiln = node.children(); 34 | for (var i = lstChiln.length; i --;) { 35 | lngDepth = maxdepth(lstChiln[i]) + 1; 36 | if (lngDepth > lngMax) lngMax = lngDepth; 37 | } 38 | } 39 | return lngMax; 40 | } 41 | 42 | function nestLevel(oNode) { 43 | // OUTLINE LEVEL OF THIS NODE 44 | var oParent = oNode.parent; 45 | if (!oNode.parent) { 46 | return -1; 47 | } else return nestLevel(oParent) +1; 48 | } 49 | 50 | var strDelim = options.delimiter, 51 | strBool = options.booltrue, 52 | oTree=editor.tree(), 53 | lngLevels = maxdepth(oTree.root), 54 | lstTags = oTree.tags(true).sort(), 55 | lngTags = lstTags.length, 56 | lngCols=lngLevels+lngTags, 57 | lstCols=new Array(lngCols), 58 | lstRecord, strTag, iTag, iCol, 59 | lstRows=[], strRow, strValue; 60 | 61 | // CREATE A HEADER (ONE COLUMN FOR EACH OUTLINE LEVEL, AND ONE COLUMN FOR EACH TAG) 62 | lstRecord = lstCols.slice(0); 63 | for (iCol=lngLevels; iCol--;) { 64 | lstRecord[iCol] = 'Level ' + (iCol+1).toString(); 65 | } 66 | for (iTag=lngTags; iTag--;) { 67 | strTag=lstTags[iTag] 68 | lstRecord[lngLevels+iTag] = strTag[0].toUpperCase() + strTag.slice(1).toLowerCase(); 69 | } 70 | lstRows.push(lstRecord.join('\\t')); 71 | 72 | // GATHER THE DATA ROWS 73 | oTree.nodes().forEach(function (oNode) { 74 | lstRecord = lstCols.slice(0); 75 | lstRecord[nestLevel(oNode)] = oNode.text(); 76 | for (iTag=lngTags;iTag--;) { 77 | strTag=lstTags[iTag]; 78 | if (oNode.hasTag(strTag)) { 79 | strValue=oNode.tag(strTag); 80 | if (strValue) { 81 | if (strValue.indexOf(strDelim) !== -1) { 82 | strValue='\"' + strValue + '\"'; 83 | } 84 | } else strValue = strBool; 85 | lstRecord[lngLevels+iTag] = strValue; 86 | } 87 | } 88 | strRow = lstRecord.join(strDelim); 89 | lstRows.push(strRow); 90 | }); 91 | 92 | return lstRows.join('\\n'); 93 | } 94 | " on run set varResult to missing value tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then tell item 1 of lstDocs to set varResult to (evaluate script pstrJS with options precOptions) set the clipboard to varResult end if end tell return varResult end run -------------------------------------------------------------------------------- /filtering in the editor/ArchiveTagsMenu.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Archive chosen tag-types to matching section" property pVer : "0.1" property pAuthor : "Copyright (c) 2014 Robin Trew" property pLicense : "MIT - see full text to be included in ALL copies at https://github.com/RobTrew/txtquery-tools 2 | 3 | (FoldingText is Copyright (c) 2014 Jesse Grosjean) 4 | " property pUse : " 5 | 6 | Archives all line tagged with a tag chosen from a menu, 7 | to a section '# Archive '. 8 | 9 | To change the affected tag, edit the tagname in property precOptions below this line. 10 | 11 | (if you comment out the precOptions line, the script will offer a menu, 12 | listing each type of tag found in the document ) 13 | " 14 | 15 | -- INCLUDE A LINE LIKE THE FOLLOWING TO BY-PASS THE MENU AND CREATE A SCRIPT SPECIFIC TO ONE TAG TYPE -- property precOptions : {archivetags:{"waiting"}} -- property precOptions : {archivetags:{"cancelled"}} property pstrJS : " 16 | function(editor) { 17 | // Skip any line already archived with an ancestor 18 | function rootsOnly(oTree, lstNodes) { 19 | var lstSeen = [], strID, oParent, lngNodes=lstNodes.length, oNode,i; 20 | 21 | nextnode: for (i=0; i Open Application Folder` 70 | - Copy the `.ftplugin` folder and its contents into the `Plug-Ins` sub-folder of the application folder 71 | - Close and restart the application -------------------------------------------------------------------------------- /copy as Markdown/Copy as Markdown.kmmacros: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Activate 7 | Normal 8 | IsActive 9 | 10 | Macros 11 | 12 | 13 | Actions 14 | 15 | 16 | DisplayKind 17 | Variable 18 | IsActive 19 | 20 | IsDisclosed 21 | 22 | MacroActionType 23 | ExecuteShellScript 24 | Path 25 | 26 | Text 27 | osascript -l JavaScript '/Users/robintrew/txtquery-tools/copy as Markdown/safariCopyAsMDjs.scpt' 2> /dev/null 28 | TimeOutAbortsMacro 29 | 30 | TrimResults 31 | 32 | TrimResultsNew 33 | 34 | UseText 35 | 36 | Variable 37 | ClipResults 38 | 39 | 40 | Conditions 41 | 42 | ConditionList 43 | 44 | 45 | ConditionType 46 | Variable 47 | Variable 48 | ClipResults 49 | VariableConditionType 50 | IsNotEmpty 51 | VariableValue 52 | value 53 | 54 | 55 | ConditionListMatch 56 | All 57 | 58 | ElseActions 59 | 60 | 61 | IsActive 62 | 63 | IsDisclosed 64 | 65 | MacroActionType 66 | Notification 67 | SoundName 68 | Blow 69 | Subtitle 70 | (HTML access may not be allowed by website) 71 | Text 72 | 73 | Title 74 | Nothing copied ... 75 | 76 | 77 | IsActive 78 | 79 | IsDisclosed 80 | 81 | MacroActionType 82 | IfThenElse 83 | ThenActions 84 | 85 | 86 | IsActive 87 | 88 | IsDisclosed 89 | 90 | MacroActionType 91 | Notification 92 | SoundName 93 | Pop 94 | Subtitle 95 | Selection Copied as Markdown 96 | Text 97 | %Variable%ClipResults% 98 | Title 99 | Safari 100 | 101 | 102 | TimeOutAbortsMacro 103 | 104 | 105 | 106 | IsActive 107 | 108 | ModificationDate 109 | 437610601.50608599 110 | Name 111 | Copy as Markdown 112 | Triggers 113 | 114 | 115 | FireType 116 | Pressed 117 | KeyCode 118 | 8 119 | MacroTriggerType 120 | HotKey 121 | Modifiers 122 | 6400 123 | 124 | 125 | UID 126 | DA64481D-4C7D-4D63-BEB8-B0065B5CE452 127 | 128 | 129 | Name 130 | Safari 131 | UID 132 | 7522F7B8-A30C-4313-AC93-813DF667C99B 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /filtering in the editor/FilterOnAllSelectedTags.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Filter FoldingText on all selected tags" property pVer : "0.1" property pAuthor : "Copyright (c) 2014 Robin Trew" property pLicense : "MIT - see full text to be included in ALL copies at https://github.com/RobTrew/txtquery-tools 2 | 3 | (FoldingText is Copyright (c) 2014 Jesse Grosjean) 4 | " property pUse : " 5 | 6 | Filters on all tags overlapping single or multiple selections. 7 | 8 | (For multiple selections in FoldingText, hold down the ⌘ command key) 9 | 10 | To include ancestors of the tagged lines: 11 | edit precOptions below to {axis:'///'} 12 | 13 | To exclude ancestors: 14 | edit precOptions below to {axis:'//'} 15 | " property precOptions : {axis:"//"} -- or axis {"///"} to include ancestors of tagged lines property pstrJS : " 16 | function(editor, options) { 17 | 18 | function selectedTags(editor) { 19 | // ALL TAGS OVERLAPPED BY ANY SINGLE OR MULTIPLE SELECTIONS 20 | var rngSeln, oNode, dctTagPosns, 21 | lstSeln, lstNodes, lstSelnPosns, lstSeldTags=[], 22 | lstNodeTags, lstTagStartEnd, 23 | strTag, blnSeln = editor.hasSelection(), i,j; 24 | 25 | if (blnSeln) { 26 | lstSeln = editor.selectedRanges(); 27 | for (i=lstSeln.length; i--;) { 28 | rngSeln = lstSeln[i]; 29 | lstNodes = rngSeln.nodesInRange(); 30 | for (j=lstNodes.length; j--;) { 31 | oNode = lstNodes[j]; 32 | // If the node has tags 33 | if (Object.keys(oNode.tags()).length) { 34 | dctTagPosns = tagPositions(oNode); 35 | lstSelnPosns = selnPositions(oNode, rngSeln); 36 | 37 | for (strTag in dctTagPosns) { 38 | // Unless we have already seen this tag 39 | if (lstSeldTags.indexOf(strTag) == -1) { 40 | if (overlap(dctTagPosns[strTag], lstSelnPosns)) 41 | lstSeldTags.push(strTag); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | return lstSeldTags; 49 | } 50 | 51 | function tagPositions(oNode) { 52 | // START AND END OFFSETS OF EACH TAG IN THIS NODE 53 | var lstRuns = oNode.lineAttributedString()._attributeRuns, 54 | dctTagPosns = {}, oRun, oAttr, strTag, iFrom, k; 55 | 56 | for (k=lstRuns.length; k--;) { 57 | oRun = lstRuns[k]; 58 | oAttr = oRun.attributes; 59 | if (oAttr.keyword == '@') { 60 | strTag = oAttr.tag; 61 | iFrom = oRun.location; 62 | dctTagPosns[tagKey(strTag)]=[iFrom, iFrom+strTag.length]; 63 | } 64 | } 65 | return dctTagPosns; 66 | } 67 | 68 | function selnPositions(oNode, rngSeln) { 69 | // OFFSETS OF FIRST AND LAST SELECTED CHARS IN THIS NODE 70 | var iNodeEnd = oNode.line().length, 71 | iSelnStart = rngSeln.startOffset, 72 | iSelnLength = rngSeln.length(), 73 | iNodeAbsStart = oNode.lineTextStart(), 74 | iNodeAbsEnd = iNodeAbsStart + iNodeEnd, 75 | iSelnAbsStart = rngSeln.location(), 76 | iSelnAbsEnd = iSelnAbsStart + iSelnLength, 77 | iStart, iEnd; 78 | 79 | if (iSelnAbsStart < iNodeAbsStart) iStart = 0; 80 | else iStart = iSelnStart; 81 | 82 | if (iSelnAbsEnd > iNodeAbsEnd) iEnd = iNodeEnd; 83 | else iEnd =iSelnStart+iSelnLength; 84 | 85 | return [iStart, iEnd]; 86 | } 87 | 88 | function overlap(lstA, lstB) { 89 | // NOT IF THIS ENDS BEFORE THAT STARTS, 90 | // OR STARTS AFTER THAT ENDS 91 | return !(lstA[1] < lstB[0] || lstA[0] > lstB[1]); 92 | } 93 | 94 | function tagKey(strKeyValueTag) { 95 | // JUST THE KEY PART OF A @key(value) OR @key TAG 96 | var strKey, iOpen = strKeyValueTag.indexOf('('); 97 | 98 | if (iOpen !== -1) strKey = strKeyValueTag.substring(1, iOpen); 99 | else strKey = strKeyValueTag.substring(1); 100 | return strKey; 101 | } 102 | 103 | var lstSeldTags = selectedTags(editor), 104 | strPath = '///*', 105 | lngTags, i; 106 | 107 | lngTags = lstSeldTags.length; 108 | if (lngTags) { 109 | strPath = options.axis 110 | if (lngTags < 2) 111 | strPath += ('@' + lstSeldTags[0]); 112 | else { 113 | strPath += '('; 114 | for (i=lngTags; i--;) { 115 | strPath += ('@' + lstSeldTags[i] + ' or '); 116 | } 117 | strPath = strPath.substr(0, strPath.length -4) + ')'; 118 | } 119 | } 120 | editor.setNodePath(strPath); 121 | return strPath; 122 | } 123 | 124 | " on run set varResult to missing value tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then tell item 1 of lstDocs set varResult to (evaluate script pstrJS with options precOptions) end tell end if end tell return varResult end run -------------------------------------------------------------------------------- /utilities/cliptextcolors.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: Rob Trew, 2014 3 | 4 | # ver 0.2 5 | 6 | # SPECIFY DEFAULT MESSAGE COLOR MODEL, VALUE RANGES, CYCLE COUNT 7 | 8 | TRANSFORM_CLIPBOARD=1 #0 (or empty) to leave plain text version in clipboard, 1 to put RTF or 2 to put HTML in clipboard) 9 | MSG="abracadabracadabracadabracadabra" 10 | FONTFACE="Courier" 11 | FONTSIZE="4" 12 | SCHEME="DarkRainBow" # Choose a key from set of scheme names in 'var options' below, or add a scheme 13 | 14 | # TO USE: 15 | # 1. Copy some plain text into the clipboard 16 | # 2. Run this script 17 | # 3. Colored HTML spans for each character will be written to STDOUT 18 | # 4. if $TRANSFORM_CLIPBOARD (above) is non-zero, an RTF (=1) or HTML (=2) version will replace the plain text in the clipboard 19 | 20 | # Example: 21 | 22 | # 'BlackToRed' : {'model':'hsl', -- 'hsl' or 'rgb' 23 | # 'percent':[false, true, true], -- should the nth value of 3 be followed by '%' ? 24 | # p1:[255, 0], p2:[20, 100], p3:[20, 50], -- range of values for the nth of 3 integers to cycle through 25 | # cycles:2} -- how many Pi cycles ? 1=from start to end value 2=return to start value N=several cycles 26 | 27 | CLIPTEXT=$(pbpaste -Prefer txt) 28 | if [ ! -z "$CLIPTEXT" ]; then 29 | MSG="$CLIPTEXT" 30 | fi 31 | 32 | escape() { 33 | echo "$1" | sed "s/\([^[:alnum:]]\)/\\\\\1/g" 34 | } 35 | 36 | CLEAN=$(escape "$MSG") 37 | 38 | JSC="/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc" 39 | COLORSPANS=$("$JSC" -e " 40 | 41 | var options = {'msg':'$CLEAN', 'fontface':'$FONTFACE','fontsize':$FONTSIZE}, 42 | dctPalette = { 43 | 'BlackToRed' : {'model':'hsl', 'percent':[false, true, true], 44 | p1:[0, 255], p2:[20, 100], p3:[20, 50], cycles:2}, 45 | 'Gray' : {'model':'rgb', 'percent':[false, false, false], 46 | p1:[255, 40], p2:[255, 40], p3:[255, 40], cycles:2}, 47 | 'RainBow' : {'model':'hsl', 'percent':[false, true, true], 48 | p1:[255, 0], p2:[100, 100], p3:[50, 50], cycles:2}, 49 | 'DarkRainBow' : {'model':'hsl', 'percent':[false, true, true], 50 | p1:[255,0], p2:[0, 100], p3:[50, 50], cycles:2}, 51 | 'RedPink' : {'model':'hsl', 'percent':[false, true, true], 52 | p1:[0, 0], p2:[100, 100], p3:[95, 50], cycles:2}, 53 | 'RedGrey' : {'model':'hsl', 'percent':[false, true, true], 54 | p1:[0, 255], p2:[100, 12], p3:[50, 80], cycles:2}, 55 | 'GreyRed' : {'model':'hsl', 'percent':[false, true, true], 56 | p1:[255, 0], p2:[12, 100], p3:[80, 50], cycles:2} 57 | }, 58 | dctScheme=dctPalette['$SCHEME']; 59 | 60 | function colorSpans(options) { 61 | var strMsg = options.msg, 62 | lngChars = strMsg.length, 63 | nCycles = options.cycles * Math.PI, 64 | lstP1=options.p1, 65 | lstP2=options.p2, 66 | lstP3=options.p3, 67 | min1=lstP1[0], max1=lstP1[1], 68 | min2=lstP2[0], max2=lstP2[1], 69 | min3=lstP3[0], max3=lstP3[1], 70 | lstStart=[min1, min2, min3], 71 | lstFixed=[Math.round(min1).toString(), Math.round(min2).toString(), Math.round(min3).toString()], 72 | lstRange=[max1-min1, max2-min2, max3-min3], nRange, 73 | lstVal=[], 74 | strModel=options.model, 75 | lstPercent=options.percent, 76 | strVal='', lstSpan=[],lstHTML=[], 77 | nTheta, rPropn, 78 | i,j; 79 | 80 | for (i=0; i', strMsg.charAt(i),'']; 97 | 98 | //and push this single character span onto the HTML list. 99 | lstHTML.push(lstSpan.join('')); 100 | } 101 | return '' + lstHTML.join('') + ''; 102 | } 103 | 104 | for (var strKey in dctScheme) { 105 | options[strKey] = dctScheme[strKey]; 106 | } 107 | print(colorSpans(options)); 108 | ") 109 | echo "$COLORSPANS" 110 | 111 | if [[ ! -z $TRANSFORM_CLIPBOARD ]]; then 112 | if [ $TRANSFORM_CLIPBOARD -eq 1 ]; then 113 | echo "$COLORSPANS" | textutil -format html -convert rtf -inputencoding UTF-8 -stdin -stdout | pbcopy -Prefer rtf 114 | elif [ $TRANSFORM_CLIPBOARD -eq 2 ]; then 115 | echo "$COLORSPANS" | pbcopy 116 | fi 117 | fi 118 | 119 | 120 | -------------------------------------------------------------------------------- /ftdoc3 url scheme/README.md: -------------------------------------------------------------------------------- 1 | ### Copy FoldingText for Atom selection as ft3doc:// link back to document and selected line 2 | 3 | A [Keyboard Maestro](http://www.keyboardmaestro.com/main/) macro: 4 | 5 | - [Copy ft3doc URL for selected line in FoldingText 3.kmmacros](./Copy%20ft3doc%20URL%20for%20selected%20line%20in%20FoldingText%203.kmmacros) 6 | - [Copy ft3doc URL for selected line in FoldingText 3.kmmacros.zip](./Copy%20ft3doc%20URL%20for%20selected%20line%20in%20FoldingText%203.kmmacros.zip) 7 | 8 | which copies a path back to the open [FoldingText 3 for Atom beta](http://jessegrosjean.gitbooks.io/foldingtext-for-atom-user-s-guide/content/) document, and to the currently selected line. (FT3 lines have unique and persistent ids which can be used as url link targets) 9 | 10 | The built-in [Atom](https://atom.io/) menu command: **Atom > Edit > Copy Path** copies a `file://` url which can be used as an argument to the **atom** command in the shell, but is not predictably handled by browsers or other applications, which may display a preview of the file, or open a Finder window to reveal it, but will not open a file and select the relevant line. 11 | 12 | This macro also uses **Atom > Edit > Copy Path**, but replaces the `file://` prefix with the custom `ft3doc://` which requires simple installation (see below) of a handler app (OpenFT3DocAtLine.app), which needs to be on your system, but only needs to be run once, to register `ft3doc://` as a known handler. 13 | 14 | 15 | ### Applications 16 | - As an external bookmark to a particular point in a document (for example from another app) 17 | - For use in cross-file perspectives (defined in XQuery) as a link back to the lines displayed by a query. 18 | 19 | ### Use 20 | - Install the KeyBoard Maestro macro 21 | - Select a line or phrase in FoldingText 3 for Atom 22 | - Run the macro with Ctrl Shift C 23 | - Paste the resulting `ft3doc://` url wherever you need it. 24 | - (Not that cross file and internal links withing FT3 can already be created by simple drag and Ctrl-drop) 25 | 26 | #### `ft3doc://` 27 | #### A url-scheme for opening a FoldingText 3 for Atom document at a specific line 28 | The `ft3doc://` url-scheme is registered and handled by an Applescript, [OpenFT3DocAtLine.app](./OpenFT3DocAtLine.app) [OpenFT3DocAtLine.app.zip](./OpenFT3DocAtLine.app.zip): 29 | 30 | ###### OpenFT3DocAtLine.app 31 | - Is a [simple applescript](Source%20and%20info.plist%20for%20OpenFT3DocAtLine/OpenFT3DocAtLine.applescript) saved as an [app bundle](./OpenFT3DocAtLine.app), and 32 | - contains an [Info.plist](Source%20and%20info.plist%20for%20OpenFT3DocAtLine/Info.plist) which registers `ft3doc://` 33 | - contains an open url handler which: 34 | - Opens a specified text document in [FoldingText 3 for Atom beta](http://jessegrosjean.gitbooks.io/foldingtext-for-atom-user-s-guide/content/) 35 | - selects the line specified by its unique id 36 | - and can contain further [query, hoisting and expansion switches](http://jessegrosjean.gitbooks.io/foldingtext-for-atom-user-s-guide/content/appendix_c_path_query_parameters.html) 37 | 38 | NB if you want to create `ftdoc3://` urls ‘by hand’ or with a script of your own, you will need to uri-encode the file path and any nodepath. There are various ways of doing this at the command line, or in an applescript, with something like: 39 | 40 | ``` 41 | on encode(strPath) 42 | do shell script "python -c 'import sys, urllib as ul; print ul.quote(sys.argv[1])' " & quoted form of strPath 43 | end encode 44 | ``` 45 | 46 | Easier, of course, in Javascript: `encodeURI()` for a whole link, or `encodeURIComponent()` for parts excluding the opening scheme name. 47 | 48 | 49 | #### Installation 50 | 51 | For the `ft3doc://` url scheme to be registered, [OpenFT3DocAtLine.app](./OpenFTDoc3AtLine.app) [OpenFT3DocAtLine.app.zip](./OpenFT3DocAtLine.app.zip) needs to be on your system, and needs to have been run at least once. 52 | To do this: 53 | - EITHER extract the copy from the [txtquery-tools](https://github.com/RobTrew/txtquery-tools) repository [zip file](https://github.com/RobTrew/txtquery-tools/archive/master.zip), and ctrl-click to allow the OS X Gate Keeper security system to run it 54 | - OR: 55 | - open the .applescript text version Applescript editor, 56 | - save as an .app bundle 57 | - Ctrl-click on the .app bundle to _Open Package Contents_ 58 | - and replace the info.plist file with the version which declares the url-scheme 59 | 60 | [Repository .zip file](https://github.com/RobTrew/txtquery-tools/archive/master.zip) 61 | 62 | ##### Reference 63 | For an explanation of this approach to registering and handling a url with an applescript.app, and the info.plist in its bundle, 64 | See [http://www.macosxautomation.com/applescript/linktrigger/](http://www.macosxautomation.com/applescript/linktrigger/) 65 | 66 | -------------------------------------------------------------------------------- /tp3doc url scheme and TP3CopyAsURL/README.md: -------------------------------------------------------------------------------- 1 | ### Copy TaskPaper selection as tp3doc:// link back to document, line, selection and filter state 2 | 3 | The [TP3CopyAsURL](./TP3CopyAsURL.applescript) applescript copies the currently selected text position in [TaskPaper](http://www.hogbaysoftware.com), and creates a url which: 4 | - Links back to the current document, 5 | - restores the current selection, and: 6 | - also restores any filters that are currently applied to the document. 7 | 8 | For example, the following link, which has various optional switches 9 | 10 | `tp3doc:///Users/houthakker/Library/Application%20Support/Notational%20Velocity/notes-2014-06-30.txt?nodepath=///@priority?selnpath=/heil/a%20se/why/who/inte?find=Intermediaries?line=10?startoffset=5?endoffset=19` 11 | 12 | 1. Reopens a particular document in a Notational Velocity text file folder, 13 | 2. restores the `//@priority` filter path from the `?nodepath=` switch 14 | 3. attempts to restore the selection by looking for a node which matches the `?selnpath=` nodepath 15 | 4. if it finds nothing matched by the `?selnpath=` switch, selects the first node with text matching the `?find=` switch 16 | 4. and if all else fails selects whatever is specified by the optional `?line=`, `?startoffset` and `?endoffset=` switches 17 | 18 | Note that the file path and any other text or node paths are automatically uri-encoded by the script. 19 | 20 | ### Use 21 | - Select a line or phrase in TaskPaper 22 | - Run [TP3CopyAsURL](./TP3CopyAsURL.applescript) 23 | - Paste the resulting tp3doc:// url wherever you need it. 24 | - (It can be used for example, to provide active links between or within TaskPaper documents) 25 | 26 | #### tp3doc:// url-scheme for opening a TaskPaper document at a specific line 27 | The tp3doc:// url-scheme is registered and handled by an Applescript, [OpenTP3docAtLine](./OpenTP3docAtLine.app): 28 | 29 | ###### OpenTP3docAtLine.app 30 | - Is an [applescript](https://github.com/RobTrew/txtquery-tools/blob/master/tp3doc%20url%20scheme%20and%20TP3CopyAsURL/Source%20and%20info.plist%20for%20OpenTP3DocAtLine/OpenTP3DocAtLine.applescript) saved as an .app bundle 31 | - contains an [Info.plist](https://github.com/RobTrew/txtquery-tools/blob/master/tp3doc%20url%20scheme%20and%20TP3CopyAsURL/Source%20and%20info.plist%20for%20OpenTP3DocAtLine/Info.plist) which registers `tp3doc://` 32 | - contains an open url handler which: 33 | - Opens a specified text document in [TaskPaper](http://www.hogbaysoftware.com) 34 | - applies any filter given in a `?nodepath=` 35 | - selects any line specified in a `?line=` switch 36 | (if the line is hidden by the nodepath filter, the editor unfolds just enough to make the line visible) 37 | - optionally restricts the selection to a part of the line, using any character position specified by either or both of the following switches 38 | - `?startoffset=` 39 | - `?endoffset=` 40 | 41 | NB if you want to create _tp3doc://_ urls ‘by hand’ or with a script of your own, you will need to uri-encode the file path and any nodepath. There are various ways of doing this at the command line, or in an applescript, with something like: 42 | 43 | ``` 44 | on encode(strPath) 45 | do shell script "python -c 'import sys, urllib as ul; print ul.quote(sys.argv[1])' " & quoted form of strPath 46 | end encode 47 | ``` 48 | 49 | Easier, of course, in Javascript: `encodeURI()` for a whole link, or `encodeURIComponent()` for parts excluding the opening scheme name. 50 | 51 | 52 | #### Installation 53 | 54 | For the _tp3doc://_ url scheme to be registered, [OpenTP3docAtLine](./OpenTP3docAtLine.app) needs to be on your system, and needs to have been run at least once. 55 | To do this: 56 | - EITHER extract the copy from the [txtquery-tools](https://github.com/RobTrew/txtquery-tools) repository [zip file](https://github.com/RobTrew/txtquery-tools/archive/master.zip), and ctrl-click to allow the OS X Gate Keeper security system to run it 57 | - OR: 58 | - open the .applescript text version Applescript editor, 59 | - save as an .app bundle 60 | - Ctrl-click on the .app bundle to _Open Package Contents_ 61 | - and replace the info.plist file with the version which declares the url-scheme 62 | 63 | [Repository .zip file](https://github.com/RobTrew/txtquery-tools/archive/master.zip) 64 | 65 | ##### Reference 66 | For an explanation of this approach to registering and handling a url with an applescript.app, and the info.plist in its bundle, 67 | See [http://www.macosxautomation.com/applescript/linktrigger/](http://www.macosxautomation.com/applescript/linktrigger/) 68 | 69 | (Many thanks to Jamie Kowalski for drawing my attention to this approach, which he uses in his excellent [wikilink plugin](https://github.com/jamiekowalski/TaskPaper-extra/blob/master/wikilink.ftplugin/README.md) for TaskPaper) 70 | 71 | 72 | -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/TaskPaper.textexpander: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | groupInfo 6 | 7 | expandAfterMode 8 | 1 9 | groupName 10 | TaskPaper 11 | 12 | snippetsTE2 13 | 14 | 15 | abbreviation 16 | 17 | abbreviationMode 18 | 0 19 | creationDate 20 | 2014-04-24T21:25:07Z 21 | flags 22 | 0 23 | label 24 | 25 | modificationDate 26 | 2014-04-24T21:30:40Z 27 | plainText 28 | INSTALLATION 29 | These snippets require installation of the plugin at: 30 | https://github.com/RobTrew/tree-tools/tree/master/FoldingText%202%20plugins%20and%20scripts 31 | 32 | In FoldingText: 33 | 1. File > Open Application Folder 34 | 2. Copy the relative dates.ftplugin folder into the Plugins folder 35 | 3. Restart FoldingText 36 | snippetType 37 | 0 38 | useCount 39 | 0 40 | uuidString 41 | 6CCCC4BB-0F28-4953-92CA-B9C872699CA8 42 | 43 | 44 | abbreviation 45 | @st 46 | abbreviationMode 47 | 0 48 | creationDate 49 | 2012-11-24T23:03:24Z 50 | flags 51 | 0 52 | label 53 | 54 | lastUsed 55 | 2014-04-24T21:24:18Z 56 | modificationDate 57 | 2014-04-24T21:20:46Z 58 | plainText 59 | @start(%snippet:smt%%|) 60 | snippetType 61 | 0 62 | useCount 63 | 45 64 | uuidString 65 | 8ED78F8D-2065-49F4-A9A4-5775486B9E8C 66 | 67 | 68 | abbreviation 69 | @du 70 | abbreviationMode 71 | 0 72 | creationDate 73 | 2012-11-24T23:03:24Z 74 | flags 75 | 0 76 | label 77 | 78 | lastUsed 79 | 2014-04-24T21:22:54Z 80 | modificationDate 81 | 2014-04-24T20:53:11Z 82 | plainText 83 | @due(%snippet:smt%%|) 84 | snippetType 85 | 0 86 | useCount 87 | 45 88 | uuidString 89 | 8825976A-C013-447A-A498-36B2C6237F8B 90 | 91 | 92 | abbreviation 93 | smt 94 | abbreviationMode 95 | 0 96 | creationDate 97 | 2012-11-24T21:52:58Z 98 | flags 99 | 1 100 | label 101 | Convert date time or adjustment to ISO 102 | lastUsed 103 | 2012-11-25T12:26:42Z 104 | modificationDate 105 | 2014-04-24T21:32:44Z 106 | plainText 107 | property pName : "smalltime" property pDescription : "'open date phrase panel'" property pVer : "0.1" property pPluginLink : "https://github.com/RobTrew/tree-tools/tree/master/FoldingText%202%20plugins%20and%20scripts" tell application "TaskPaper" set lstDocs to documents if lstDocs ≠ {} then activate tell item 1 of lstDocs set varResult to (evaluate script "function(editor, options) { 108 | return editor.performCommand(options.command); 109 | }" with options {command:pName}) end tell if varResult = false then set strBtnLink to "Go to Plugin page" tell (display dialog "Plugin not installed:" & linefeed & linefeed & pName & " – " & pDescription & linefeed & linefeed & pPluginLink buttons {strBtnLink, "OK"} default button "OK" with title pName & " ver. " & pVer) if button returned = strBtnLink then ¬ tell me to do shell script "open " & quoted form of pPluginLink end tell end if end if end tell 110 | snippetType 111 | 2 112 | useCount 113 | 11 114 | uuidString 115 | 1B2445AD-8BF5-47A6-BCA5-4B123720D937 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /relative dates and date adjustments.ftplugin/FoldingTextOLD.textexpander: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | groupInfo 6 | 7 | expandAfterMode 8 | 1 9 | groupName 10 | FoldingText 11 | 12 | snippetsTE2 13 | 14 | 15 | abbreviation 16 | 17 | abbreviationMode 18 | 0 19 | creationDate 20 | 2014-04-24T21:25:07Z 21 | flags 22 | 0 23 | label 24 | 25 | modificationDate 26 | 2014-04-24T21:30:40Z 27 | plainText 28 | INSTALLATION 29 | These snippets require installation of the plugin at: 30 | https://github.com/RobTrew/tree-tools/tree/master/FoldingText%202%20plugins%20and%20scripts 31 | 32 | In FoldingText: 33 | 1. File > Open Application Folder 34 | 2. Copy the relative dates.ftplugin folder into the Plugins folder 35 | 3. Restart FoldingText 36 | snippetType 37 | 0 38 | useCount 39 | 0 40 | uuidString 41 | DD26392B-534A-41FE-9933-4A05D5BA513D 42 | 43 | 44 | abbreviation 45 | @st 46 | abbreviationMode 47 | 0 48 | creationDate 49 | 2012-11-24T23:03:24Z 50 | flags 51 | 0 52 | label 53 | 54 | lastUsed 55 | 2014-04-24T21:24:18Z 56 | modificationDate 57 | 2014-04-24T21:20:46Z 58 | plainText 59 | @start(%snippet:smt%%|) 60 | snippetType 61 | 0 62 | useCount 63 | 45 64 | uuidString 65 | 00B9035B-DE9F-464C-84C5-DD4A63797369 66 | 67 | 68 | abbreviation 69 | @du 70 | abbreviationMode 71 | 0 72 | creationDate 73 | 2012-11-24T23:03:24Z 74 | flags 75 | 0 76 | label 77 | 78 | lastUsed 79 | 2014-04-24T21:22:54Z 80 | modificationDate 81 | 2014-04-24T20:53:11Z 82 | plainText 83 | @due(%snippet:smt%%|) 84 | snippetType 85 | 0 86 | useCount 87 | 45 88 | uuidString 89 | DCD61060-751F-4436-B9F5-322B0EB5B166 90 | 91 | 92 | abbreviation 93 | smt 94 | abbreviationMode 95 | 0 96 | creationDate 97 | 2012-11-24T21:52:58Z 98 | flags 99 | 1 100 | label 101 | Convert date time or adjustment to ISO 102 | lastUsed 103 | 2012-11-25T12:26:42Z 104 | modificationDate 105 | 2014-04-24T20:49:26Z 106 | plainText 107 | property pName : "smalltime" property pDescription : "'open date phrase panel'" property pVer : "0.1" property pPluginLink : "https://github.com/RobTrew/tree-tools/tree/master/FoldingText%202%20plugins%20and%20scripts" tell application "FoldingText" set lstDocs to documents if lstDocs ≠ {} then activate tell item 1 of lstDocs set varResult to (evaluate script "function(editor, options) { 108 | return editor.performCommand(options.command); 109 | }" with options {command:pName}) end tell if varResult = false then set strBtnLink to "Go to Plugin page" tell (display dialog "Plugin not installed:" & linefeed & linefeed & pName & " – " & pDescription & linefeed & linefeed & pPluginLink buttons {strBtnLink, "OK"} default button "OK" with title pName & " ver. " & pVer) if button returned = strBtnLink then ¬ tell me to do shell script "open " & quoted form of pPluginLink end tell end if end if end tell 110 | snippetType 111 | 2 112 | useCount 113 | 11 114 | uuidString 115 | 3C54AA1B-1647-48C8-BD74-F062E590969C 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /links between plain text and Reminders/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Plain text project files with links to and from Reminders.app 3 | Nothing beats plain text outlines for quickly and flexibly gathering thoughts and developing project structure, and well synched databases like Reminders.app are very good for automating alerts. 4 | 5 | Plain text and Reminders can be linked to each other, and we can script the creation of these links (and the exchange of dates and other details between our notes and our reminders). 6 | 7 | #### Creating links back and forth between plain text and reminders, 8 | 9 | ##### Plain text links to alerts in Reminders.app 10 | 11 | ![](./PlainTextToReminders.png) 12 | 13 | These links are made possible by the [x-apple-reminder://](x-apple-reminder://) URL scheme of Reminders.app 14 | 15 | *FoldingText > View > Show Syntax* 16 | 17 | ![](./x-apple-reminder.png) 18 | 19 | We can create these links automatically, either copying them from existing Reminders, 20 | 21 | ![](./CopyReminderAsMD.png) 22 | 23 | as TaskPaper / FoldingText entries, [with this script,](./CopyReminderAsTaskPaperOrFT.applescript) 24 | 25 | ![](./CopyReminderAsTaskPaperorFT.png) 26 | 27 | (result with MD link and TaskPaper-style tags) 28 | 29 | - Release a raven and a dove after 40 days [🕖](x-apple-reminder://144D021C-06D7-4FE0-AE28-76295FDCB08C) @list(voyage) @alert(2015-03-29 07:00) @priority(2) 30 | 31 | 32 | OR simply as MD links, [with another script.](./CopyReminderAsLink.applescript) 33 | 34 | ![](./CopyReminderAsLink.png) 35 | 36 | (Resulting link to a reminder): 37 | 38 | [🕖](x-apple-reminder://144D021C-06D7-4FE0-AE28-76295FDCB08C) 39 | 40 | Alternatively, we can automatically push details from plain text entries to create new reminders 41 | 42 | 1. Select a line which contains an `@alert(date/time) tag (the date/time can be informal or relative) ... 43 | ![](./SelectLineWithAlert.png) 44 | 45 | 2. Run the script [FTMakeOrUpdateReminder.scpt](./FTMakeOrUpdateReminder.applescript) in Applescript Editor 46 | ![](./FTMakeOrUpdateReminder.png) 47 | 48 | A clock-faced link to a new reminder is created: 49 | ![](./PlainTextToReminders.png) 50 | 51 | and the note of the new reminder also contains a link **back** to the FoldingText entry: 52 | ![](./LinkBackFromReminder.png) 53 | 54 | ##### Links from Reminders.app back to plain text 55 | - There are url schemes for linking to particular lines TaskPaper and Foldingtext files, 56 | - [ftdoc://](https://github.com/RobTrew/txtquery-tools/blob/master/ftdoc%20url%20scheme%20and%20FTCopyAsURL/README.md) 57 | - [tp3doc://](https://github.com/RobTrew/txtquery-tools/blob/master/tp3doc%20url%20scheme%20and%20TP3CopyAsURL/README.md) 58 | - To download and install these url schemes, see: 59 | - [FoldingText 2 url scheme ftdoc://](https://github.com/RobTrew/txtquery-tools/blob/master/ftdoc%20url%20scheme%20and%20FTCopyAsURL/README.md) 60 | - [TaskPaper 3 url scheme tp3doc://](https://github.com/RobTrew/txtquery-tools/blob/master/tp3doc%20url%20scheme%20and%20TP3CopyAsURL/README.md) 61 | 62 | #### Synching adjusted details 63 | 64 | The values of various `@key(value` tags in plain text notes, 65 | 66 | - `@alert(yyyy-mm-dd HH:MM)` 67 | - `@priority(1|2|3)` 68 | - `@done(yyyy-mm-dd HH:MM)` 69 | - `@cal(list name)` 70 | 71 | can be sent back and forth by script between text and Reminders.app. 72 | 73 | - [Pushing details from FoldingText to Reminders](./FTMakeOrUpdateReminder.applescript) 74 | - [Pulling details from Reminders to FoldingText](./FTPullDetailsFROMLinkedReminder.applescript) 75 | - [Toggling @done status at both ends](./FTToggleDoneUpdateReminders.applescript) 76 | 77 | 1. Make some edits in FoldingText to the value(s) of any @alert or @priority tag(s) 78 | ![](./Edits%20to%20date%20and%20priority.png) 79 | 80 | (any new dates or times can be relative or informal) 81 | 82 | 2. then run the create/update script again. 83 | 84 | The clock-face icon and time display will be normalized in FT: 85 | ![](./Icon%20and%20time%20normalized.png) 86 | 87 | and the linked reminder will be updated to the new time and priority level: 88 | ![](./ReminderUpdated.png) 89 | 90 | 91 | #### Using relative date / time expressions 92 | The update script understands the same set of relative and informal date time expressions as the [Relative dates and date adjustment](../relative%20dates%20and%20date%20adjustments.ftplugin/README.md) plugin for FoldingText. 93 | 94 | 95 | #### Installation 96 | The applescripts require installation of [smalltime.ftplugin](../smalltime.ftplugin) and [reminder tools.ftplugin](../reminder%20tools.ftplugin) plugins for FoldingText. 97 | 98 | - [txtQuery Tools repository](https://github.com/RobTrew/txtquery-tools) 99 | - [Download .zip](https://github.com/RobTrew/txtquery-tools/archive/master.zip) 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /ftdoc url scheme and FTCopyAsURL/README.md: -------------------------------------------------------------------------------- 1 | ### Copy FoldingText selection as ftdoc:// link back to document, line, selection and filter state 2 | 3 | The [FTCopyAsURL](./FTCopyAsURL.applescript) applescript copies the currently selected text position in [FoldingText](http://www.foldingtext.com), and creates a url which: 4 | - Links back to the current document, 5 | - restores the current selection, and: 6 | - also restores any filters that are currently applied to the document. 7 | 8 | For example, the following link, which has various optional switches 9 | 10 | `ftdoc:///Users/robintrew/Library/Application%20Support/Notational%20Velocity/notes-2014-06-30.txt?nodepath=///@priority?selnpath=/heil/a%20se/why/who/inte?find=Intermediaries?line=10?startoffset=5?endoffset=19` 11 | 12 | 1. Reopens a particular document in a Notational Velocity text file folder, 13 | 2. restores the `//@priority` filter path from the `?nodepath=` switch 14 | 3. attempts to restore the selection by looking for a node which matches the `?selnpath=` nodepath 15 | 4. if it finds nothing matched by the `?selnpath=` switch, selects the first node with text matching the `?find=` switch 16 | 4. and if all else fails selects whatever is specified by the optional `?line=`, `?startoffset` and `?endoffset=` switches 17 | 18 | Note that the file path and any other text or node paths are automatically uri-encoded by the script. 19 | 20 | ### Applications 21 | - As a kind of bookmark to a particular point in a document 22 | - As a way (when using the [FoldingText CLI](https://www.npmjs.org/package/foldingtext) to generate a single perspective or report from several documents) of creating links which jump back from the perspective to a specific document and line. 23 | 24 | ### Use 25 | - Select a line or phrase in FoldingText 26 | - Run [FTCopyAsURL](./FTCopyAsURL.applescript) 27 | - Paste the resulting ftdoc:// url wherever you need it. 28 | - (It can be used for example, to provide active links between or within FoldingText documents) 29 | 30 | #### ftdoc:// url-scheme for opening a FoldingText document at a specific line 31 | The ftdoc:// url-scheme is registered and handled by an Applescript, [OpenFTDocAtLine](./OpenFTDocAtLine.app): 32 | 33 | ###### OpenFTDocAtLine.app 34 | - Is an [applescript](https://github.com/RobTrew/txtquery-tools/blob/master/ftdoc%20url%20scheme%20and%20FTCopyAsURL/Source%20and%20info.plist%20for%20OpenFTDocAtLine/OpenFTDocAtLine.applescript) saved as an .app bundle 35 | - contains an [Info.plist](https://github.com/RobTrew/txtquery-tools/blob/master/ftdoc%20url%20scheme%20and%20FTCopyAsURL/Source%20and%20info.plist%20for%20OpenFTDocAtLine/Info.plist) which registers `ftdoc://` 36 | - contains an open url handler which: 37 | - Opens a specified text document in [FoldingText](http://www.foldingtext.com) 38 | - applies any filter given in a `?nodepath=` 39 | - selects any line specified in a `?line=` switch 40 | (if the line is hidden by the nodepath filter, the editor unfolds just enough to make the line visible) 41 | - optionally restricts the selection to a part of the line, using any character position specified by either or both of the following switches 42 | - `?startoffset=` 43 | - `?endoffset=` 44 | 45 | NB if you want to create _ftdoc://_ urls ‘by hand’ or with a script of your own, you will need to uri-encode the file path and any nodepath. There are various ways of doing this at the command line, or in an applescript, with something like: 46 | 47 | ``` 48 | on encode(strPath) 49 | do shell script "python -c 'import sys, urllib as ul; print ul.quote(sys.argv[1])' " & quoted form of strPath 50 | end encode 51 | ``` 52 | 53 | Easier, of course, in Javascript: `encodeURI()` for a whole link, or `encodeURIComponent()` for parts excluding the opening scheme name. 54 | 55 | 56 | #### Installation 57 | 58 | For the _ftdoc://_ url scheme to be registered, [OpenFTDocAtLine](./OpenFTDocAtLine.app) needs to be on your system, and needs to have been run at least once. 59 | To do this: 60 | - EITHER extract the copy from the [txtquery-tools](https://github.com/RobTrew/txtquery-tools) repository [zip file](https://github.com/RobTrew/txtquery-tools/archive/master.zip), and ctrl-click to allow the OS X Gate Keeper security system to run it 61 | - OR: 62 | - open the .applescript text version Applescript editor, 63 | - save as an .app bundle 64 | - Ctrl-click on the .app bundle to _Open Package Contents_ 65 | - and replace the info.plist file with the version which declares the url-scheme 66 | 67 | [Repository .zip file](https://github.com/RobTrew/txtquery-tools/archive/master.zip) 68 | 69 | ##### Reference 70 | For an explanation of this approach to registering and handling a url with an applescript.app, and the info.plist in its bundle, 71 | See [http://www.macosxautomation.com/applescript/linktrigger/](http://www.macosxautomation.com/applescript/linktrigger/) 72 | 73 | (Many thanks to Jamie Kowalski for drawing my attention to this approach, which he uses in his excellent [wikilink plugin](https://github.com/jamiekowalski/foldingtext-extra/blob/master/wikilink.ftplugin/README.md) for FoldingText) 74 | 75 | 76 | -------------------------------------------------------------------------------- /reminder tools.ftplugin/main.js: -------------------------------------------------------------------------------- 1 | // Interpret date tags: 2 | // 1. Informal or relative phrases in 3 | // specified date tags in selected line are converted to ISO 4 | // 2. The date and other values are returned to any calling Applescript 5 | // through window.RTpluginReturn, so that a linked Reminder.app 6 | // can be updated or created. 7 | 8 | define(function(require, exports, module) { 9 | 'use strict'; 10 | 11 | var Extensions = require('ft/core/extensions').Extensions, 12 | dateLogic = require('../smalltime.ftplugin/main.js'), 13 | Editor; 14 | 15 | function updateAndReadForLink(Editor, dctArgs) { 16 | 17 | var lstResults = translateDateTags(Editor, dctArgs), 18 | node = lstResults[0], strLine = node.line(), 19 | strText = node.text(), strLabel=dctArgs['linklabel'], 20 | dctReturn = lstResults[1], strTag='', oMatch, dteAlarm, 21 | lstHeat = dctArgs.heat || [], lngHeat = 0, i, 22 | rgxReminder = /\[[^\]]*\]\((x-apple-reminder:\/\/[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12})\)/; 23 | 24 | // Find the highest priority/heat tag and return a 1-based index 25 | lngHeat = lstHeat.length; 26 | for (i=0; i 2) i=2; 31 | dctReturn.heat = i+1; 32 | break; 33 | } 34 | } 35 | 36 | // Separate any Reminders UUID from the main text entry 37 | oMatch = rgxReminder.exec(strText); 38 | if (oMatch) { 39 | dctReturn.uuid = oMatch[1]; 40 | dctReturn.text = strText.substring(0, oMatch.index-1); 41 | } else dctReturn.text = strText; 42 | 43 | //translate any linklabel 44 | if (strLabel) { 45 | dteAlarm=dctReturn['alarmtime']; 46 | if (dteAlarm) strLabel=dateLogic.timeEmoji(strLabel, dteAlarm); 47 | else strLabel=dateLogic.timeEmoji(strLabel); 48 | } else strLabel='??'; 49 | dctReturn['linklabel']=strLabel; 50 | 51 | // return the harvest for any calling Applescript 52 | 53 | return dctReturn; 54 | } 55 | 56 | // translate values of specified date tags to ISO (if informal/relative) 57 | // and return any values for uuid, text, alarm tag, priority, 58 | // and any other requested date tags 59 | function translateDateTags(Editor, dctArgs) { 60 | var lstUpdateTags = ['alarm'], 61 | tree = Editor.tree(), 62 | node = Editor.selectedRange().startNode, 63 | dctReturn = {'uuid':null, 'text':null, 'alarm':null, 64 | 'alarmlist':null,'datetext':null, 'iso':null, 'heat':null}, 65 | dctNodeTags = node.tags(), 66 | lstNodeTags = Object.keys(dctNodeTags), strKey='', strVal, 67 | lngSeconds=0, lstKeySet, lngTag, 68 | strTrans='', dteUpdated, strAlarmKey, blnDelta, i; 69 | 70 | // read any arguments to override defaults for alarm, 71 | // other date tags, and priority tags 72 | if (typeof dctArgs !== 'undefined') { 73 | strAlarmKey = dctArgs.alarm || 'alarm'; 74 | if (strAlarmKey.charAt(0) === '@') 75 | strAlarmKey = strAlarmKey.substring(1); 76 | lstUpdateTags = dctArgs.others || []; 77 | lstUpdateTags.push(strAlarmKey); 78 | } 79 | // strip any leading '@' from date tags to get the key 80 | lngTag = lstUpdateTags.length; 81 | for (i=0; i irngEnd)); 55 | } 56 | 57 | // return the selected text 58 | // or select and return the value of any @key(value) tag at the cursor 59 | function selectDate() { 60 | // is the cursor in or next to a tag ? 61 | var rngSeln = Editor.selectedRange(), node = rngSeln.startNode, 62 | lngLineStart = node.lineTextStart(), 63 | oTree = Editor.tree(), 64 | iSelnFrom = rngSeln.location() - lngLineStart, 65 | iSelnTo = iSelnFrom + rngSeln.length(), 66 | iTagFrom = 0, iTagTo = 0, iValFrom=0, 67 | strLine = node.line(), 68 | nodeTags = node.tags(), strKey, strVal, lngChars=0, 69 | lstTag=[], lngKeyChars, lngValChars, strText; 70 | 71 | // look for any tag at the cursor 72 | if (nodeTags) { 73 | Object.keys(nodeTags).forEach(function(strKey) { 74 | strVal = node.tag(strKey); 75 | lngKeyChars = strKey.length; 76 | if (strVal) { 77 | lngValChars = strVal.length; 78 | lngChars = lngKeyChars + lngValChars + 3; 79 | lngKeyChars +=2; 80 | } else { 81 | lngChars = lngKeyChars + 1; 82 | lngValChars = 0; 83 | lngKeyChars +=1; 84 | } 85 | iTagFrom = strLine.indexOf('@' + strKey); 86 | iTagTo = iTagFrom + lngChars; 87 | 88 | // is the cursor in or touching a tag ? 89 | if (overLapsWith(iSelnFrom, iSelnTo, 90 | iTagFrom, iTagTo)) { 91 | lstTag.push([iTagFrom + lngKeyChars, 92 | lngValChars]); 93 | } 94 | }); 95 | if (lstTag.length) { // cursor in/touching tag 96 | iValFrom = lngLineStart + lstTag[0][0]; 97 | lngValChars = lstTag[0][1]; 98 | //tag selected but no value 99 | //place cursor between pair of new brackets 100 | if (!lngValChars) { 101 | rngSeln = oTree.createRangeFromLocation( 102 | iValFrom, 0); 103 | Editor.replaceSelection('()'); 104 | iValFrom +=1; 105 | } 106 | rngSeln = oTree.createRangeFromLocation( 107 | iValFrom, lngValChars); 108 | Editor.setSelectedRange(rngSeln); 109 | } 110 | } 111 | 112 | strText = Editor.selectedText(); 113 | return strText; 114 | } 115 | 116 | Extensions.add('com.foldingtext.editor.commands', { 117 | name: 'informal dates and adjustments', 118 | description: 'open date phrase panel', 119 | performCommand: show_date_panel 120 | }); 121 | 122 | Extensions.add('com.foldingtext.editor.init', function(editor) { 123 | Editor = editor; 124 | 125 | panel = new Panel({ 126 | className: 'RTDatePanel', 127 | placeholder: 'enter date phrase ...', 128 | onReturn: function() { 129 | panel.clear(); 130 | Editor.performCommand('moveRight'); 131 | Editor.performCommand('moveRight'); 132 | }, 133 | onBlur: function() { 134 | panel.clear(); 135 | }, 136 | onTextChange: function() { 137 | translatePhrase(panel.input.value); 138 | } 139 | }); 140 | 141 | Editor.addKeyMap({ 142 | "Cmd-Alt-'" : 'informal dates and adjustments' 143 | //Shift-, Cmd-, Ctrl-, and Alt- (in that order!) to 144 | }); 145 | Editor.addKeyMap({ 146 | //open bracket with Cmd held down 147 | "Shift-Cmd-9" : 'informal dates and adjustments' 148 | }); 149 | }); 150 | 151 | 152 | 153 | }); 154 | -------------------------------------------------------------------------------- /structure editing/MoveSelnToSectionJS.applescript: -------------------------------------------------------------------------------- 1 | // Yosemite JavaScript for Applications (JXA) version 2 | function run() { 3 | 4 | var dctScript = { 5 | title: "Move line(s) to new section", 6 | version: "0.04", 7 | author: "RobTrew", 8 | license: "MIT: ALL copies should include the license notice at https://github.com/RobTrew/txtquery-tools" 9 | } 10 | 11 | var blnDebug = 0; // zero unless using the symbolic debugger 12 | 13 | 14 | // JSLR 15 | // edit to `blnTPIndent = true` for an extra TaskPaper indent - false for default formatting 16 | var blnTPIndent = false; 17 | 18 | 19 | var docsFT = Application("FoldingText").documents(), 20 | oDoc = docsFT.length ? docsFT[0] : null; 21 | 22 | if (!oDoc) return null; 23 | 24 | 25 | // GET LIST OF HEADING TITLES AND THEIR MINIMUM PATHS (+ any selected text) 26 | var fnProcess = (blnDebug ? oDoc.debug : oDoc.evaluate), 27 | lstHeadsAndSeln = fnProcess({ 28 | script: sectionList.toString() 29 | }), 30 | lstMenu = lstHeadsAndSeln[0], 31 | 32 | lstSectionPaths = lstHeadsAndSeln[1], 33 | strSeln = lstHeadsAndSeln[2], 34 | lstNumberedMenu = numberedMenu(lstMenu); 35 | 36 | // Exit here, if the document contains no headers 37 | if (!lstNumberedMenu.length) return 0; 38 | 39 | // GET USER CHOICE 40 | var app = Application.currentApplication(), 41 | mbChoice, varResult; 42 | 43 | app.includeStandardAdditions = true; 44 | 45 | mbChoice = maybeChoiceIndex( 46 | app.chooseFromList(lstNumberedMenu, { 47 | withTitle: dctScript.title, 48 | withPrompt: "Choose new section for selected line(s):\n\n" + strSeln + 49 | "\n", 50 | defaultItems: lstNumberedMenu[0], 51 | okButtonName: "Move", 52 | cancelButtonName: "Cancel", 53 | multipleSelectionsAllowed: false, 54 | emptySelectionAllowed: false, 55 | }) 56 | ); 57 | 58 | 59 | // IF A TARGET SECTION HAS BEEN CHOSEN, MOVE THE SELECTION 60 | if (mbChoice) { 61 | return fnProcess({ 62 | script: itemMove.toString(), 63 | withOptions: { 64 | // JSLR 65 | taskpaperindent: blnTPIndent, 66 | targetpath: lstSectionPaths[mbChoice] 67 | } 68 | }); 69 | } else return null; 70 | } 71 | 72 | // Number of any target section chosen, or null if no choice 73 | function maybeChoiceIndex(varResult) { 74 | return varResult ? 75 | parseInt(varResult[0].split('\t')[0]) : null; 76 | } 77 | 78 | // move selected item(s) to target path 79 | function itemMove(editor, options) { 80 | 81 | // FIND THE TARGET SECTION 82 | var oTree = editor.tree(), 83 | oNewParent = oTree.evaluateNodePath(options.targetpath + '[0]')[0], 84 | rngSeln = editor.selectedRange(), 85 | lstNodes = rngSeln.nodesInRange(), 86 | lstSeen = [], 87 | lstSelnRoots = [], 88 | strID, 89 | 90 | // edit (1 of 2) for JSLR (using TaskPaper format in FT) 91 | blnExtraIndent = options.taskpaperindent; 92 | 93 | // WORK ONLY WITH THE HIGHEST LEVEL NODES IN THE SELECTION 94 | // (CHILDREN TRAVEL WITH THEM) 95 | lstNodes.forEach(function (oNode) { 96 | strID = oNode.parent.id; 97 | if (lstSeen.indexOf(strID) == -1) { 98 | lstSelnRoots.push(oNode); 99 | lstSeen.push(oNode.id); 100 | } 101 | }); 102 | 103 | // APPEND EACH SELECTED PARENT NODE TO THE CHOSEN TARGET NODE 104 | // Taking children with each parent, unless we are relocating an ancestor under one 105 | // of its own descendants (demoted ancestors travel alone) 106 | 107 | lstSelnRoots.forEach(function (oNode) { 108 | if (oNewParent.isAncestorOfSelf(oNode)) //detach traveller from its descendants before moving it 109 | oTree.removeNode(oNode); 110 | 111 | oNewParent.appendChild(oNode); // by default children travel with parents 112 | 113 | // edit (2 of 2) for JSLR (using TaskPaper format in FT) 114 | if (blnExtraIndent) 115 | oNode.setLine('\t' + oNode.line()) 116 | }); 117 | } 118 | 119 | // GATHER LIST OF SECTIONS FOR THE UI MENU 120 | function sectionList(editor) { 121 | var libNodePath = require('ft/core/nodepath').NodePath, 122 | oTree = editor.tree(), 123 | lstHeads = oTree.evaluateNodePath('//@type=heading'), 124 | lstSelnNodes = editor.selectedRange().nodesInRange(), 125 | lngSeln = lstSelnNodes.length; 126 | 127 | var lstMenu = [], 128 | lstPath = [], 129 | strText = '', 130 | rngLines; 131 | 132 | 133 | // get any selected text 134 | if (lngSeln) { 135 | rngLines = oTree.createRangeFromNodes(lstSelnNodes[0], 0, lstSelnNodes[ 136 | lngSeln - 1], -1); 137 | strText = rngLines.textInRange().trim(); 138 | } 139 | 140 | 141 | // and get each heading, plus its full outline path (compressed) 142 | lstHeads.forEach(function (oHead) { 143 | // header title 144 | lstMenu.push( 145 | [Array(oHead.typeIndentLevel() + 1).join('#'), 146 | oHead.text() 147 | ].join(' ') 148 | ); 149 | // and compressed header path 150 | lstPath.push(libNodePath.calculateMinNodePath(oHead)); 151 | }); 152 | 153 | 154 | // headers, their outline paths, and any selected text 155 | return [lstMenu, lstPath, strText]; 156 | } 157 | 158 | 159 | // List of strings re written with zero-padded numeric prefixes 160 | // [strItem] ? maybeStringSeparator ? [strNumberedItem] 161 | function numberedMenu(lstItems, strSeparator) { 162 | // default separator between number string and item string 163 | strSeparator = strSeparator || '\t'; 164 | 165 | var lng = lstItems.length, 166 | lngPadWidth = lng.toString().length; 167 | 168 | // Numbers string padded to left with zeros to get fixed width 169 | // intNumber --> intDigits --> strDigits 170 | function zeroPad(intNumber, intDigits) { 171 | var strUnpadded = intNumber.toString(), 172 | intUnpadded = strUnpadded.length; 173 | 174 | return Array((intDigits - intUnpadded) + 1).join('0') + strUnpadded; 175 | } 176 | 177 | // list rewritten with numeric prefixes of even length 178 | // left-padded with zeros where needed 179 | return lstItems.map(function (str, i) { 180 | return zeroPad(i, lngPadWidth) + strSeparator + str; 181 | }); 182 | } -------------------------------------------------------------------------------- /ftdoc url scheme and FTCopyAsURL/Source and info.plist for OpenFTDocAtLine/OpenFTDocAtLine.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Register and handle ftdoc:// url scheme" property pVer : "0.04" property pAuthor : "Rob Trew" property pDescription : " 2 | 3 | Use in conjunction with the 'FTCopyAsURL' Applescript to get 4 | a URL which opens the specified document, optionally restoring selection and filter state. 5 | 6 | " -- Registers the url-scheme ftdoc://encoded-file-path with optional switches: --[?nodepath=//@due] -- nodepath used to apply a filter --[?selnpath=] -- nodepath used to specify a selection --[?find=] -- text string to find --[?line=N][?startoffset=0][?endoffset=-1] -- where line is zero-based and defaults to 0 -- startoffset is an offset of a number of characters from the start of the line -- endoffset is ditto -- and the url opens the document in FoldingText: -- 1. Applying any specified ?nodepath= value as a filter -- 2. Selecting the first line that matches (in the following order) -- -- the value of ?selnpath= ?find= or ?line= -- 3. Restricts the selection to a subset of a line selected by number if startoffset > 0 or endoffset ≠ -1 -- for the approach to registering and handling a url with an applescript.app and the .plist in its bundle, -- see http://www.macosxautomation.com/applescript/linktrigger/ property piNodePath : 1 property piSelnPath : 2 property piFindText : 3 property piLine : 4 property piStartOffset : 5 property piEndOffset : 6 property plstKeys : {"nodepath", "selnpath", "find", "line", "startoffset", "endoffset"} property plngKeys : length of plstKeys property pjsSelect : " 7 | 8 | function(editor, options) { 9 | function getValue(strSwitch) { 10 | return lstSwitches[lstSwitches.indexOf('?' + strSwitch + '=')+1]; 11 | } 12 | 13 | var tree= editor.tree(), 14 | oNode, rngSeln, 15 | //options.filepath, options.switches, options.keys 16 | lstKeys = options.keys, 17 | strRegex = '(\\\\?' + lstKeys.join('=|\\\\?') + '=)', 18 | oRegex = new RegExp(strRegex, 'g'), 19 | strPath = decodeURIComponent(options.filepath), 20 | strSwitches = decodeURIComponent(options.switches), 21 | lstSwitches = strSwitches.split(oRegex), 22 | strPath, strLineNum, 23 | strSelnPath, 24 | strFind, 25 | strStartOffset, strEndOffset, 26 | lngLine, lngStartOffset=0, lngEndOffset=-1, 27 | varStartOffset, varEndOffset, 28 | lstMatches=[], lstRanges=[], i; 29 | 30 | 31 | // Try to restore any selection that is specified 32 | if (strPath = getValue('nodepath')) { 33 | //restore any filter 34 | editor.setNodePath(strPath); 35 | } 36 | 37 | 38 | strSelnPath = getValue('selnpath'); 39 | strFind = getValue('find'); 40 | 41 | if (strSelnPath || strFind) { 42 | if (strSelnPath) { 43 | lstMatches = tree.evaluateNodePath(strSelnPath); 44 | } 45 | if (strFind && (lstMatches.length == 0)) { 46 | lstMatches = tree.evaluateNodePath('//\"' + strFind + '\"'); 47 | } 48 | if (lstMatches.length) { 49 | lstMatches.forEach(function(varNode) { 50 | lstRanges.push(tree.createRangeFromNodes( 51 | varNode, 0, varNode, -1)); 52 | // unfold if this range is hidden 53 | if (editor.nodeIsHiddenInFold(varNode)) { 54 | editor.expandToRevealNode(varNode); 55 | } 56 | }); 57 | editor.setSelectedRanges(lstRanges); 58 | //Make sure that at least the first of any selections is visible 59 | editor.scrollRangeToVisible(lstRanges[0]); 60 | } 61 | } else { 62 | 63 | // make any selection specified by line number etc 64 | if (strLine = getValue('line')) { 65 | lngLine = parseInt(strLine, 10); 66 | if (!(isNaN(lngLine))) { 67 | oNode = tree.lineNumberToNode(lngLine); 68 | if (editor.nodeIsHiddenInFold(oNode)) { 69 | editor.expandToRevealNode(oNode); 70 | editor.scrollToLine(lngLine); 71 | } 72 | 73 | if (strStartOffset = getValue('startoffset')) { 74 | varStartOffset = parseInt(strStartOffset, 10); 75 | if (!isNaN(varStartOffset)) { 76 | lngStartOffset = varStartOffset; 77 | } 78 | } 79 | 80 | if (strEndOffset = getValue('endoffset')) { 81 | varEndOffset = parseInt(strEndOffset, 10); 82 | if (!isNaN(varEndOffset)) { 83 | lngEndOffset = varEndOffset; 84 | } 85 | } 86 | 87 | rngSeln = tree.createRangeFromNodes( 88 | oNode, lngStartOffset, oNode, lngEndOffset); 89 | editor.setSelectedRange(rngSeln); 90 | } 91 | } 92 | } 93 | } 94 | " on open location strURL set recParse to pathAndSwitches(strURL) if recParse is not missing value then set strPath to Decode(filepath of recParse) tell application "FoldingText" set oDoc to (open strPath) tell oDoc set varResult to (evaluate script pjsSelect with options (recParse & {keys:plstKeys})) activate end tell end tell end if end open location --on Decode(strEncodedPath) -- set strCMD to "JSC=/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc --\"$JSC\" -e \"print(decodeURI('" & strEncodedPath & "'));\"" -- return do shell script strCMD --end Decode on Decode(strEncodedPath) set strCMD to " 95 | urldecode() { 96 | local url_encoded=\"${1//+/ }\" 97 | printf '%b' \"${url_encoded//%/\\x}\" 98 | } 99 | urldecode '" & strEncodedPath & "'" return do shell script strCMD end Decode on pathAndSwitches(strURL) -- we can't simply split on '?' as there may be '?' in the text -- extracting the file in .js would require an active document, -- so we do it here to save the time and distraction caused by creating one set strSwitches to "" set {dlm, my text item delimiters} to {my text item delimiters, "ftdoc://"} set lstParts to text items of strURL if length of lstParts < 2 then set varParse to missing value else set strTarget to item 2 of lstParts set lngFull to length of strTarget set lngClosest to lngFull repeat with varKey in plstKeys set my text item delimiters to ("?" & varKey & "=") set lstParts to text items of strTarget if length of lstParts > 1 then set lngPosn to length of item 1 of lstParts if lngPosn < lngClosest then set lngClosest to lngPosn end if end repeat set strPath to text 1 thru lngClosest of strTarget if lngClosest < lngFull then set strSwitches to text (lngClosest + 1) thru -1 of strTarget end if set varParse to {filepath:strPath, switches:strSwitches} end if set my text item delimiters to dlm return varParse end pathAndSwitches ---- "ftdoc://encoded-file-path[?nodepath=//@due][?line=N][?startoffset=0][?endoffset=-1]" -------------------------------------------------------------------------------- /utilities/SafariClippings.applescript: -------------------------------------------------------------------------------- 1 | function run() { 2 | var dct = { 3 | title: "Append Safari clipboard to end of file", 4 | ver: "0.4", 5 | description: "Creates MD header with page title, url & timestamp", 6 | author: "RobTrew copyright 2014", 7 | license: "MIT", 8 | site: "https://github.com/RobTrew/txtquery-tools" 9 | }; 10 | 11 | var appSafari = Application("Safari"); 12 | if (appSafari.windows.length < 1) return 'No windows open in Safari ...'; 13 | 14 | var dctOptions = { 15 | $today:fmtTP(new Date(),false), 16 | clipfile:"notes-{$today}.txt", // or just 'clippings.txt' etc 17 | clipfolder: "$HOME/Library/Application Support/Notational Velocity/", 18 | clipheadings: "### ", 19 | urlprefix: "- ", 20 | cliptimetag: "clip", 21 | useNSnotification: false, // turn off for KeyBoard Maestro 22 | clippedsound: 'Pop', 23 | problemsound: 'Blow' 24 | }; 25 | //ALTERNATIVE SOUND NAMES 26 | //Basso Frog Hero Pop Submarine 27 | //Blow Funk Morse Purr Tink 28 | //Bottle Glass Ping Sosumi 29 | 30 | //PREPARE FOR ANY NOTIFICATION 31 | ObjC.import('Cocoa'); 32 | try { 33 | ObjC.registerSubclass({ 34 | name: 'BriefNotify', 35 | methods: { 36 | 'userNotificationCenter:shouldPresentNotification:': { 37 | types: ['bool', ['id', 'id']], 38 | implementation: function (center, notification) { 39 | return true; 40 | } 41 | } 42 | } 43 | }); 44 | } catch (e) {} // already defined 45 | 46 | 47 | 48 | 49 | var app = Application.currentApplication(), 50 | oWindow = appSafari.windows[0], 51 | oTab = oWindow.currentTab, 52 | strName = oWindow.name(), 53 | strURL = oTab.url(), 54 | strLink = '[' + strName + '](' + strURL + ')', 55 | strSetUTF8='LANGSTATE="$(defaults read -g AppleLocale).UTF-8"; if [[ "$LC_CTYPE" != *"UTF-8"* ]]; then export LC_ALL="$LANGSTATE" ; fi; ', 56 | strHeading, strRefce, strCmd, 57 | strClip = '', 58 | strPath, strQuotedPath='', 59 | oBriefNotify = $.BriefNotify.alloc.init, 60 | strMsg, strDetail, strSound = '', 61 | strLastPage='', 62 | strTag='@' + dctOptions.cliptimetag + "(" + fmtTP(new Date()) + ")", 63 | varResult; 64 | 65 | function expandTokens(strSource, dctTokens) { //patterns like {$token} replaced by value of dctTokens.$token 66 | var rgxToken = /\{(\$\w+)\}/g, 67 | strTrans = strSource, 68 | oMatch=rgxToken.exec(strSource); 69 | 70 | while (oMatch) { 71 | strTrans=strTrans.replace(oMatch[0], dctTokens[oMatch[1]]); 72 | oMatch=rgxToken.exec(strSource); 73 | } 74 | return strTrans; 75 | } 76 | 77 | 78 | function makePath(strFolder, strFileName) { 79 | var strCMD = 'fldr=$(echo "' + strFolder + '"); if [ -e \"$fldr\" ]; then echo $fldr; fi', 80 | strPath=app.doShellScript(strCMD).trim(), 81 | strFullPath=''; 82 | 83 | if (strPath) { 84 | if (strPath[strPath.length-1]==='/') 85 | strFullPath=strPath+strFileName 86 | else strFullPath=strPath+'/'+strFileName 87 | }; 88 | return strFullPath; 89 | } 90 | 91 | // Taskpaper-style datetime stamp yyy-mm-dd HH:MM 92 | function fmtTP(dte, blnTime) { 93 | var strDate = '', 94 | strDay = '', 95 | strTime = '', 96 | blnAddTime=(typeof blnTime)==='undefined'?true:blnTime; 97 | 98 | 99 | strDay = [dte.getFullYear(), ('0' + (dte.getMonth() + 100 | 1)).slice(-2), ('0' + dte.getDate()).slice(-2)].join('-'); 101 | strTime = [('00' + dte.getHours()).slice(-2), ('00' + 102 | dte.getMinutes()).slice(-2)].join(':'); 103 | if (blnAddTime && (strTime !== '00:00')) strDate = [strDay, strTime].join(' '); 104 | else strDate = strDay; 105 | return strDate; 106 | } 107 | 108 | // single quoting for bash shell 109 | function shellEscaped(cmd) { 110 | return '\'' + cmd.replace(/\'/g, "'\\''") + '\''; 111 | } 112 | 113 | 114 | 115 | 116 | function postNote(strTitle, txtInfo, strSoundName) { 117 | var noteFlash = $.NSUserNotification.alloc.init, 118 | strSound = strSoundName || 'Glass'; 119 | 120 | noteFlash.title = strTitle; 121 | noteFlash.informativeText = txtInfo; 122 | noteFlash.soundName = strSound; 123 | $.NSUserNotificationCenter.defaultUserNotificationCenter.deliverNotification(noteFlash); 124 | } 125 | 126 | function selnAsHTML() { 127 | var selnWin = window.getSelection(), 128 | lng = selnWin.rangeCount, 129 | oDiv; 130 | 131 | if (lng) { 132 | oDiv = document.createElement('div'); 133 | for (var i = 0; i < lng; i++) { 134 | oDiv.appendChild(selnWin.getRangeAt(i).cloneContents()); 135 | } 136 | return oDiv.innerHTML; 137 | } 138 | return ''; 139 | } 140 | 141 | 142 | app.includeStandardAdditions=true; 143 | 144 | strFullFileName=expandTokens(dctOptions.clipfile, dctOptions); 145 | 146 | strPath = makePath(dctOptions.clipfolder, strFullFileName); 147 | if (strPath) strQuotedPath = '"' + strPath + '"' 148 | else return "Folder not found" 149 | 150 | // Get most recent link (if any, in clippings file) 151 | strCMD="CLIPFILE=" + strQuotedPath + ";if [ -e \"$CLIPFILE\" ]; then cat -n \"$CLIPFILE\" | sort -t: -k 1nr,1 | grep -o -m 1 \'\\[.*\\](.*)\' ; fi" 152 | //return strCMD; 153 | try { 154 | strLastPage=app.doShellScript(strCMD); 155 | } catch (e) { strLastPage=''}; 156 | 157 | 158 | // Get the selected HTML 159 | 160 | 161 | 162 | 163 | if (strLastPage !== strLink) { 164 | // Creating new heading from web page name and MD link to URL 165 | strHeading = dctOptions.clipheadings + strName; 166 | strRefce = dctOptions.urlprefix + strLink; 167 | strClip = [strHeading, strRefce, ''].join('\n') + '\n\n'; 168 | 169 | // and append heading and clipboard text to the end of the file 170 | strCMD = strSetUTF8 + 'echo ' + 171 | shellEscaped(strClip) + ' >> ' + strQuotedPath +'; pbpaste -Prefer txt >> ' + 172 | strQuotedPath + ' ; printf " ' + strTag + '\n\n" >> ' + strQuotedPath; 173 | } else { 174 | strCMD = strSetUTF8 + 'pbpaste -Prefer txt >> ' + strQuotedPath + ' ; printf " ' + strTag + '\n\n" >> ' + strQuotedPath; 175 | } 176 | 177 | 178 | try { 179 | varResult = app.doShellScript(strCMD); 180 | } catch (e) { 181 | varResult = e.message; 182 | } 183 | 184 | if (!varResult) { 185 | strMsg = "Appended to " + dctOptions.clipfile; 186 | strDetail = strLink; 187 | strSound = dctOptions.clippedsound; 188 | } else { 189 | strMsg = "NOT clipped ..."; 190 | strDetail = varResult; 191 | strSound = dctOptions.problemsound; 192 | } 193 | 194 | if (dctOptions.useNSnotification) { 195 | $.NSUserNotificationCenter.defaultUserNotificationCenter.delegate = oBriefNotify; 196 | postNote(strMsg, strDetail, strSound); 197 | } 198 | return strFullFileName; 199 | 200 | } -------------------------------------------------------------------------------- /tp3doc url scheme and TP3CopyAsURL/Source and info.plist for OpenTP3DocAtLine/OpenTP3DocAtLine.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Register and handle tp3doc:// url scheme" property pVer : "0.04" property pAuthor : "Rob Trew" property pDescription : " 2 | 3 | Use in conjunction with the 'TP3CopyAsURL' Applescript to get 4 | a URL which opens the specified document, optionally restoring selection and filter state. 5 | 6 | " -- Registers the url-scheme tp3doc://encoded-file-path with optional switches: --[?nodepath=//@due] -- nodepath used to apply a filter --[?selnpath=] -- nodepath used to specify a selection --[?find=] -- text string to find --[?line=N][?startoffset=0][?endoffset=-1] -- where line is zero-based and defaults to 0 -- startoffset is an offset of a number of characters from the start of the line -- endoffset is ditto -- and the url opens the document in TaskPaper: -- 1. Applying any specified ?nodepath= value as a filter -- 2. Selecting the first line that matches (in the following order) -- -- the value of ?selnpath= ?find= or ?line= -- 3. Restricts the selection to a subset of a line selected by number if startoffset > 0 or endoffset ≠ -1 -- for the approach to registering and handling a url with an applescript.app and the .plist in its bundle, -- see http://www.macosxautomation.com/applescript/linktrigger/ property piNodePath : 1 property piSelnPath : 2 property piFindText : 3 property piLine : 4 property piStartOffset : 5 property piEndOffset : 6 property plstKeys : {"nodepath", "selnpath", "find", "line", "startoffset", "endoffset"} property plngKeys : length of plstKeys property pjsSelect : " 7 | 8 | function(editor, options) { 9 | function getValue(strSwitch) { 10 | return lstSwitches[lstSwitches.indexOf('?' + strSwitch + '=')+1]; 11 | } 12 | 13 | var tree= editor.tree(), 14 | oNode, rngSeln, 15 | //options.filepath, options.switches, options.keys 16 | lstKeys = options.keys, 17 | strRegex = '(\\\\?' + lstKeys.join('=|\\\\?') + '=)', 18 | oRegex = new RegExp(strRegex, 'g'), 19 | strPath = decodeURIComponent(options.filepath), 20 | strSwitches = decodeURIComponent(options.switches), 21 | lstSwitches = strSwitches.split(oRegex), 22 | strPath, strLineNum, 23 | strSelnPath, 24 | strFind, 25 | strStartOffset, strEndOffset, 26 | lngLine, lngStartOffset=0, lngEndOffset=-1, 27 | varStartOffset, varEndOffset, 28 | lstMatches=[], lstRanges=[], i; 29 | 30 | 31 | // Try to restore any selection that is specified 32 | if (strPath = getValue('nodepath')) { 33 | //restore any filter 34 | editor.setNodePath(strPath); 35 | } 36 | 37 | 38 | strSelnPath = getValue('selnpath'); 39 | strFind = getValue('find'); 40 | 41 | if (strSelnPath || strFind) { 42 | if (strSelnPath) { 43 | lstMatches = tree.evaluateNodePath(strSelnPath); 44 | } 45 | if (strFind && (lstMatches.length == 0)) { 46 | lstMatches = tree.evaluateNodePath('//\"' + strFind + '\"'); 47 | } 48 | if (lstMatches.length) { 49 | lstMatches.forEach(function(varNode) { 50 | lstRanges.push(tree.createRangeFromNodes( 51 | varNode, 0, varNode, varNode.line().length)); 52 | // unfold if this range is hidden 53 | if (editor.nodeIsHiddenInFold(varNode)) { 54 | editor.expandToRevealNode(varNode); 55 | } 56 | }); 57 | editor.setSelectedRanges(lstRanges); 58 | //Make sure that at least the first of any selections is visible 59 | editor.scrollRangeToVisible(lstRanges[0]); 60 | } 61 | } else { 62 | 63 | // make any selection specified by line number etc 64 | if (strLine = getValue('line')) { 65 | lngLine = parseInt(strLine, 10); 66 | if (!(isNaN(lngLine))) { 67 | oNode = tree.lineNumberToNode(lngLine); 68 | if (editor.nodeIsHiddenInFold(oNode)) { 69 | editor.expandToRevealNode(oNode); 70 | editor.scrollToLine(lngLine); 71 | } 72 | 73 | if (strStartOffset = getValue('startoffset')) { 74 | varStartOffset = parseInt(strStartOffset, 10); 75 | if (!isNaN(varStartOffset)) { 76 | lngStartOffset = varStartOffset; 77 | } 78 | } 79 | 80 | if (strEndOffset = getValue('endoffset')) { 81 | varEndOffset = parseInt(strEndOffset, 10); 82 | if (!isNaN(varEndOffset)) { 83 | lngEndOffset = varEndOffset; 84 | } 85 | } 86 | 87 | rngSeln = tree.createRangeFromNodes( 88 | oNode, lngStartOffset, oNode, lngEndOffset); 89 | editor.setSelectedRange(rngSeln); 90 | } 91 | } 92 | } 93 | } 94 | " on run open location "tp3doc:///Users/robintrew/Desktop/Here%20we%20have%20a%20doc.taskpaper" end run on open location strURL set recParse to pathAndSwitches(strURL) if recParse is not missing value then set strPath to urldecode(filepath of recParse) tell application "TaskPaper" set oDoc to (open strPath) tell oDoc set varResult to (evaluate script pjsSelect with options (recParse & {keys:plstKeys})) activate end tell end tell end if end open location on pathAndSwitches(strURL) -- we can't simply split on '?' as there may be '?' in the text -- extracting the file in .js would require an active document, -- so we do it here to save the time and distraction caused by creating one set strSwitches to "" set {dlm, my text item delimiters} to {my text item delimiters, "tp3doc://"} set lstParts to text items of strURL if length of lstParts < 2 then set varParse to missing value else set strTarget to item 2 of lstParts set lngFull to length of strTarget set lngClosest to lngFull repeat with varKey in plstKeys set my text item delimiters to ("?" & varKey & "=") set lstParts to text items of strTarget if length of lstParts > 1 then set lngPosn to length of item 1 of lstParts if lngPosn < lngClosest then set lngClosest to lngPosn end if end repeat set strPath to text 1 thru lngClosest of strTarget if lngClosest < lngFull then set strSwitches to text (lngClosest + 1) thru -1 of strTarget end if set varParse to {filepath:strPath, switches:strSwitches} end if set my text item delimiters to dlm return varParse end pathAndSwitches -- based on http://harvey.nu/applescript_url_decode_routine.html on urldecode(theText) set sDst to "" set sHex to "0123456789ABCDEF" set i to 1 repeat while i ≤ length of theText set c to character i of theText if c = "+" then set sDst to sDst & " " else if c = "%" then if i > ((length of theText) - 2) then tell application "System Events" activate display dialog ("Invalid URL Encoded string - missing hex char") buttons {"OK"} default button "OK" with title pTitle & " ver. " & pVer end tell return "" end if set iCVal1 to (offset of (character (i + 1) of theText) in sHex) - 1 set iCVal2 to (offset of (character (i + 2) of theText) in sHex) - 1 if iCVal1 = -1 or iCVal2 = -1 then tell application "System Events" activate display dialog ("Invalid URL Encoded string - not 2 hex chars after % sign") buttons {"OK"} default button "OK" with title pTitle & " ver. " & pVer end tell return "" end if set sDst to sDst & (ASCII character (iCVal1 * 16 + iCVal2)) set i to i + 2 else set sDst to sDst & c end if set i to i + 1 end repeat return sDst end urldecode -- "tp3doc://encoded-file-path[?nodepath=//@due][?line=N][?startoffset=0][?endoffset=-1]" -------------------------------------------------------------------------------- /perspectives.ftplugin/README.md: -------------------------------------------------------------------------------- 1 | #### NAME 2 | **txtquery** -- grouped and sorted queries of sets of `@key(value)` tagged [FoldingText/TaskPaper](http://www.foldingtext.com) files 3 | 4 | #### TWO VERSIONS 5 | 1. FoldingText .ftplugin + an Applescript which uses it. 6 | 2. A shell script 7 | 8 | The **plugin** and its **Applescript** are useful for experimenting with TXTQUERY custom perspectives/reports, but only generate perspectives for the current document that is open in FoldingText. 9 | 10 | The **Shell script** uses the [FoldingText CLI tool](https://www.npmjs.org/package/foldingtext) and the [TXTQuery.js](https://github.com/RobTrew/txtquery-tools/tree/master/perspectives.ftplugin) file, and generates grouped and sorted reports across several different text files. 11 | 12 | ###### Shared ViewMenu.json 13 | 14 | Both (plugin/applescript and shell script) can use the same [ViewMenu.json](https://github.com/RobTrew/txtquery-tools/blob/master/perspectives.ftplugin/ViewMenu.json) file (created in the script folder when either is first run). You can inspect this file in a text editor, and tweak existing report templates (FLOWR queries), or even add new ones). 15 | 16 | (Probably simplest, of course, to make a duplicate of an existing FLOWR, give it a new name, and adjust and test it incrementally). 17 | 18 | #### SHELL SCRIPT SYNOPSIS 19 | txtquery.sh [-m] [-r report name|index] [-q query name|index] [options] 20 | #### SHELL SCRIPT DESCRIPTION 21 | **txtquery** generates Markdown-formatted custom reports by applying FLWOR-style queries to sets of `@key(value)` tagged text files. 22 | 23 | The [FLWOR](http://en.wikipedia.org/wiki/FLWOR) queries which it uses are stored in .json menu files. These use [FoldingText](http://www.foldingtext.com) nodepath syntax (FoldingText > Help > NodePaths Guide) to filter text nodes by their tags, values, and text content, and can also include clauses like: 24 | 25 | - Let 26 | - Grouped By ([as in Xquery 3](http://www.w3.org/TR/xquery-30-use-cases/#groupby)) 27 | - Order by 28 | - Return (optionally nested) 29 | 30 | 31 | (Note that the tag and text filtering is all done by FoldingText nodepaths, rather than by WHERE clauses) 32 | 33 | Each query FLWOR may also contain a description of the set(s) of text files to which it will be applied. 34 | If a query lacks a 'Sources' clause, then it can fall back to: 35 | 36 | 1. Any defaults sources clause in the menu file, or (in the absence of a menu-level sources clause) 37 | 2. to any defaults sources defined at the head of this script. 38 | 39 | All of these defaults for source text files can, however, be overriden by command line source switches (see below) 40 | including `-mtime` (last modified) and other Bash `find` command filter switches. 41 | 42 | For installation, and more detail on sources etc, see below. 43 | 44 | #### SHELL SCRIPT OPTIONS 45 | 46 | -h help - display this help string 47 | -c collections (paths or globs) - sets of text files to query 48 | -d documents (paths or globs) - documents to query 49 | -f file (path to an alternative ViewMenu.json) 50 | -i include a in the report 51 | -k Marked - view the report in [Marked](http://markedapp2.com) 52 | -l last modified (N[smhdw]) - only query files last modified within period 53 | -m menu - display the menu of report types in ViewMenu.json (or of -f above) 54 | -o output (path) - target file path for report (default is STDOUT) 55 | -r report (name|index) - generate a report of the type named or indicated 56 | -s switches (string) - additional source text switches for Bash Find 57 | -t types of file (comma delimited extension list like 'txt,md,ft') 58 | -v print version numbers of script(s) and FoldingText CLI 59 | -q query (name|index) output the FLWOR definition of the indicated type 60 | 61 | #### INSTALLATION AND DEPENDENCIES 62 | 63 | ##### FoldingText plugin and Applescript 64 | 65 | Requires: [Perspectives.ftplugin folder](https://github.com/RobTrew/txtquery-tools) and its contents, [ftdoc:// url scheme]((https://github.com/RobTrew/txtquery-tools) ) 66 | 67 | 1. Copy the Perspectives.ftplugin folder and its contents to the FT ‘Application Folder’ (FoldingText > File > Application Folder) 68 | 2. Close and restart FoldingText 69 | 3. Check in FoldingText > Plugin manager that the Perspectives plugin installed without errors 70 | 4. Open a @key(value) tagged document in FoldingText 71 | 4. Run the Perspectives Applescript, and choose one of the Group by Tags perspectives from the menu 72 | 73 | ##### txtquery.sh shell script for FT CLI 74 | 75 | Requires: [TXTQuery.js](https://github.com/RobTrew/txtquery-tools/tree/master/perspectives.ftplugin), [ftdoc:// url scheme](https://github.com/RobTrew/txtquery-tools), [FoldingText CLI plugin](https://www.npmjs.org/package/foldingtext) 76 | 77 | 1. Copy TXTQuery.js to the same folder as this script 78 | 2. Follow instructions at https://www.npmjs.org/package/foldingtext to install the FT CLI 79 | 3. At the top of this script edit the value of PathToFT to match the path of the FT executable on your system. 80 | 4. In terminal.app, cd to the folder containing this script, and make it executable (chmod +x ./txtquery.sh) 81 | 82 | #### HOW IT WORKS 83 | 84 | The set of specified files is concatenated together into a temporary snowball, which is read by the 85 | [FoldingText CLI tool](https://www.npmjs.org/package/foldingtext) into a FoldingText parse tree. 86 | 87 | This allows for FoldingText nodepath filtering, to which a FoldingText .js script adds nested grouping and sorting by tags and their values. Tracking the size and position of each component file in the snowball also makes it possible for lines in the generated reports to contain hyperlinks (ftdoc:// tp3doc://) back to particular source documents and the corresponding lines within them. 88 | 89 | FLWOR 'Return' clauses allow complete flexibility in the formatting (Markdown or other), of headings subheadings and data linesin the generated reports. A command line switch provides the option of viewing the output reports in Brett Terpstra's [Marked](http://markedapp2.com) as soon as they are generated. 90 | 91 | #### AVOID QUERYING THE REPORT FILES :-) 92 | 93 | Reports, like their sources, are plain text files, and may conceivably contain tags. To avoid circularity and ballooning duplication, it's sensible to make sure that your report output paths are not included in your report source paths :-) 94 | 95 | #### DEFINING SOURCES 96 | ##### Collections 97 | Any paths listed (comma-delimited) under the *collections* key of a query source (or specified on the command line with a `-c` switch) are interpreted as references to SETS of documents. 98 | 99 | 1. A globbed path (ending in *, for example) will be expanded to a list of files 100 | 2. A folder path will be expanded to a list of the folder contents (bash find -maxdepth N can be added with the -s switch). 101 | 3. If the path is to a single text file, that file will be assumed to contain a simple list of paths, each of which will be queried. 102 | 103 | ##### Documents 104 | Any paths listed (comma-delimited) under the *documents* key of a query source (or specified on the command line with a `-d` switch) are interpreted as *direct* references to text files to be queried. 105 | Globs and folders will still be expanded, any path is to single text file, it will be treated as a source to be queried, rather than being read as a list of paths to query. 106 | 107 | 108 | #### Syntax of the FLWOR queries 109 | 110 | - Essentially an initial sketch of a small subset of XQUERY 3 FLOWR, in JSON format, but not yet documented. 111 | - It may be quicker to describe a query or report template to me (Rob Trew @complexpoint on Twitter), and get me to quickly sketch it, than to struggle inductively with the (still rough) syntax yourself :-) 112 | -------------------------------------------------------------------------------- /perspectives.ftplugin/ViewMenu.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": { 3 | "collections": "~/Library/Application Support/Notational Velocity/project*", 4 | "docs": "~/Library/Application Support/Notational Velocity/inbox.txt", 5 | "filetypes": "txt,md,ft", 6 | "switches": "-maxdepth 1", 7 | "mtime": "14" 8 | }, 9 | "menu": { 10 | "Done by date": { 11 | "sources": { 12 | "mtime": [ 13 | "7" 14 | ] 15 | }, 16 | "title": "## Done (by date)", 17 | "for": "$items in //@done", 18 | "let": [ 19 | "$date = fn:daypart($items@done)", 20 | "$project = $items@project" 21 | ], 22 | "groupby": "$date", 23 | "orderby": "$date", 24 | "return": [ 25 | "### {$date}", 26 | { 27 | "for": "$i in $items", 28 | "orderby": "$project", 29 | "return": "- {$i@text} {$i@tags}" 30 | }, 31 | "" 32 | ] 33 | }, 34 | "Grouped by tag III": { 35 | "title": "## Tags and their values", 36 | "for": "$tag in fn:tagSet()", 37 | "let": "$items = //@{$tag}", 38 | "orderby": "$tag", 39 | "return": [ 40 | "### fn:sentence_case({$tag})", 41 | { 42 | "for": "$i in $items", 43 | "let": "$val = $i@{$tag}", 44 | "groupby": "$val", 45 | "orderby": "$val", 46 | "return": [ 47 | "{$val}", 48 | { 49 | "for": "$j in $i", 50 | "return": "[{$j@file} line {$j@linenum}]({$j@link})\t- {$j}" 51 | }, 52 | "" 53 | ] 54 | }, 55 | "" 56 | ] 57 | }, 58 | "Grouped by tag II": { 59 | "for": "$tag in fn:tagSet()", 60 | "let": "$items = //@{$tag}", 61 | "orderby": "$tag", 62 | "return": [ 63 | "#### fn:sentence_case({$tag}) (fn:count($items))", 64 | { 65 | "for": "$i in $items", 66 | "let": "$val = $i@{$tag}", 67 | "orderby": "$val", 68 | "return": "{$val} - {$i}" 69 | }, 70 | "" 71 | ] 72 | }, 73 | "Grouped by tag": { 74 | "sources": { 75 | "collections": "~/Library/Application Support/Notational Velocity/project*", 76 | "docs": "~/Library/Application Support/Notational Velocity/inbox.txt", 77 | "filetypes": [ 78 | "txt", 79 | "md", 80 | "ft" 81 | ], 82 | "mtime": "42d", 83 | "switches": "-maxdepth 1" 84 | }, 85 | "for": "$tag in fn:tagSet()", 86 | "let": "$items = //@{$tag}", 87 | "orderby": "$tag", 88 | "return": [ 89 | "#### fn:sentence_case({$tag}) (fn:count($items))", 90 | { 91 | "for": "$item in //@{$tag}", 92 | "return": "- {$item} ({$item@$tag})" 93 | }, 94 | "" 95 | ] 96 | }, 97 | "Simple sort": { 98 | "title": "### Testing empty sort order", 99 | "for": "$line in //@type!=empty", 100 | "let": "$level = $line@priority", 101 | "orderby": "$level", 102 | "return": "- fn:sentence_case({$line@text}) {$level}" 103 | }, 104 | "Grouped by any old tags": { 105 | "for": "$tag in fn:tagSet()", 106 | "let": "$lines = //@{$tag}", 107 | "return": [ 108 | "### fn:sentence_case({$tag}) (fn:count($lines) items)", 109 | { 110 | "for": "$i in $lines", 111 | "let": "$value = $i@{$tag}", 112 | "groupby": "$value", 113 | "orderby": "$value", 114 | "return": [ 115 | "\t{$value}", 116 | { 117 | "for": "$j in $i", 118 | "return": "\t\t- {$j}" 119 | }, 120 | "" 121 | ] 122 | }, 123 | "" 124 | ] 125 | }, 126 | "Dates with priority subsections": { 127 | "title": "## Dates by priority", 128 | "for": "$line in //@type!=empty", 129 | "let": "$due = fn:daypart($line@due)", 130 | "groupby": "$due", 131 | "orderby": "$due", 132 | "return": [ 133 | "### Due {$due}", 134 | "", 135 | { 136 | "for": "$item in $line", 137 | "let": "$level = $item@priority", 138 | "groupby": "$level", 139 | "orderby": "$level asc", 140 | "return": [ 141 | "#### Priority ({$level})", 142 | { 143 | "for": "$i in $item", 144 | "let": "$time = fn:timepart($i@due)", 145 | "orderby": "$i@due", 146 | "return": "- {$time} {$i}" 147 | }, 148 | "" 149 | ] 150 | }, 151 | "" 152 | ] 153 | }, 154 | "Grouped by due date and priority": { 155 | "for": "$line in //@priority and @due", 156 | "let": "$level = $line@priority, $due = fn:daypart($line@due)", 157 | "groupby": "$due, $level", 158 | "orderby": "$due, $level", 159 | "return": [ 160 | "#### Due {$due} (Priority {$level})", 161 | { 162 | "for": "$i in $line", 163 | "return": "- fn:timepart({$i@due}) {$i}" 164 | }, 165 | "" 166 | ] 167 | }, 168 | "Due this week (with NVALT links)": { 169 | "title": "## This WEEK", 170 | "for": "$item in //@due <= {today + 7d}", 171 | "let": "$day = fn:daypart($item@due)", 172 | "groupby": "$day", 173 | "orderby": "$day", 174 | "return": [ 175 | "### {$day}", 176 | { 177 | "for": "$i in $item", 178 | "orderby": "$i@due", 179 | "return": "- fn:timepart({$i@due}) [{$i}](nvalt://fn:encode_for_uri({$i}))" 180 | }, 181 | "" 182 | ] 183 | }, 184 | "Overdue with ftdoc links": { 185 | "title": "## This WEEK", 186 | "for": "$item in //@due < {now}", 187 | "let": "$day = fn:daypart($item@due)", 188 | "groupby": "$day", 189 | "orderby": "$day", 190 | "return": [ 191 | "### fn:format_date({$day}, '[FNn] [D] [MNn] [Y]'')", 192 | { 193 | "for": "$i in $item", 194 | "orderby": "$i@due", 195 | "return": "- fn:timepart({$i@due}) [{$i}]({$i@url})" 196 | }, 197 | "" 198 | ] 199 | }, 200 | "Due this week": { 201 | "title": "## This WEEK no links", 202 | "for": "$item in //@due <= {today + 7d}", 203 | "let": "$day = fn:daypart($item@due)", 204 | "groupby": "$day", 205 | "orderby": "$day", 206 | "return": [ 207 | "### {$day}", 208 | { 209 | "for": "$i in $item", 210 | "orderby": "$i@due", 211 | "return": "- fn:timepart({$i@due}) {$i}" 212 | }, 213 | "" 214 | ] 215 | }, 216 | "Allocated time": { 217 | "title": "## Time Allocated", 218 | "for": "$head in /* intersect //@mins/ancestor::*", 219 | "let": "$lines = /{$head}//@mins", 220 | "return": [ 221 | "### fn:upper_case({$head}): total fn:sum($lines@mins)m", 222 | { 223 | "for": "$i in $lines", 224 | "return": "- {$i} ({$i@mins}m)" 225 | }, 226 | "" 227 | ] 228 | }, 229 | "Tagged lines - grouped by tags": { 230 | "for": "$tag in fn:tagSet()", 231 | "return": [ 232 | "## fn:sentence_case({$tag})", 233 | { 234 | "for": "$node in //@{$tag}", 235 | "orderby": "$node@{$tag} asc", 236 | "return": "{$node@$tag} - {$node}" 237 | }, 238 | "" 239 | ] 240 | }, 241 | "Grouped by any priority levels found": { 242 | "title": "### Priorities", 243 | "for": "$line in //@priority", 244 | "let": "$level = $line@priority", 245 | "groupby": "$level", 246 | "orderby": "$level", 247 | "return": [ 248 | "### Priority {$level} [fn:sum($line@priority)]", 249 | { 250 | "for": "$node in $line", 251 | "return": "- {$node}" 252 | }, 253 | "" 254 | ] 255 | }, 256 | "All Nodes simplest sort": { 257 | "for": "$line in //@type!=empty", 258 | "let": "$text=$line", 259 | "orderby": "$text desc", 260 | "return": "- {$text}" 261 | }, 262 | "Sorted list with NOTES": { 263 | "for": "$line in //@type=unordered", 264 | "orderby": "$line desc", 265 | "return": [ 266 | "- {$line}{$line@note}" 267 | ] 268 | }, 269 | "Numbers in sorted in sequence": { 270 | "for": "$num in (10,9,234, 1,2, 5 ,6,7,3,4,8,9)", 271 | "orderby": "$num asc", 272 | "return": "- {$num}" 273 | }, 274 | "Tags in sequence": { 275 | "for": "$tag in fn:tagSet()", 276 | "let": "$all = //@{$tag}", 277 | "return": [ 278 | "#### fn:sentence_case({$tag}) (fn:count($all))", 279 | { 280 | "for": "$i in $all", 281 | "return": "- {$i}" 282 | }, 283 | "" 284 | ] 285 | }, 286 | "Tags in document": { 287 | "for": "$tag in fn:tagSet()", 288 | "orderby": "$tag", 289 | "return": "- fn:sentence_case({$tag})" 290 | }, 291 | "Grouped by due date": { 292 | "for": "$line in //@due", 293 | "let": "$due = $line@due", 294 | "groupby": "$due", 295 | "orderby": "$due", 296 | "return": [ 297 | "## Due {$due}", 298 | { 299 | "for": "$l in $line", 300 | "return": "- {$l@parent} → {$l}" 301 | }, 302 | "" 303 | ] 304 | } 305 | }, 306 | "queue": [] 307 | } 308 | -------------------------------------------------------------------------------- /utilities/MakeTestData.applescript: -------------------------------------------------------------------------------- 1 | property pTitle : "Make random tagged and nested tasks" property pDescription : "for testing new plain text queries with txtQuery.sh" property pVer : "0.03" property pAuthor : "Rob Trew" property pblnDebug : false -- Generates a new foldingtext document populated with a random set of tagged and nested tasks -- define subset of tags to use by setting true|false in the following record -- NB if you create any additional tags, you will need to define value lists or functions -- for them in dctTagVals below either as: -- 1. An array of possible values e.g. ['alpha', 'beta', 'gamma'] from which a random choice will be made -- 2. A lambda (anonymous function) which returns a value e.g. function() {return (randomInt(1,19)*5).toString();} -- 3. The string 'day' for which a random date will be generated -- 4. The string 'time' for which a random date and time will be generated -- SET THE RANGE OF DATES WITHIN WHICH RANDOM DATE TAGS WILL BE GENERATED property pNow : (current date) property pFrom : pNow - 30 * days property pTo : pNow + 60 * days property precRange : {earliest:pFrom, latest:pTo} property precTags : {activetags:{priority:true, start:true, due:true, mins:true, alert:true, next:true, done:true}} property pUnixEpoch : missing value property pstrJS : " 2 | function(editor, options) { 3 | var tree=editor.tree(), oParent=tree.root, 4 | lstSyntax = ['process','affected','instrument','circumstance','time'], 5 | dctTags = options.activetags, 6 | lstTagSet = Object.keys(dctTags), 7 | lstActiveTags=lstTagSet.filter( 8 | function (oTag) {return dctTags[oTag];}), 9 | lngActiveTags=lstActiveTags.length, 10 | iLastTag=lngActiveTags-1, 11 | dctLex={ 12 | 'process':['build','make', 'think', 'work', 'drink','give','call', 'try', 'winnow', 'aggregate', 'link', 'derive', 'summarise'], 13 | 'affected':['school','system','program','question','water','book','earth','umbrella','time','thing','world','life','footsoldier','company','problem', 'group', 'number','weaver','toothbrush','derivation','method', 'mountain', 'termite mound'], 14 | 'instrument':['school','system','program','question','water','book','earth','umbrella','time','thing','world','life','footsoldier','company','problem', 'group', 'number','weaver','sandwich', 'theorem', 'hypothesis', 'assumption', 'contradiction', 'function', 'derivation'], 15 | 'circumstance':['at the office', 'in the library', 'at home', 'in the forest', 'on the mountain', 'while commuting', 'at lunch', 'after breakfast', 'before supper', 'tomorrow morning', 'in a boat', 'on the sea', 'by a bridge', 'along the river', 'under the maples', 'with lambda', 'with lemma'], 16 | 'time':['early','on Fridays', 'next week', 'in two days', 'at the end of the month', 'before 2015', 'after 2017','after the harvest','during the spring sowing','after Michaelmas','in Trinity', 'tomorrow', 'this evening', 'at 7pm on Monday', 'by August', 'first thing', 'before retiring'] 17 | }, 18 | 19 | dteStart=new Date(), 20 | dteHorizon=new Date(), 21 | 22 | dctTagVals={ 23 | 'priority':[1, 2, 3], 24 | 'start':'day', 25 | 'alert':'time', 26 | 'due':'time', 27 | 'done':'time', 28 | 'mins':function() {return (randomInt(1,19)*5).toString();} 29 | }, 30 | 31 | lngCount, lngPhrase=60, strPhrase='', lstParts=[], lstPhrases=[],lngSyntax=lstSyntax.length,blnTag, 32 | lstWords, lngWords, strKey, iWord, strWord; 33 | 34 | function randomInt(min, max) { 35 | return Math.floor(Math.random() * (max - min + 1)) + min; 36 | } 37 | 38 | function simplePhrase(lngLevel, dteLocalStart, dteLocalDue) { 39 | var strPrefix, strPhrase, lngPhrase, 40 | dteFrom=dteLocalStart, dteTo=dteLocalDue, varTagVal, 41 | strType, varValue, strValue, lngRange, 42 | lstParts = [], lstSeen=[], iTag, dteMoment, lngTags; 43 | 44 | 45 | // CREATE A RANDOM PHRASE 46 | // using only a number of words that matches the nesting level, 47 | lngPhrase=Math.min(lngSyntax, lngLevel); 48 | for (var j=0;j Help > SDK > Run Editor set varResult to (debug script pstrJS with options recOptions) end if end tell return varResult end run on AsDate2JS(dteAs) if pUnixEpoch is missing value then set pUnixEpoch to UnixEpoch() return (dteAs - pUnixEpoch) * 1000 end AsDate2JS on UnixEpoch() tell (current date) set {its year, its day, its time} to {1970, 1, 0} set its month to 1 -- set after day for fear of Feb :-) return (it + (my (time to GMT))) end tell end UnixEpoch -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/QueryAFolderOfOPMLFiles.applescript: -------------------------------------------------------------------------------- 1 | // Simple demonstration of using XQuery from OS X Yosemite Javascript for Applications 2 | // To get a menu of custom perspectives across several files (using OS X (NSXML) XQUERY from Javascript): 3 | 4 | // 1. Copy the accompanying OPML files (tagged with attributes from FoldingText / TaskPaper) into a folder 5 | // 2. Run this script, choosing (from the dialog which pops up) the folder containing the OPML files 6 | 7 | 8 | // Copyright Robin Trew Feb 2015 9 | 10 | 11 | function run() { 12 | 'use strict'; 13 | /* jshint multistr: true */ 14 | 15 | 16 | // OPEN OUTPUT IN [Marked 2](http://marked2app.com) ? 17 | var blnOpenPerspectiveInMarked = true; 18 | 19 | // OPEN SOURCE FILE FOR SELECTED ACTIONS ? 20 | var blnOpenSelected = false; 21 | 22 | // Temporary output file for "Marked 2.app" to preview 23 | var strTestFile = "~/Desktop/TestXQuery01.txt"; // WILL BE OVERWRITTEN - CHECK THAT THIS DOESN'T CLASH 24 | 25 | // EXTENSION OF FILES TO QUERY 26 | var strExtn = "opml"; 27 | 28 | var strExtnUpper = strExtn.toUpperCase(); 29 | 30 | 31 | // MENU OF CUSTOM PERSPECTIVES - DEFINED (OVER A SET OF OPML FILES) IN NSXMLNODE XQUERY 1.0 32 | var dctXQuery = { 33 | 'Grouped by Priority level - sorted by descending @due date': ' \ 34 | let $v := distinct-values(//outline/@priority) \ 35 | for $i in $v \ 36 | order by $i \ 37 | return ( \ 38 | concat("\n### Priority ", $i, ""), \ 39 | for $o in //outline[@priority=$i] \ 40 | let $d := $o/@due \ 41 | order by $d empty greatest \ 42 | return ( \ 43 | concat( \ 44 | "- ", $o/@text, \ 45 | if ($d) then concat(" @due(",$d,")") else () \ 46 | ) \ 47 | ) \ 48 | )', 49 | 50 | 'Starting in March': ' \ 51 | let $v := distinct-values( \ 52 | //outline[@start > "2015-02-30" and @start < "2015-04-01"]/substring(@start,1,10) \ 53 | ) \ 54 | for $d in $v \ 55 | order by $d \ 56 | return ( \ 57 | concat("\n#### ", substring($d, 1, 7), " ", substring($d, 9), ""), \ 58 | for $o in //outline[@start=$d]\ 59 | let $r := $o/ancestor::outline[@file], \ 60 | $f := $r/@file, \ 61 | $fp := $r/@path, \ 62 | $t := $o/@text \ 63 | order by $t \ 64 | return ( \ 65 | concat( \ 66 | "- ", $t, \ 67 | " [", $f, "](file://", $fp, $f, ")" \ 68 | ) \ 69 | ) \ 70 | )', 71 | 72 | 'Immediate priorities': ' \ 73 | let $v := distinct-values(//outline[@priority=1 and @due]/substring(@due,1,10)) \ 74 | for $d in $v \ 75 | let $dte := xs:date($d), \ 76 | $e := xs:date("1901-01-06") \ 77 | order by $d \ 78 | return (\ 79 | concat( \ 80 | "\n#### ", \ 81 | ("Sun", "Mon", "Tue", "Wed","Thu", "Fri", "Sat")[ \ 82 | (days-from-duration($dte - $e) mod 7) + 1 \ 83 | ], \ 84 | " ", substring($d, 1, 7), \ 85 | " ", substring($d, 9, 2), " " \ 86 | ), \ 87 | for $i in //outline[@priority=1 and starts-with(@due, $d)] \ 88 | let $it := $i/@due, \ 89 | $t := substring($it, 12), \ 90 | $tme := if ($t) then xs:time(concat($t, ":00")) else xs:time("00:00:00")\ 91 | order by $t empty least \ 92 | return concat( \ 93 | if ($t) then concat("",$t," ") else (), \ 94 | $i/@text \ 95 | ) \ 96 | )', 97 | 98 | 99 | 'Due and urgent': ' \ 100 | for $p in //outline[@priority=1 and @due] \ 101 | let $d := $p/@due, \ 102 | $r := $p/ancestor::outline[@file], \ 103 | $f := $r/@file, \ 104 | $fp := $r/@path \ 105 | order by $d \ 106 | return concat( \ 107 | "- ", $p/@text, " ", \ 108 | if ($d) then \ 109 | concat("@due(", $d, ")") \ 110 | else (), \ 111 | " [", $f, "](file://", $fp, $f, ")" \ 112 | )', 113 | 114 | 115 | 'Projects with alert dates': ' \ 116 | for $p in //outline[@type="heading" and @alert] \ 117 | let $a := $p/@alert, \ 118 | $r := $p/ancestor::outline[@file], \ 119 | $f := $r/@file, \ 120 | $fp := $r/@path \ 121 | order by $a \ 122 | return concat( \ 123 | "- ", $p/@text, " ", \ 124 | "@alert(", $a, ")", \ 125 | " [", $f, "](file://", $fp, $f, ")" \ 126 | )' 127 | }; 128 | 129 | // strPath --> [strFileName] 130 | function filesInFolder(strPath) { 131 | var fm = $.NSFileManager.defaultManager, 132 | lstFiles = ObjC.unwrap(fm.contentsOfDirectoryAtPathError(strPath, null)), 133 | lst = []; 134 | 135 | for (var i = 0, lng = lstFiles.length; i < lng; i++) { 136 | lst.push(ObjC.unwrap(lstFiles[i])); 137 | } 138 | return lst; 139 | } 140 | 141 | // strFolderPath --> strExtension --> [strFilePath] 142 | function filesInFolderWithExtn(strPath, strExtn) { 143 | var lst = filesInFolder(strPath), 144 | lstMatch = [], 145 | strBase = (strPath.charAt(strPath.length - 1) === '/') ? 146 | strPath : strPath + '/', 147 | strFileName, lstParts, lngParts; 148 | 149 | for (var i = 0, lng = lst.length; i < lng; i++) { 150 | strFileName = lst[i]; 151 | lstParts = strFileName.split('.'); 152 | lngParts = lstParts.length; 153 | if ((lngParts > 1) && (lstParts[lngParts - 1] === strExtn)) 154 | lstMatch.push(strBase + strFileName); 155 | } 156 | return lstMatch; 157 | } 158 | 159 | // XInclude LIST OF FILES TO INCLUDE IN THE COMPOSITE NSXMLDOCUMENT WHICH WE WILL QUERY 160 | // [filePath] --> strWrapElementName --> strInnerElementName --> strIncludeXML 161 | function xIncludeXML(lstFilePaths, strWrapElement, strInnerElement) { 162 | var lst = [ 163 | '', 164 | '<' + strWrapElement + ' xmlns:xi="http://www.w3.org/2003/XInclude">' 165 | ], 166 | strXML; 167 | 168 | function fileEntry(strPath, strElement) { 169 | var lstParts = strPath.split('/'), 170 | strFile = encodeURI(lstParts.pop()); 171 | 172 | return [ 173 | '\t<' + strElement + ' text="" path="' + encodeURI(lstParts.join( 174 | '/')) + 175 | '/" file="' + strFile + '">', 176 | '\t\t', 177 | '\t' 178 | ].join('\n'); 179 | } 180 | 181 | i = lstFilePaths.length; 182 | while (i--) { 183 | lst.push(fileEntry(lstFilePaths[i], strInnerElement)); 184 | } 185 | 186 | strXML = lst.join('\n') + '\n'; 187 | //console.log(strXML); 188 | return strXML; 189 | } 190 | 191 | 192 | /////// MAIN //////////////////////////////////////////////////////////////////////////////////// 193 | 194 | var rgxFileURL = /\((file.*)\)$/, 195 | varChoice = true, 196 | oMatch, docXML, 197 | lstMenu, lst, lstFiles, 198 | strURL, strMenuKey, strTitle, strXML, strTXT, strPATH, 199 | lng, i, 200 | blnWritten; 201 | 202 | // PREPARE FOR USE OF DIALOGS 203 | var app = Application.currentApplication(); 204 | app.includeStandardAdditions = true; 205 | app.activate(); 206 | 207 | // CHOOSE THE FOLDER CONTAINING THE FOUR DEMO .OPML FILES (TAGGED WITH VARIOUS ATTRIBUTE VALUES) 208 | var strFolderPath = app.chooseFolder({ 209 | withPrompt: "CHOOSE FOLDER CONTAINING SAMPLE " + strExtnUpper + " FILES" 210 | }).toString(); 211 | 212 | // SHOW A MENU OF PERSPECTIVES 213 | 214 | while (varChoice) { 215 | lstMenu = Object.keys(dctXQuery); 216 | varChoice = lstMenu.length ? app.chooseFromList(lstMenu, { 217 | withTitle: "xQuery across several files", 218 | withPrompt: "Select Perspective:", 219 | defaultItems: [lstMenu[0]] 220 | }) : false; 221 | 222 | 223 | // USE THE MENU NAME AS THE REPORT TITLE 224 | strMenuKey = varChoice[0]; 225 | 226 | 227 | // REFERENCE EACH FILE IN AN XINCLUDE DOCUMENT 228 | lstFiles = filesInFolderWithExtn(strFolderPath, strExtn); 229 | if (lstFiles.length) { 230 | strXML = xIncludeXML( 231 | filesInFolderWithExtn(strFolderPath, strExtn), 'body', 'outline' 232 | ); 233 | } else { 234 | app.displayDialog("No " + strExtnUpper + " files found in " + strFolderPath); 235 | return; 236 | } 237 | 238 | // READ THE XINCLUSIONS INTO A COMPOSITE XML FILE 239 | docXML = $.NSXMLDocument.alloc.initWithXMLStringOptionsError( 240 | strXML, $.NSXMLDocumentXInclude, null 241 | ); 242 | 243 | return strXML 244 | 245 | // THEN APPLY THE QUERY ... 246 | lst = ObjC.unwrap(docXML.objectsForXQueryError( 247 | dctXQuery[strMenuKey], null 248 | )); 249 | 250 | // debug return lst for quick harvest check 251 | // return lst; 252 | 253 | 254 | // AND HARVEST ANY RESULT 255 | if (!lst) return; 256 | i = lst.length; 257 | if (i) { 258 | while (i--) lst[i] = ObjC.unwrap(lst[i]); 259 | 260 | if (blnOpenPerspectiveInMarked) { 261 | 262 | // WRITE OUT PERSPECTIVE AS TEXT FILE 263 | strTXT = $.NSString.alloc.initWithUTF8String( 264 | '## ' + strMenuKey + '\n\n' + lst.join('\n') 265 | ); 266 | 267 | strPATH = $.NSString.alloc.initWithUTF8String( 268 | strTestFile 269 | ); 270 | strPATH = ObjC.unwrap(strPATH.stringByExpandingTildeInPath); 271 | 272 | 273 | blnWritten = strTXT.writeToFileAtomically(strPATH, true); 274 | 275 | // AND OPEN IT IN [MARKED 2](http://marked2app.com) 276 | if (blnWritten) { 277 | app.doShellScript( 278 | 'open -a "Marked 2" ' + strPATH 279 | ); 280 | } 281 | } else { 282 | 283 | varChoice = app.chooseFromList(lst, { 284 | withTitle: strMenuKey, 285 | withPrompt: "Select from actions matching [" + strMenuKey + "]:", 286 | defaultItems: lst[0], 287 | multipleSelectionsAllowed: true 288 | }); 289 | 290 | // OPEN THE OPML FILE(S) FOR THE CHOSEN ACTION(S) IN THE DEFAULT OPML EDITOR ? 291 | if (blnOpenSelected) { 292 | if (varChoice) { 293 | i = varChoice.length; 294 | while (i--) { 295 | oMatch = rgxFileURL.exec(varChoice[i]); 296 | if (oMatch) { 297 | app.doShellScript('open ' + oMatch[1]); 298 | } 299 | } 300 | break; 301 | } 302 | } 303 | } 304 | } else { 305 | app.displayAlert( 306 | "No matches for " + strMenuKey + " found in " + strFolderPath + 307 | " .opml files" 308 | ); 309 | } 310 | 311 | } 312 | 313 | 314 | return true; 315 | } -------------------------------------------------------------------------------- /Yosemite Javascript XQuery demo/SampleFolder/workProject.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | # work @alert(2015-02-03 03:00) @start(2015-03-07) @done(2015-02-25) @due(2015-02-26 03:00) 4 | 5 | ## call mountain @priority(1) @status(wait) @alert(2015-02-18 07:30) @start(2015-02-12) 6 | - build school hypothesis @status(wait) @mins(50) @alert(2015-02-12 03:30) 7 | - drink earth book with lambda @due(2015-02-09 12:30) @mins(75) @priority(1) @start(2015-02-27) @alert(2015-02-03 16:00) 8 | - build program number with lemma @due(2015-03-05 04:30) @mins(65) @alert(2015-02-27 05:30) @status(wait) @done(2015-02-06 20:00) @priority(2) 9 | - try group group along the river @alert(2015-02-02 14:00) @due(2015-02-02 23:00) @priority(2) @start(2015-02-25) 10 | - work mountain function @status(throw) 11 | - make group group in the forest @done(2015-03-01 16:00) @due(2015-02-27 15:00) @status(throw) @alert(2015-02-05 05:30) @mins(10) 12 | - think world footsoldier at lunch @start(2015-02-13) @pom(45) 13 | - aggregate life earth before supper @priority(1) @pom(70) @done(2015-03-08 02:00) @due(2015-02-15 21:30) @mins(30) @status(wait) 14 | - work life time @status(throw) @done(2015-02-02 13:30) @mins(60) @priority(3) @alert(2015-02-11 20:00) 15 | - make derivation number by a bridge @start(2015-02-25) @done(2015-02-20 07:00) @status(grade) @mins(70) 16 | - link footsoldier derivation while commuting 17 | - aggregate book derivation by a bridge @start(2015-03-03) @mins(30) 18 | 19 | ## make time @mins(85) 20 | - make derivation contradiction @done(2015-02-22 08:30) @pom(55) @status(grade) @start(2015-02-24) @due(2015-02-13 22:30) 21 | - build group theorem before supper @pom(40) @done(2015-02-18 20:30) @alert(2015-02-21 09:00) 22 | - link world school at the office 23 | - drink earth system along the river @mins(55) @status(wait) @priority(1) @done(2015-02-03 08:30) @alert(2015-02-11 17:00) 24 | - summarise weaver question @done(2015-02-08 18:00) @mins(80) @due(2015-02-02 01:00) 25 | - link program water along the river @alert(2015-02-10 20:00) @pom(15) 26 | - drink water problem in the forest @pom(35) @status(wait) @start(2015-02-11) @mins(75) @done(2015-02-15 04:30) @alert(2015-02-10) @priority(1) 27 | - summarise number theorem with lemma 28 | - winnow program thing @due(2015-02-10 06:00) @priority(1) @pom(70) @done(2015-03-01 23:00) @mins(10) @status(wait) @start(2015-02-28) 29 | - work problem problem by a bridge @priority(3) 30 | - try umbrella life tomorrow morning @priority(1) @done(2015-02-14 11:00) @start(2015-03-01) @alert(2015-02-08 16:30) @mins(90) 31 | - give thing school with lemma @done(2015-02-07 18:00) @start(2015-02-17) 32 | 33 | ## call number @done(2015-02-11) @start(2015-03-04) @mins(30) @due(2015-02-27 07:00) @pom(10) @alert(2015-02-03 11:00) 34 | - link school program @start(2015-02-10) @mins(55) @status(wait) @done(2015-02-18 07:30) @alert(2015-02-04 00:30) @pom(55) @priority(3) 35 | - try toothbrush weaver at the office @status(read) @mins(55) @start(2015-02-20) @priority(3) @done(2015-02-16) @due(2015-02-19 21:00) @pom(5) 36 | - think earth sandwich on the sea @mins(5) 37 | - aggregate group question at home @due(2015-02-10 07:00) @start(2015-02-22) @pom(35) @status(wait) 38 | - build system thing @due(2015-02-27 04:00) @done(2015-03-07 06:00) @priority(3) @pom(5) @mins(70) @start(2015-02-10) 39 | - give program school in the forest @priority(2) @done(2015-03-04 21:30) @status(read) @start(2015-03-07) @pom(15) @mins(95) 40 | - summarise number footsoldier along the river @mins(35) @priority(1) 41 | - think system earth tomorrow morning @priority(3) @done(2015-02-06 17:00) @pom(85) 42 | - call number contradiction @start(2015-02-19) 43 | - give weaver water in the library @priority(1) @pom(30) @done(2015-02-28 11:30) @start(2015-03-02) @mins(30) 44 | - build book theorem after breakfast @priority(3) 45 | - make water number on the sea @start(2015-02-22) @alert(2015-02-18 09:00) 46 | 47 | # link @status(throw) 48 | 49 | ## winnow book @done(2015-02-14 13:00) 50 | - drink termite mound hypothesis @pom(75) @done(2015-02-22 11:00) @status(wait) @mins(45) @due(2015-02-02 20:30) @priority(1) 51 | - aggregate system umbrella along the river 52 | - make school company at the office @mins(65) @status(wait) @alert(2015-02-13 20:30) 53 | - drink derivation company at lunch @pom(40) @alert(2015-02-28 03:00) @mins(15) @start(2015-03-05) @priority(3) 54 | - build problem water @priority(2) @done(2015-02-12 02:00) @alert(2015-02-18 18:00) @mins(60) 55 | - link program book on the sea @due(2015-03-04 03:00) @alert(2015-02-10 13:00) @done(2015-02-26 17:00) @priority(2) 56 | - derive number system with lemma @pom(55) 57 | - work method derivation at lunch @done(2015-02-03 18:30) @mins(70) @alert(2015-02-14 12:00) @pom(40) @status(read) @start(2015-03-06) @priority(1) 58 | - make program function @pom(95) @due(2015-03-01 23:30) 59 | - build toothbrush number on the sea @alert(2015-02-08 01:00) 60 | - try thing weaver in the library 61 | - aggregate world weaver with lambda @due(2015-02-14 16:00) @pom(75) @mins(45) 62 | 63 | ## link umbrella @done(2015-02-21 12:00) @priority(1) @due(2015-03-02 03:00) @alert(2015-02-19 19:30) @mins(60) 64 | - call termite mound number @due(2015-02-14 10:30) @priority(3) 65 | - give company company on the mountain @status(throw) @priority(1) @pom(75) @done(2015-02-25 17:00) 66 | - build number assumption at the office 67 | - make earth weaver by a bridge @start(2015-02-06) @done(2015-03-07 04:00) @priority(1) 68 | - build life book @start(2015-03-04) @priority(1) @done(2015-02-28 04:00) 69 | - drink umbrella world on the sea @start(2015-02-22) 70 | - winnow world derivation under the maples @status(wait) @pom(65) 71 | - build time system along the river @priority(3) 72 | - aggregate derivation question @pom(25) @priority(1) @mins(5) 73 | - winnow weaver company on the sea @done(2015-02-03 11:00) @priority(3) 74 | - build system group with lemma 75 | - summarise world hypothesis with lambda @due(2015-02-13 21:00) 76 | 77 | ## derive program @status(throw) @alert(2015-03-07 10:00) 78 | - drink thing theorem 79 | - try derivation time on the sea @alert(2015-03-06 21:00) @start(2015-02-20) @due(2015-02-15 02:00) @status(throw) 80 | - summarise school book along the river @pom(75) @alert(2015-02-21 20:00) @due(2015-03-08 01:00) 81 | - think number number after breakfast @pom(25) @mins(55) @start(2015-02-24) @due(2015-02-05 15:00) 82 | - derive school book @start(2015-02-23) @due(2015-02-07 13:00) @done(2015-02-22 18:00) @alert(2015-02-23 15:00) @status(grade) @priority(2) 83 | - give mountain number while commuting @priority(3) @pom(10) @alert(2015-03-05 10:30) @done(2015-03-04 20:00) @status(wait) @mins(65) 84 | - build life school in the library @due(2015-02-08 07:00) @status(read) @done(2015-02-28 19:00) 85 | - summarise time footsoldier by a bridge @mins(45) @start(2015-03-01) @priority(2) @due(2015-02-08 03:00) @pom(10) @done(2015-03-05 14:30) 86 | - work book assumption @alert(2015-02-26 22:00) @due(2015-03-03 01:00) @mins(55) @start(2015-02-26) @status(read) @priority(2) 87 | - try school problem before supper @due(2015-02-15 02:00) @done(2015-03-01 04:30) @mins(80) @pom(45) @start(2015-02-20) 88 | - work time contradiction at lunch @mins(75) @pom(40) @done(2015-02-20 19:30) @status(read) 89 | - make company question in a boat 90 | 91 | # call @alert(2015-02-14 09:00) @status(wait) @pom(55) @due(2015-03-02 13:00) @done(2015-03-06 21:00) @priority(3) 92 | 93 | ## make company @mins(75) @status(wait) @pom(30) 94 | - build world book @status(wait) @mins(40) @due(2015-03-08 20:00) @done(2015-02-13 01:30) @priority(2) 95 | - summarise program book at home @start(2015-03-03) @status(throw) @priority(2) @mins(35) @done(2015-02-25 07:00) @due(2015-02-18 23:00) 96 | - link toothbrush company with lemma @due(2015-02-27 16:00) @status(throw) @alert(2015-03-06 14:30) @priority(3) 97 | - try mountain school with lemma @due(2015-02-19 08:00) @alert(2015-02-27 16:00) @mins(45) @status(grade) 98 | - think program company 99 | - link mountain number while commuting @alert(2015-02-08 22:00) 100 | - summarise derivation book on the sea @status(throw) @due(2015-02-13 21:00) @alert(2015-02-27 23:00) 101 | - winnow group system after breakfast @priority(1) @status(grade) @mins(60) @done(2015-02-17 23:00) @due(2015-02-16 05:00) @alert(2015-02-07 15:30) @pom(75) 102 | - aggregate world world @status(throw) @done(2015-02-12 06:30) @pom(30) @mins(90) @alert(2015-03-08 17:30) 103 | - think life company along the river @done(2015-03-03 22:00) @priority(3) @mins(90) @status(grade) @start(2015-02-18) 104 | - give earth umbrella under the maples @priority(2) @mins(60) @due(2015-02-09 23:00) 105 | - derive termite mound system in the library 106 | 107 | ## derive book @status(grade) @mins(70) 108 | - give derivation umbrella @start(2015-02-16) @due(2015-02-15 02:00) @done(2015-03-06 23:30) @alert(2015-02-25 14:00) @pom(80) 109 | - summarise life weaver in the forest @pom(15) @done(2015-02-22 16:00) @start(2015-02-24) @alert(2015-03-06) @status(grade) 110 | - try group program along the river 111 | - work life hypothesis in the forest @due(2015-02-10 14:00) @start(2015-02-04) @status(grade) @pom(70) @priority(3) 112 | - work school group @status(throw) @start(2015-03-01) @mins(80) @alert(2015-02-05 14:00) @due(2015-02-23 02:00) 113 | - aggregate mountain time at the office @pom(80) @due(2015-03-07 19:30) @start(2015-02-22) @mins(25) @done(2015-02-02 16:00) @status(grade) @priority(2) 114 | - try termite mound derivation on the mountain @pom(95) @start(2015-03-08) @alert(2015-02-17 17:00) @mins(60) @priority(1) 115 | - make umbrella number after breakfast @mins(30) 116 | - call water time @status(grade) @pom(45) @priority(1) @alert(2015-02-05 20:00) @mins(60) @due(2015-02-11 18:30) @done(2015-02-21 02:00) 117 | - winnow time world in a boat @pom(40) 118 | - call number group by a bridge @due(2015-02-09 02:30) 119 | - think time contradiction under the maples @done(2015-02-16 04:00) 120 | 121 | ## call school @done(2015-02-16 04:30) @priority(1) @mins(60) @alert(2015-03-02 07:00) @pom(70) @start(2015-02-28) @status(throw) 122 | - work derivation theorem 123 | - give derivation sandwich in a boat 124 | - try number weaver tomorrow morning @done(2015-02-12 18:30) @alert(2015-02-16 19:30) 125 | - link toothbrush group by a bridge @pom(45) 126 | - call umbrella question @pom(95) 127 | - think method question tomorrow morning @alert(2015-02-23 21:00) 128 | - give termite mound hypothesis under the maples @due(2015-03-06 05:00) @done(2015-02-08 17:00) 129 | - work thing derivation at home 130 | - think thing system @status(wait) @done(2015-02-16 06:00) @due(2015-02-10 04:00) @priority(2) @alert(2015-02-28 06:00) 131 | - summarise termite mound function under the maples @mins(20) @priority(3) @due(2015-03-05 14:00) 132 | - give question number on the mountain 133 | - make number weaver after breakfast @due(2015-03-03 16:00) --------------------------------------------------------------------------------