├── .gitignore ├── 2022-10-02-065309.png ├── 2022-10-02-065739.png ├── 2022-10-02-065802.png ├── OmniFocus.d.ts ├── Project from Template.draftsAction ├── README.md ├── copySkipRepeat.omnifocusjs ├── Resources │ ├── copySkipRepeat.js │ ├── en.lproj │ │ ├── copySkipRepeat.strings │ │ └── manifest.strings │ └── icon.png └── manifest.json ├── draftstemplate.omnifocusjs ├── Resources │ ├── draftstemplate.js │ ├── draftstemplate.png │ └── en.lproj │ │ ├── draftstemplate.strings │ │ └── manifest.strings └── manifest.json ├── duration.omnifocusjs ├── Resources │ ├── duration.js │ ├── duration.png │ └── en.lproj │ │ ├── duration.strings │ │ └── manifest.strings └── manifest.json ├── focus-home.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── focus.strings │ │ └── manifest.strings │ ├── focus.js │ └── focus.png └── manifest.json ├── focus-not-alarms.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── focus.strings │ │ └── manifest.strings │ ├── focus.js │ └── focus.png └── manifest.json ├── focus-work.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── focus.strings │ │ └── manifest.strings │ ├── focus.js │ └── focus.png └── manifest.json ├── repeatTag.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── manifest.strings │ │ └── repeat.strings │ ├── repeat.js │ └── repeat.png └── manifest.json ├── sentinel.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── manifest.strings │ │ └── selectionSentinel.strings │ ├── icon.png │ └── selectionSentinel.js └── manifest.json ├── sort.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── manifest.strings │ │ └── sort.strings │ ├── sort.js │ └── sort.png └── manifest.json ├── template.omnifocusjs ├── Resources │ ├── en.lproj │ │ ├── manifest.strings │ │ └── selection.strings │ ├── icon.png │ ├── lib.js │ └── selection.js └── manifest.json ├── toggle.omnifocusjs ├── Resources │ ├── chooseActivate.js │ ├── chooseOnHold.js │ ├── en.lproj │ │ ├── chooseActivate.strings │ │ ├── chooseOnHold.strings │ │ ├── manifest.strings │ │ ├── selectionActivate.strings │ │ └── selectionOnHold.strings │ ├── pause.png │ ├── play.png │ ├── selectionActivate.js │ ├── selectionOnHold.js │ └── toggleLib.js └── manifest.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /SalsSnippets.omnigrafflejs/ 3 | /node_modules/ 4 | /delegation.omnijs/ 5 | /toggle2.omnifocusjs/ 6 | .DS_Store 7 | /.obsidian 8 | -------------------------------------------------------------------------------- /2022-10-02-065309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/2022-10-02-065309.png -------------------------------------------------------------------------------- /2022-10-02-065739.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/2022-10-02-065739.png -------------------------------------------------------------------------------- /2022-10-02-065802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/2022-10-02-065802.png -------------------------------------------------------------------------------- /OmniFocus.d.ts: -------------------------------------------------------------------------------- 1 | // From Omni Automation... doc, Release Notes, Jan 5th 2021, "Typescript API definitions" link 2 | 3 | // TypeScript definitions for OmniFocus 3.11.7 (149.14) on macOS 10.15.7 4 | // Generated on 2021-05-31 08:03:26 +0000 5 | 6 | // To use these definitions, save this file as `OmniFocus.d.ts` 7 | // and create a `tsconfig.json` file with compiler settings which indicate 8 | // an appropriate set of implicitly defined TypeScript libraries: 9 | // 10 | // { 11 | // "compilerOptions": { 12 | // "lib": ["es7"] 13 | // } 14 | // } 15 | 16 | 17 | // Alert 18 | 19 | declare class Alert { 20 | constructor (title: string, message: string); 21 | show(callback: Function | null): Promise; 22 | addOption(string: string); 23 | } 24 | 25 | // Application 26 | 27 | declare class Application { 28 | openDocument(from: Document | null, url: URL, completed: Function); 29 | readonly buildVersion: Version; 30 | readonly commandKeyDown: boolean; 31 | readonly controlKeyDown: boolean; 32 | readonly name: string; 33 | readonly optionKeyDown: boolean; 34 | readonly platformName: string; 35 | readonly shiftKeyDown: boolean; 36 | readonly userVersion: Version; 37 | readonly version: string; 38 | } 39 | 40 | // ApplyResult 41 | 42 | declare namespace ApplyResult { 43 | const SkipChildren: ApplyResult; 44 | const SkipPeers: ApplyResult; 45 | const Stop: ApplyResult; 46 | const all: Array; 47 | } 48 | 49 | declare class ApplyResult { 50 | } 51 | 52 | // FolderArray 53 | 54 | declare class FolderArray extends Array { 55 | byName(name: string): Folder | null; 56 | } 57 | 58 | // ProjectArray 59 | 60 | declare class ProjectArray extends Array { 61 | byName(name: string): Project | null; 62 | } 63 | 64 | // SectionArray 65 | 66 | declare class SectionArray extends Array { 67 | byName(name: string): Project | Folder | null; 68 | } 69 | 70 | // Library 71 | 72 | declare class Library extends SectionArray { 73 | apply(f: Function): ApplyResult | null; 74 | readonly beginning: Folder.ChildInsertionLocation; 75 | readonly ending: Folder.ChildInsertionLocation; 76 | } 77 | 78 | // TagArray 79 | 80 | declare class TagArray extends Array { 81 | byName(name: string): Tag | null; 82 | } 83 | 84 | // Tags 85 | 86 | declare class Tags extends TagArray { 87 | apply(f: Function): ApplyResult | null; 88 | readonly beginning: Tag.ChildInsertionLocation; 89 | readonly ending: Tag.ChildInsertionLocation; 90 | } 91 | 92 | // TaskArray 93 | 94 | declare class TaskArray extends Array { 95 | byName(name: string): Task | null; 96 | } 97 | 98 | // Inbox 99 | 100 | declare class Inbox extends TaskArray { 101 | apply(f: Function): ApplyResult | null; 102 | readonly beginning: Task.ChildInsertionLocation; 103 | readonly ending: Task.ChildInsertionLocation; 104 | } 105 | 106 | // Calendar 107 | 108 | declare namespace Calendar { 109 | const buddhist: Calendar; 110 | const chinese: Calendar; 111 | const coptic: Calendar; 112 | const current: Calendar; 113 | const ethiopicAmeteAlem: Calendar; 114 | const ethiopicAmeteMihret: Calendar; 115 | const gregorian: Calendar; 116 | const hebrew: Calendar; 117 | const indian: Calendar; 118 | const islamic: Calendar; 119 | const islamicCivil: Calendar; 120 | const islamicTabular: Calendar; 121 | const islamicUmmAlQura: Calendar; 122 | const iso8601: Calendar; 123 | const japanese: Calendar; 124 | const persian: Calendar; 125 | const republicOfChina: Calendar; 126 | } 127 | 128 | declare class Calendar { 129 | dateByAddingDateComponents(date: Date, components: DateComponents): Date | null; 130 | dateFromDateComponents(components: DateComponents): Date | null; 131 | dateComponentsFromDate(date: Date): DateComponents; 132 | dateComponentsBetweenDates(start: Date, end: Date): DateComponents; 133 | startOfDay(date: Date): Date; 134 | readonly identifier: string; 135 | readonly locale: Locale | null; 136 | readonly timeZone: TimeZone; 137 | } 138 | 139 | // Color 140 | 141 | declare namespace Color { 142 | function RGB(r: number, g: number, b: number, a: number | null): Color; 143 | function HSB(h: number, s: number, b: number, a: number | null): Color; 144 | function White(w: number, a: number | null): Color; 145 | const black: Color; 146 | const blue: Color; 147 | const brown: Color; 148 | const clear: Color; 149 | const cyan: Color; 150 | const darkGray: Color; 151 | const gray: Color; 152 | const green: Color; 153 | const lightGray: Color; 154 | const magenta: Color; 155 | const orange: Color; 156 | const purple: Color; 157 | const red: Color; 158 | const white: Color; 159 | const yellow: Color; 160 | } 161 | 162 | declare class Color { 163 | blend(otherColor: Color, fraction: number): Color | null; 164 | readonly alpha: number; 165 | readonly blue: number; 166 | readonly brightness: number; 167 | readonly colorSpace: ColorSpace; 168 | readonly green: number; 169 | readonly hue: number; 170 | readonly red: number; 171 | readonly saturation: number; 172 | readonly white: number; 173 | } 174 | 175 | // ColorSpace 176 | 177 | declare namespace ColorSpace { 178 | const CMYK: ColorSpace; 179 | const HSB: ColorSpace; 180 | const Named: ColorSpace; 181 | const Pattern: ColorSpace; 182 | const RGB: ColorSpace; 183 | const White: ColorSpace; 184 | const all: Array; 185 | } 186 | 187 | declare class ColorSpace { 188 | } 189 | 190 | // Console 191 | 192 | declare class Console { 193 | log(message: Object, additional: Array); 194 | error(message: Object, additional: Array); 195 | info(message: Object, additional: Array); 196 | warn(message: Object, additional: Array); 197 | clear(); 198 | } 199 | 200 | // Credentials 201 | 202 | declare class Credentials { 203 | constructor (); 204 | read(service: string): object | null; 205 | write(service: string, username: string, password: string); 206 | remove(service: string); 207 | readBookmark(service: string): URL.Bookmark | null; 208 | writeBookmark(service: string, bookmark: URL.Bookmark); 209 | } 210 | 211 | // Data 212 | 213 | declare namespace Data { 214 | function fromString(string: string): Data; 215 | function fromBase64(string: string): Data; 216 | } 217 | 218 | declare class Data { 219 | toString(): string; 220 | toBase64(): string; 221 | readonly length: number; 222 | readonly toObject: Object | null; 223 | } 224 | 225 | // Database 226 | 227 | declare class Database { 228 | tagNamed(name: string): Tag | null; 229 | folderNamed(name: string): Folder | null; 230 | projectNamed(name: string): Project | null; 231 | projectsMatching(search: string): Array; 232 | foldersMatching(search: string): Array; 233 | tagsMatching(search: string): Array; 234 | taskNamed(name: string): Task | null; 235 | save(); 236 | moveTasks(tasks: Array, position: Project | Task | Task.ChildInsertionLocation); 237 | duplicateTasks(tasks: Array, position: Project | Task | Task.ChildInsertionLocation): TaskArray; 238 | convertTasksToProjects(tasks: Array, position: Folder | Folder.ChildInsertionLocation): Array; 239 | moveSections(sections: Array, position: Folder | Folder.ChildInsertionLocation); 240 | duplicateSections(sections: Array, position: Folder | Folder.ChildInsertionLocation): SectionArray; 241 | moveTags(tags: Array, position: Tag | Tag.ChildInsertionLocation); 242 | duplicateTags(tags: Array, position: Tag | Tag.ChildInsertionLocation): TagArray; 243 | cleanUp(); 244 | undo(); 245 | redo(); 246 | deleteObject(object: DatabaseObject); 247 | copyTasksToPasteboard(tasks: Array, pasteboard: Pasteboard); 248 | canPasteTasks(pasteboard: Pasteboard): boolean; 249 | pasteTasksFromPasteboard(pasteboard: Pasteboard): Array; 250 | readonly canRedo: boolean; 251 | readonly canUndo: boolean; 252 | readonly document: DatabaseDocument | null; 253 | readonly flattenedFolders: FolderArray; 254 | readonly flattenedProjects: ProjectArray; 255 | readonly flattenedSections: SectionArray; 256 | readonly flattenedTags: TagArray; 257 | readonly flattenedTasks: TaskArray; 258 | readonly folders: FolderArray; 259 | readonly inbox: Inbox; 260 | readonly library: Library; 261 | readonly projects: ProjectArray; 262 | readonly settings: Settings; 263 | readonly tags: Tags; 264 | } 265 | 266 | // DatabaseObject 267 | 268 | declare class DatabaseObject { 269 | readonly id: ObjectIdentifier; 270 | } 271 | 272 | // DatedObject 273 | 274 | declare class DatedObject extends DatabaseObject { 275 | added: Date | null; 276 | modified: Date | null; 277 | } 278 | 279 | // ActiveObject 280 | 281 | declare class ActiveObject extends DatedObject { 282 | active: boolean; 283 | readonly effectiveActive: boolean; 284 | } 285 | 286 | // Folder 287 | 288 | declare namespace Folder { 289 | function byIdentifier(identifier: string): Folder | null; 290 | } 291 | 292 | declare class Folder extends ActiveObject { 293 | constructor (name: string, position: Folder | Folder.ChildInsertionLocation | null); 294 | folderNamed(name: string): Folder | null; 295 | projectNamed(name: string): Project | null; 296 | sectionNamed(name: string): Project | Folder | null; 297 | childNamed(name: string): Project | Folder | null; 298 | apply(f: Function): ApplyResult | null; 299 | readonly after: Folder.ChildInsertionLocation; 300 | readonly before: Folder.ChildInsertionLocation; 301 | readonly beginning: Folder.ChildInsertionLocation; 302 | readonly children: SectionArray; 303 | readonly ending: Folder.ChildInsertionLocation; 304 | readonly flattenedChildren: SectionArray; 305 | readonly flattenedFolders: FolderArray; 306 | readonly flattenedProjects: ProjectArray; 307 | readonly flattenedSections: SectionArray; 308 | readonly folders: FolderArray; 309 | name: string; 310 | readonly parent: Folder | null; 311 | readonly projects: ProjectArray; 312 | readonly sections: SectionArray; 313 | status: Folder.Status; 314 | } 315 | 316 | // Tag 317 | 318 | declare namespace Tag { 319 | function byIdentifier(identifier: string): Tag | null; 320 | const forecastTag: Tag | null; 321 | } 322 | 323 | declare class Tag extends ActiveObject { 324 | constructor (name: string, position: Tag | Tag.ChildInsertionLocation | null); 325 | tagNamed(name: string): Tag | null; 326 | childNamed(name: string): Tag | null; 327 | apply(f: Function): ApplyResult | null; 328 | readonly after: Tag.ChildInsertionLocation; 329 | allowsNextAction: boolean; 330 | readonly availableTasks: TaskArray; 331 | readonly before: Tag.ChildInsertionLocation; 332 | readonly beginning: Tag.ChildInsertionLocation; 333 | readonly children: TagArray; 334 | readonly ending: Tag.ChildInsertionLocation; 335 | readonly flattenedChildren: TagArray; 336 | readonly flattenedTags: TagArray; 337 | name: string; 338 | readonly parent: Tag | null; 339 | readonly projects: ProjectArray; 340 | readonly remainingTasks: TaskArray; 341 | status: Tag.Status; 342 | readonly tags: TagArray; 343 | readonly tasks: TaskArray; 344 | } 345 | 346 | // Task 347 | 348 | declare namespace Task { 349 | function byParsingTransportText(text: string, singleTask: boolean | null): Array; 350 | function byIdentifier(identifier: string): Task | null; 351 | } 352 | 353 | declare class Task extends ActiveObject { 354 | constructor (name: string, position: Project | Task | Task.ChildInsertionLocation | null); 355 | taskNamed(name: string): Task | null; 356 | childNamed(name: string): Task | null; 357 | appendStringToNote(stringToAppend: string); 358 | addLinkedFileURL(url: URL); 359 | removeLinkedFileWithURL(url: URL); 360 | addAttachment(attachment: FileWrapper); 361 | removeAttachmentAtIndex(index: number); 362 | addTag(tag: Tag); 363 | addTags(tags: Array); 364 | removeTag(tag: Tag); 365 | removeTags(tags: Array); 366 | clearTags(); 367 | markComplete(date: Date | null): Task; 368 | markIncomplete(); 369 | drop(allOccurrences: boolean); 370 | apply(f: Function): ApplyResult | null; 371 | addNotification(info: number | Date): Task.Notification; 372 | removeNotification(notification: Task.Notification); 373 | readonly after: Task.ChildInsertionLocation; 374 | assignedContainer: Project | Task | Inbox | null; 375 | attachments: Array; 376 | readonly before: Task.ChildInsertionLocation; 377 | readonly beginning: Task.ChildInsertionLocation; 378 | readonly children: TaskArray; 379 | readonly completed: boolean; 380 | completedByChildren: boolean; 381 | readonly completionDate: Date | null; 382 | readonly containingProject: Project | null; 383 | deferDate: Date | null; 384 | readonly dropDate: Date | null; 385 | dueDate: Date | null; 386 | readonly effectiveCompletedDate: Date | null; 387 | readonly effectiveDeferDate: Date | null; 388 | readonly effectiveDropDate: Date | null; 389 | readonly effectiveDueDate: Date | null; 390 | readonly effectiveFlagged: boolean; 391 | readonly ending: Task.ChildInsertionLocation; 392 | estimatedMinutes: number | null; 393 | flagged: boolean; 394 | readonly flattenedChildren: TaskArray; 395 | readonly flattenedTasks: TaskArray; 396 | readonly hasChildren: boolean; 397 | readonly inInbox: boolean; 398 | readonly linkedFileURLs: Array; 399 | name: string; 400 | note: string; 401 | readonly notifications: Array; 402 | readonly parent: Task | null; 403 | readonly project: Project | null; 404 | repetitionRule: Task.RepetitionRule | null; 405 | sequential: boolean; 406 | shouldUseFloatingTimeZone: boolean; 407 | readonly tags: TagArray; 408 | readonly taskStatus: Task.Status; 409 | readonly tasks: TaskArray; 410 | } 411 | 412 | // Perspective.Custom 413 | 414 | declare namespace Perspective.Custom { 415 | function byName(name: string): Perspective.Custom | null; 416 | function byIdentifier(identifier: string): Perspective.Custom | null; 417 | const all: Array; 418 | } 419 | 420 | declare namespace Perspective { 421 | class Custom extends DatedObject { 422 | fileWrapper(): FileWrapper; 423 | writeFileRepresentationIntoDirectory(parentURL: URL): URL; 424 | readonly identifier: string; 425 | readonly name: string; 426 | } 427 | } 428 | 429 | // Task.Notification 430 | 431 | declare namespace Task { 432 | class Notification extends DatedObject { 433 | absoluteFireDate: Date; 434 | readonly initialFireDate: Date; 435 | readonly isSnoozed: boolean; 436 | readonly kind: Task.Notification.Kind; 437 | readonly nextFireDate: Date | null; 438 | relativeFireOffset: number; 439 | repeatInterval: number; 440 | readonly task: Task | null; 441 | readonly usesFloatingTimeZone: boolean; 442 | } 443 | } 444 | 445 | // Project 446 | 447 | declare namespace Project { 448 | function byIdentifier(identifier: string): Project | null; 449 | } 450 | 451 | declare class Project extends DatabaseObject { 452 | constructor (name: string, position: Folder | Folder.ChildInsertionLocation | null); 453 | taskNamed(name: string): Task | null; 454 | appendStringToNote(stringToAppend: string); 455 | addAttachment(attachment: FileWrapper); 456 | removeAttachmentAtIndex(index: number); 457 | markComplete(date: Date | null): Task; 458 | markIncomplete(); 459 | addNotification(info: number | Date): Task.Notification; 460 | removeNotification(notification: Task.Notification); 461 | addTag(tag: Tag); 462 | addTags(tags: Array); 463 | removeTag(tag: Tag); 464 | removeTags(tags: Array); 465 | clearTags(); 466 | addLinkedFileURL(url: URL); 467 | removeLinkedFileWithURL(url: URL); 468 | readonly after: Folder.ChildInsertionLocation; 469 | attachments: Array; 470 | readonly before: Folder.ChildInsertionLocation; 471 | readonly beginning: Task.ChildInsertionLocation; 472 | readonly children: TaskArray; 473 | readonly completed: boolean; 474 | completedByChildren: boolean; 475 | completionDate: Date | null; 476 | containsSingletonActions: boolean; 477 | defaultSingletonActionHolder: boolean; 478 | deferDate: Date | null; 479 | dropDate: Date | null; 480 | dueDate: Date | null; 481 | readonly effectiveCompletedDate: Date | null; 482 | readonly effectiveDeferDate: Date | null; 483 | readonly effectiveDropDate: Date | null; 484 | readonly effectiveDueDate: Date | null; 485 | readonly effectiveFlagged: boolean; 486 | readonly ending: Task.ChildInsertionLocation; 487 | estimatedMinutes: number | null; 488 | flagged: boolean; 489 | readonly flattenedChildren: TaskArray; 490 | readonly flattenedTasks: TaskArray; 491 | readonly hasChildren: boolean; 492 | lastReviewDate: Date | null; 493 | readonly linkedFileURLs: Array; 494 | name: string; 495 | nextReviewDate: Date | null; 496 | readonly nextTask: Task | null; 497 | note: string; 498 | readonly notifications: Array; 499 | readonly parentFolder: Folder | null; 500 | repetitionRule: Task.RepetitionRule | null; 501 | reviewInterval: Project.ReviewInterval; 502 | sequential: boolean; 503 | shouldUseFloatingTimeZone: boolean; 504 | status: Project.Status; 505 | readonly tags: TagArray; 506 | readonly task: Task; 507 | readonly taskStatus: Task.Status; 508 | readonly tasks: TaskArray; 509 | } 510 | 511 | // DateComponents 512 | 513 | declare class DateComponents { 514 | constructor (); 515 | readonly date: Date | null; 516 | day: number | null; 517 | era: number | null; 518 | hour: number | null; 519 | minute: number | null; 520 | month: number | null; 521 | nanosecond: number | null; 522 | second: number | null; 523 | timeZone: TimeZone | null; 524 | year: number | null; 525 | } 526 | 527 | // DateRange 528 | 529 | declare class DateRange { 530 | readonly end: Date; 531 | readonly name: string; 532 | readonly start: Date; 533 | } 534 | 535 | // Decimal 536 | 537 | declare namespace Decimal { 538 | function fromString(string: string): Decimal; 539 | const maximum: Decimal; 540 | const minimum: Decimal; 541 | const notANumber: Decimal; 542 | const one: Decimal; 543 | const zero: Decimal; 544 | } 545 | 546 | declare class Decimal { 547 | toString(): string; 548 | add(number: Decimal): Decimal; 549 | subtract(number: Decimal): Decimal; 550 | multiply(number: Decimal): Decimal; 551 | divide(number: Decimal): Decimal; 552 | compare(number: Decimal): number; 553 | equals(number: Decimal): boolean; 554 | } 555 | 556 | // Device 557 | 558 | declare namespace Device { 559 | const current: Device; 560 | } 561 | 562 | declare class Device { 563 | readonly iOS: boolean; 564 | readonly iPad: boolean; 565 | readonly mac: boolean; 566 | readonly operatingSystemVersion: Version; 567 | readonly type: DeviceType | null; 568 | } 569 | 570 | // DeviceType 571 | 572 | declare namespace DeviceType { 573 | const all: Array; 574 | const iPad: DeviceType; 575 | const iPhone: DeviceType; 576 | const mac: DeviceType; 577 | } 578 | 579 | declare class DeviceType { 580 | } 581 | 582 | // Document 583 | 584 | declare namespace Document { 585 | function makeNew(resultFunction: Function | null): Promise; 586 | function makeNewAndShow(resultFunction: Function | null): Promise; 587 | } 588 | 589 | declare class Document { 590 | close(didCancel: Function | null); 591 | save(); 592 | fileWrapper(type: string | null): FileWrapper; 593 | makeFileWrapper(baseName: string, type: string | null): Promise; 594 | undo(); 595 | redo(); 596 | show(resultFunction: Function | null); 597 | readonly canRedo: boolean; 598 | readonly canUndo: boolean; 599 | readonly fileType: string | null; 600 | readonly name: string | null; 601 | readonly writableTypes: Array; 602 | } 603 | 604 | // DatabaseDocument 605 | 606 | declare class DatabaseDocument extends Document { 607 | newWindow(): Promise; 608 | newTabOnWindow(window: DocumentWindow): Promise; 609 | readonly windows: Array; 610 | } 611 | 612 | // Email 613 | 614 | declare class Email { 615 | constructor (); 616 | generate(); 617 | blindCarbonCopy: string | Array | null; 618 | body: string | null; 619 | carbonCopy: string | Array | null; 620 | fileWrappers: Array; 621 | receiver: string | Array | null; 622 | subject: string | null; 623 | } 624 | 625 | // FilePicker 626 | 627 | declare class FilePicker { 628 | constructor (); 629 | show(): Promise>; 630 | folders: boolean; 631 | message: string; 632 | multiple: boolean; 633 | types: Array | null; 634 | } 635 | 636 | // FileSaver 637 | 638 | declare class FileSaver { 639 | constructor (); 640 | show(fileWrapper: FileWrapper): Promise; 641 | message: string; 642 | nameLabel: string; 643 | prompt: string; 644 | types: Array | null; 645 | } 646 | 647 | // FileWrapper 648 | 649 | declare namespace FileWrapper { 650 | function withContents(name: string | null, contents: Data): FileWrapper; 651 | function withChildren(name: string | null, children: Array): FileWrapper; 652 | } 653 | 654 | declare class FileWrapper { 655 | filenameForChild(child: FileWrapper): string | null; 656 | readonly children: Array; 657 | readonly contents: Data | null; 658 | readonly destination: URL | null; 659 | filename: string | null; 660 | preferredFilename: string | null; 661 | readonly type: FileWrapper.Type; 662 | } 663 | 664 | // FileWrapper.Type 665 | 666 | declare namespace FileWrapper.Type { 667 | const Directory: FileWrapper.Type; 668 | const File: FileWrapper.Type; 669 | const Link: FileWrapper.Type; 670 | const all: Array; 671 | } 672 | 673 | declare namespace FileWrapper { 674 | class Type { 675 | } 676 | } 677 | 678 | // Folder.ChildInsertionLocation 679 | 680 | declare namespace Folder { 681 | class ChildInsertionLocation { 682 | } 683 | } 684 | 685 | // Folder.Status 686 | 687 | declare namespace Folder.Status { 688 | const Active: Folder.Status; 689 | const Dropped: Folder.Status; 690 | const all: Array; 691 | } 692 | 693 | declare namespace Folder { 694 | class Status { 695 | } 696 | } 697 | 698 | // ForecastDay 699 | 700 | declare namespace ForecastDay { 701 | let badgeCountsIncludeDeferredItems: boolean; 702 | } 703 | 704 | declare class ForecastDay { 705 | badgeKind(): ForecastDay.Status; 706 | readonly badgeCount: number; 707 | readonly date: Date; 708 | readonly deferredCount: number; 709 | readonly kind: ForecastDay.Kind; 710 | readonly name: string; 711 | } 712 | 713 | // ForecastDay.Kind 714 | 715 | declare namespace ForecastDay.Kind { 716 | const Day: ForecastDay.Kind; 717 | const DistantFuture: ForecastDay.Kind; 718 | const FutureMonth: ForecastDay.Kind; 719 | const Past: ForecastDay.Kind; 720 | const Today: ForecastDay.Kind; 721 | const all: Array; 722 | } 723 | 724 | declare namespace ForecastDay { 725 | class Kind { 726 | } 727 | } 728 | 729 | // ForecastDay.Status 730 | 731 | declare namespace ForecastDay.Status { 732 | const Available: ForecastDay.Status; 733 | const DueSoon: ForecastDay.Status; 734 | const NoneAvailable: ForecastDay.Status; 735 | const Overdue: ForecastDay.Status; 736 | const all: Array; 737 | } 738 | 739 | declare namespace ForecastDay { 740 | class Status { 741 | } 742 | } 743 | 744 | // Form 745 | 746 | declare class Form { 747 | constructor (); 748 | addField(field: Form.Field, index: number | null); 749 | removeField(field: Form.Field); 750 | show(title: string, confirmTitle: string): Promise
; 751 | readonly fields: Array; 752 | validate: Function | null; 753 | readonly values: Object; 754 | } 755 | 756 | // Form.Field 757 | 758 | declare namespace Form { 759 | class Field { 760 | readonly displayName: string | null; 761 | readonly key: string; 762 | } 763 | } 764 | 765 | // Form.Field.Checkbox 766 | 767 | declare namespace Form.Field { 768 | class Checkbox extends Form.Field { 769 | constructor (key: string, displayName: string | null, value: boolean | null); 770 | } 771 | } 772 | 773 | // Form.Field.Date 774 | 775 | declare namespace Form.Field { 776 | class Date extends Form.Field { 777 | constructor (key: string, displayName: string | null, value: Date | null, formatter: Formatter.Date | null); 778 | } 779 | } 780 | 781 | // Form.Field.MultipleOptions 782 | 783 | declare namespace Form.Field { 784 | class MultipleOptions extends Form.Field { 785 | constructor (key: string, displayName: string | null, options: Array, names: Array | null, selected: Array); 786 | } 787 | } 788 | 789 | // Form.Field.Option 790 | 791 | declare namespace Form.Field { 792 | class Option extends Form.Field { 793 | constructor (key: string, displayName: string | null, options: Array, names: Array | null, selected: Object | null, nullOptionTitle: string | null); 794 | allowsNull: boolean; 795 | nullOptionTitle: string | null; 796 | } 797 | } 798 | 799 | // Form.Field.Password 800 | 801 | declare namespace Form.Field { 802 | class Password extends Form.Field { 803 | constructor (key: string, displayName: string | null, value: string | null); 804 | } 805 | } 806 | 807 | // Form.Field.String 808 | 809 | declare namespace Form.Field { 810 | class String extends Form.Field { 811 | constructor (key: string, displayName: string | null, value: Object | null, formatter: Formatter | null); 812 | } 813 | } 814 | 815 | // Formatter 816 | 817 | declare class Formatter { 818 | } 819 | 820 | // Formatter.Date 821 | 822 | declare namespace Formatter.Date { 823 | function withStyle(dateStyle: Formatter.Date.Style, timeStyle: Formatter.Date.Style | null): Formatter.Date; 824 | function withFormat(format: string): Formatter.Date; 825 | const iso8601: Formatter.Date; 826 | } 827 | 828 | declare namespace Formatter { 829 | class Date extends Formatter { 830 | stringFromDate(date: Date): string; 831 | dateFromString(string: string): Date | null; 832 | calendar: Calendar; 833 | readonly dateFormat: string; 834 | locale: Locale; 835 | timeZone: TimeZone; 836 | } 837 | } 838 | 839 | // Formatter.Decimal 840 | 841 | declare namespace Formatter.Decimal { 842 | function currency(code: string | null): Formatter.Decimal; 843 | const currencyCodes: Array; 844 | const custom: Formatter.Decimal; 845 | const decimal: Formatter.Decimal; 846 | const percent: Formatter.Decimal; 847 | const percentWithDecimal: Formatter.Decimal; 848 | const plain: Formatter.Decimal; 849 | const thousandsAndDecimal: Formatter.Decimal; 850 | } 851 | 852 | declare namespace Formatter { 853 | class Decimal extends Formatter { 854 | stringFromDecimal(number: Decimal): string | null; 855 | decimalFromString(string: string): Decimal | null; 856 | decimalSeparator: string; 857 | negativeFormat: string; 858 | positiveFormat: string; 859 | thousandsSeparator: string | null; 860 | zeroSymbol: string | null; 861 | } 862 | } 863 | 864 | // Formatter.Duration 865 | 866 | declare namespace Formatter { 867 | class Duration extends Formatter { 868 | constructor (); 869 | stringFromDecimal(number: Decimal): string | null; 870 | decimalFromString(string: string): Decimal | null; 871 | hoursPerDay: number; 872 | hoursPerWeek: number; 873 | useVerboseFormat: boolean; 874 | } 875 | } 876 | 877 | // Formatter.Date.Style 878 | 879 | declare namespace Formatter.Date.Style { 880 | const Full: Formatter.Date.Style; 881 | const Long: Formatter.Date.Style; 882 | const Medium: Formatter.Date.Style; 883 | const Short: Formatter.Date.Style; 884 | const all: Array; 885 | } 886 | 887 | declare namespace Formatter.Date { 888 | class Style { 889 | } 890 | } 891 | 892 | // Image 893 | 894 | declare class Image { 895 | } 896 | 897 | // LigatureStyle 898 | 899 | declare namespace LigatureStyle { 900 | const All: LigatureStyle; 901 | const Essential: LigatureStyle; 902 | const Standard: LigatureStyle; 903 | const all: Array; 904 | } 905 | 906 | declare class LigatureStyle { 907 | } 908 | 909 | // Locale 910 | 911 | declare namespace Locale { 912 | const identifiers: Array; 913 | } 914 | 915 | declare class Locale { 916 | constructor (identifier: string); 917 | readonly calendar: Calendar; 918 | readonly currencyCode: string | null; 919 | readonly identifier: string; 920 | } 921 | 922 | // MenuItem 923 | 924 | declare class MenuItem { 925 | checked: boolean; 926 | label: string; 927 | } 928 | 929 | // NamedStyle.List 930 | 931 | declare namespace NamedStyle { 932 | class List { 933 | add(name: string | null): NamedStyle; 934 | byName(name: string): NamedStyle | null; 935 | byIdentifier(identifier: string): NamedStyle | null; 936 | moveStyles(styles: Array, position: NamedStylePosition); 937 | duplicateStyles(styles: Array, position: NamedStylePosition): Array; 938 | readonly all: Array; 939 | readonly beginning: NamedStylePosition; 940 | readonly end: NamedStylePosition; 941 | } 942 | } 943 | 944 | // NamedStylePosition 945 | 946 | declare class NamedStylePosition { 947 | } 948 | 949 | // ObjectIdentifier 950 | 951 | declare class ObjectIdentifier { 952 | readonly objectClass: Object | null; 953 | readonly primaryKey: string; 954 | } 955 | 956 | // Pasteboard 957 | 958 | declare namespace Pasteboard { 959 | function makeUnique(): Pasteboard; 960 | const general: Pasteboard; 961 | } 962 | 963 | declare class Pasteboard { 964 | availableType(types: Array): TypeIdentifier | null; 965 | addItems(items: Array); 966 | clear(); 967 | dataForType(type: TypeIdentifier): Data | null; 968 | setDataForType(data: Data, type: TypeIdentifier); 969 | stringForType(type: TypeIdentifier): string | null; 970 | setStringForType(string: string, type: TypeIdentifier); 971 | URL: URL | null; 972 | URLs: Array | null; 973 | color: Color | null; 974 | colors: Array | null; 975 | readonly hasColors: boolean; 976 | readonly hasImages: boolean; 977 | readonly hasStrings: boolean; 978 | readonly hasURLs: boolean; 979 | image: Image | null; 980 | images: Array | null; 981 | items: Array; 982 | string: string | null; 983 | strings: Array | null; 984 | readonly types: Array; 985 | } 986 | 987 | // Pasteboard.Item 988 | 989 | declare namespace Pasteboard { 990 | class Item { 991 | constructor (); 992 | dataForType(type: TypeIdentifier): Data | null; 993 | setDataForType(data: Data, type: TypeIdentifier); 994 | stringForType(type: TypeIdentifier): string | null; 995 | setStringForType(string: string, type: TypeIdentifier); 996 | readonly types: Array; 997 | } 998 | } 999 | 1000 | // Perspective 1001 | 1002 | declare namespace Perspective { 1003 | const all: Array; 1004 | } 1005 | 1006 | declare class Perspective { 1007 | } 1008 | 1009 | // Perspective.BuiltIn 1010 | 1011 | declare namespace Perspective.BuiltIn { 1012 | const Flagged: Perspective.BuiltIn; 1013 | const Forecast: Perspective.BuiltIn; 1014 | const Inbox: Perspective.BuiltIn; 1015 | const Nearby: Perspective.BuiltIn; 1016 | const Projects: Perspective.BuiltIn; 1017 | const Review: Perspective.BuiltIn; 1018 | const Search: Perspective.BuiltIn; 1019 | const Tags: Perspective.BuiltIn; 1020 | const all: Array; 1021 | } 1022 | 1023 | declare namespace Perspective { 1024 | class BuiltIn { 1025 | readonly name: string; 1026 | } 1027 | } 1028 | 1029 | // PlugIn 1030 | 1031 | declare namespace PlugIn { 1032 | function find(identifier: string, minimumVersion: Version | null): PlugIn | null; 1033 | const all: Array; 1034 | } 1035 | 1036 | declare class PlugIn { 1037 | library(identifier: string): PlugIn.Library | null; 1038 | action(identifier: string): PlugIn.Action | null; 1039 | handler(identifier: string): PlugIn.Handler | null; 1040 | resourceNamed(name: string): URL | null; 1041 | imageNamed(name: string): Image | null; 1042 | readonly URL: URL | null; 1043 | readonly actions: Array; 1044 | readonly author: string; 1045 | readonly description: string; 1046 | readonly displayName: string; 1047 | readonly handlers: Array; 1048 | readonly identifier: string; 1049 | readonly libraries: Array; 1050 | readonly version: Version; 1051 | } 1052 | 1053 | // PlugIn.Action 1054 | 1055 | declare namespace PlugIn { 1056 | class Action { 1057 | constructor (perform: Function); 1058 | readonly description: string; 1059 | readonly label: string; 1060 | readonly longLabel: string; 1061 | readonly mediumLabel: string; 1062 | readonly name: string; 1063 | readonly paletteLabel: string; 1064 | readonly perform: Function; 1065 | readonly plugIn: PlugIn; 1066 | readonly shortLabel: string; 1067 | validate: Function | null; 1068 | } 1069 | } 1070 | 1071 | // PlugIn.Handler 1072 | 1073 | declare namespace PlugIn { 1074 | class Handler { 1075 | constructor (invoke: Function); 1076 | readonly invoke: Function; 1077 | readonly name: string; 1078 | readonly plugIn: PlugIn; 1079 | willAttach: Function | null; 1080 | willDetach: Function | null; 1081 | } 1082 | } 1083 | 1084 | // PlugIn.Library 1085 | 1086 | declare namespace PlugIn { 1087 | class Library { 1088 | constructor (version: Version); 1089 | readonly name: string; 1090 | readonly plugIn: PlugIn; 1091 | readonly version: Version; 1092 | } 1093 | } 1094 | 1095 | // Preferences 1096 | 1097 | declare class Preferences { 1098 | constructor (identifier: string | null); 1099 | read(key: string): Object | null; 1100 | readBoolean(key: string): boolean; 1101 | readString(key: string): string | null; 1102 | readNumber(key: string): number; 1103 | readDate(key: string): Date | null; 1104 | readData(key: string): Data | null; 1105 | write(key: string, value: boolean | string | number | Date | Data | null); 1106 | remove(key: string); 1107 | readonly identifier: string; 1108 | } 1109 | 1110 | // Project.ReviewInterval 1111 | 1112 | declare namespace Project { 1113 | class ReviewInterval { 1114 | steps: number; 1115 | unit: string; 1116 | } 1117 | } 1118 | 1119 | // Project.Status 1120 | 1121 | declare namespace Project.Status { 1122 | const Active: Project.Status; 1123 | const Done: Project.Status; 1124 | const Dropped: Project.Status; 1125 | const OnHold: Project.Status; 1126 | const all: Array; 1127 | } 1128 | 1129 | declare namespace Project { 1130 | class Status { 1131 | } 1132 | } 1133 | 1134 | // Selection 1135 | 1136 | declare class Selection { 1137 | readonly allObjects: Array; 1138 | readonly database: Database | null; 1139 | readonly databaseObjects: Array; 1140 | readonly document: DatabaseDocument | null; 1141 | readonly folders: FolderArray; 1142 | readonly projects: ProjectArray; 1143 | readonly tags: TagArray; 1144 | readonly tasks: TaskArray; 1145 | readonly window: DocumentWindow | null; 1146 | } 1147 | 1148 | // Settings 1149 | 1150 | declare class Settings { 1151 | defaultObjectForKey(key: string): Object | null; 1152 | hasNonDefaultObjectForKey(key: string): boolean; 1153 | objectForKey(key: string): Object | null; 1154 | setObjectForKey(value: Object | null, key: string); 1155 | boolForKey(key: string): boolean; 1156 | setBoolForKey(value: boolean, key: string); 1157 | integerForKey(key: string): number; 1158 | setIntegerForKey(value: number, key: string); 1159 | readonly keys: Array; 1160 | } 1161 | 1162 | // SharePanel 1163 | 1164 | declare class SharePanel { 1165 | constructor (items: Array); 1166 | addItem(shareItem: URL | string | Image | FileWrapper); 1167 | addItems(shareItems: Array); 1168 | removeItem(shareItem: URL | string | Image | FileWrapper); 1169 | removeItems(shareItems: Array); 1170 | clearItems(); 1171 | show(); 1172 | items: Array; 1173 | } 1174 | 1175 | // Style 1176 | 1177 | declare class Style { 1178 | set(attribute: Style.Attribute, value: Object | null): boolean; 1179 | get(attribute: Style.Attribute): Object | null; 1180 | localValueForAttribute(attribute: Style.Attribute): Object | null; 1181 | addNamedStyle(namedStyle: NamedStyle); 1182 | removeNamedStyle(namedStyle: NamedStyle); 1183 | influencedBy(otherStyle: Style): boolean; 1184 | setStyle(style: Style); 1185 | clear(); 1186 | fontFillColor: Color; 1187 | readonly link: URL | null; 1188 | readonly locallyDefinedAttributes: Array; 1189 | readonly namedStyles: Array; 1190 | } 1191 | 1192 | // NamedStyle 1193 | 1194 | declare class NamedStyle extends Style { 1195 | remove(); 1196 | readonly after: NamedStylePosition; 1197 | readonly before: NamedStylePosition; 1198 | readonly identifier: string; 1199 | name: string; 1200 | } 1201 | 1202 | // Style.Attribute 1203 | 1204 | declare namespace Style.Attribute { 1205 | const BackgroundColor: Style.Attribute; 1206 | const BaselineOffset: Style.Attribute; 1207 | const BaselineSuperscript: Style.Attribute; 1208 | const Expansion: Style.Attribute; 1209 | const FontCondensed: Style.Attribute; 1210 | const FontFamily: Style.Attribute; 1211 | const FontFillColor: Style.Attribute; 1212 | const FontFixedPitch: Style.Attribute; 1213 | const FontItalic: Style.Attribute; 1214 | const FontName: Style.Attribute; 1215 | const FontNarrow: Style.Attribute; 1216 | const FontSize: Style.Attribute; 1217 | const FontStrokeColor: Style.Attribute; 1218 | const FontStrokeWidth: Style.Attribute; 1219 | const FontWeight: Style.Attribute; 1220 | const KerningAdjustment: Style.Attribute; 1221 | const LigatureSelection: Style.Attribute; 1222 | const Link: Style.Attribute; 1223 | const Obliqueness: Style.Attribute; 1224 | const ParagraphAlignment: Style.Attribute; 1225 | const ParagraphBaseWritingDirection: Style.Attribute; 1226 | const ParagraphDefaultTabInterval: Style.Attribute; 1227 | const ParagraphFirstLineHeadIndent: Style.Attribute; 1228 | const ParagraphHeadIndent: Style.Attribute; 1229 | const ParagraphLineHeightMultiple: Style.Attribute; 1230 | const ParagraphLineSpacing: Style.Attribute; 1231 | const ParagraphMaximumLineHeight: Style.Attribute; 1232 | const ParagraphMinimumLineHeight: Style.Attribute; 1233 | const ParagraphSpacing: Style.Attribute; 1234 | const ParagraphSpacingBefore: Style.Attribute; 1235 | const ParagraphTabStops: Style.Attribute; 1236 | const ParagraphTailIndent: Style.Attribute; 1237 | const ShadowBlurRadius: Style.Attribute; 1238 | const ShadowColor: Style.Attribute; 1239 | const ShadowOffset: Style.Attribute; 1240 | const StrikethroughAffinity: Style.Attribute; 1241 | const StrikethroughColor: Style.Attribute; 1242 | const StrikethroughPattern: Style.Attribute; 1243 | const StrikethroughStyle: Style.Attribute; 1244 | const UnderlineAffinity: Style.Attribute; 1245 | const UnderlineColor: Style.Attribute; 1246 | const UnderlinePattern: Style.Attribute; 1247 | const UnderlineStyle: Style.Attribute; 1248 | } 1249 | 1250 | declare namespace Style { 1251 | class Attribute { 1252 | readonly defaultValue: Object; 1253 | readonly key: string; 1254 | } 1255 | } 1256 | 1257 | // Tag.ChildInsertionLocation 1258 | 1259 | declare namespace Tag { 1260 | class ChildInsertionLocation { 1261 | } 1262 | } 1263 | 1264 | // Tag.Status 1265 | 1266 | declare namespace Tag.Status { 1267 | const Active: Tag.Status; 1268 | const Dropped: Tag.Status; 1269 | const OnHold: Tag.Status; 1270 | const all: Array; 1271 | } 1272 | 1273 | declare namespace Tag { 1274 | class Status { 1275 | } 1276 | } 1277 | 1278 | // Task.ChildInsertionLocation 1279 | 1280 | declare namespace Task { 1281 | class ChildInsertionLocation { 1282 | } 1283 | } 1284 | 1285 | // Task.Notification.Kind 1286 | 1287 | declare namespace Task.Notification.Kind { 1288 | const Absolute: Task.Notification.Kind; 1289 | const DueRelative: Task.Notification.Kind; 1290 | const Unknown: Task.Notification.Kind; 1291 | const all: Array; 1292 | } 1293 | 1294 | declare namespace Task.Notification { 1295 | class Kind { 1296 | } 1297 | } 1298 | 1299 | // Task.RepetitionMethod 1300 | 1301 | declare namespace Task.RepetitionMethod { 1302 | const DeferUntilDate: Task.RepetitionMethod; 1303 | const DueDate: Task.RepetitionMethod; 1304 | const Fixed: Task.RepetitionMethod; 1305 | const None: Task.RepetitionMethod; 1306 | const all: Array; 1307 | } 1308 | 1309 | declare namespace Task { 1310 | class RepetitionMethod { 1311 | } 1312 | } 1313 | 1314 | // Task.RepetitionRule 1315 | 1316 | declare namespace Task { 1317 | class RepetitionRule { 1318 | constructor (ruleString: string, method: Task.RepetitionMethod); 1319 | firstDateAfterDate(date: Date): Date; 1320 | readonly method: Task.RepetitionMethod; 1321 | readonly ruleString: string; 1322 | } 1323 | } 1324 | 1325 | // Task.Status 1326 | 1327 | declare namespace Task.Status { 1328 | const Available: Task.Status; 1329 | const Blocked: Task.Status; 1330 | const Completed: Task.Status; 1331 | const Dropped: Task.Status; 1332 | const DueSoon: Task.Status; 1333 | const Next: Task.Status; 1334 | const Overdue: Task.Status; 1335 | const all: Array; 1336 | } 1337 | 1338 | declare namespace Task { 1339 | class Status { 1340 | } 1341 | } 1342 | 1343 | // Text 1344 | 1345 | declare namespace Text { 1346 | function makeFileAttachment(fileWrapper: FileWrapper, style: Style): Text; 1347 | } 1348 | 1349 | declare class Text { 1350 | constructor (string: string, style: Style); 1351 | textInRange(range: Text.Range): Text; 1352 | styleForRange(range: Text.Range): Style; 1353 | ranges(component: TextComponent, useEnclosingRange: boolean | null): Array; 1354 | replace(range: Text.Range, with_: Text); 1355 | append(text: Text); 1356 | insert(position: Text.Position, text: Text); 1357 | remove(range: Text.Range); 1358 | find(string: string, options: Array | null, range: Text.Range | null): Text.Range | null; 1359 | readonly attachments: Array; 1360 | readonly attributeRuns: Array; 1361 | readonly characters: Array; 1362 | readonly end: Text.Position; 1363 | readonly fileWrapper: FileWrapper | null; 1364 | readonly paragraphs: Array; 1365 | readonly range: Text.Range; 1366 | readonly sentences: Array; 1367 | readonly start: Text.Position; 1368 | string: string; 1369 | readonly style: Style; 1370 | readonly words: Array; 1371 | } 1372 | 1373 | // Text.FindOption 1374 | 1375 | declare namespace Text.FindOption { 1376 | const Anchored: Text.FindOption; 1377 | const Backwards: Text.FindOption; 1378 | const CaseInsensitive: Text.FindOption; 1379 | const DiacriticInsensitive: Text.FindOption; 1380 | const ForcedOrdering: Text.FindOption; 1381 | const Literal: Text.FindOption; 1382 | const Numeric: Text.FindOption; 1383 | const RegularExpression: Text.FindOption; 1384 | const WidthInsensitive: Text.FindOption; 1385 | const all: Array; 1386 | } 1387 | 1388 | declare namespace Text { 1389 | class FindOption { 1390 | } 1391 | } 1392 | 1393 | // Text.Position 1394 | 1395 | declare namespace Text { 1396 | class Position { 1397 | } 1398 | } 1399 | 1400 | // Text.Range 1401 | 1402 | declare namespace Text { 1403 | class Range { 1404 | constructor (start: Text.Position, end: Text.Position); 1405 | readonly end: Text.Position; 1406 | readonly isEmpty: boolean; 1407 | readonly start: Text.Position; 1408 | } 1409 | } 1410 | 1411 | // TextAlignment 1412 | 1413 | declare namespace TextAlignment { 1414 | const Center: TextAlignment; 1415 | const Justified: TextAlignment; 1416 | const Left: TextAlignment; 1417 | const Natural: TextAlignment; 1418 | const Right: TextAlignment; 1419 | const all: Array; 1420 | } 1421 | 1422 | declare class TextAlignment { 1423 | } 1424 | 1425 | // TextComponent 1426 | 1427 | declare namespace TextComponent { 1428 | const Attachments: TextComponent; 1429 | const AttributeRuns: TextComponent; 1430 | const Characters: TextComponent; 1431 | const Paragraphs: TextComponent; 1432 | const Sentences: TextComponent; 1433 | const Words: TextComponent; 1434 | const all: Array; 1435 | } 1436 | 1437 | declare class TextComponent { 1438 | } 1439 | 1440 | // TimeZone 1441 | 1442 | declare namespace TimeZone { 1443 | const abbreviations: Array; 1444 | } 1445 | 1446 | declare class TimeZone { 1447 | constructor (abbreviation: string); 1448 | readonly abbreviation: string | null; 1449 | readonly daylightSavingTime: boolean; 1450 | readonly secondsFromGMT: number; 1451 | } 1452 | 1453 | // Timer 1454 | 1455 | declare namespace Timer { 1456 | function once(interval: number, action: Function): Timer; 1457 | function repeating(interval: number, action: Function): Timer; 1458 | } 1459 | 1460 | declare class Timer { 1461 | cancel(); 1462 | readonly interval: number; 1463 | } 1464 | 1465 | // ToolbarItem 1466 | 1467 | declare class ToolbarItem { 1468 | image: Image | null; 1469 | label: string; 1470 | toolTip: string | null; 1471 | } 1472 | 1473 | // Tree 1474 | 1475 | declare class Tree { 1476 | nodeForObject(object: Object): TreeNode | null; 1477 | nodesForObjects(object: Array): Array; 1478 | reveal(nodes: Array); 1479 | select(nodes: Array, extending: boolean | null); 1480 | copyNodes(nodes: Array, to: Pasteboard); 1481 | paste(from: Pasteboard, parentNode: TreeNode | null, childIndex: number | null); 1482 | readonly rootNode: TreeNode; 1483 | readonly selectedNodes: Array; 1484 | } 1485 | 1486 | // ContentTree 1487 | 1488 | declare class ContentTree extends Tree { 1489 | } 1490 | 1491 | // SidebarTree 1492 | 1493 | declare class SidebarTree extends Tree { 1494 | } 1495 | 1496 | // TreeNode 1497 | 1498 | declare class TreeNode { 1499 | expand(completely: boolean | null); 1500 | collapse(completely: boolean | null); 1501 | expandNote(completely: boolean | null); 1502 | collapseNote(completely: boolean | null); 1503 | reveal(); 1504 | apply(f: Function); 1505 | readonly canCollapse: boolean; 1506 | readonly canExpand: boolean; 1507 | readonly children: Array; 1508 | readonly index: number; 1509 | readonly isExpanded: boolean; 1510 | readonly isNoteExpanded: boolean; 1511 | readonly isRevealed: boolean; 1512 | readonly isRootNode: boolean; 1513 | readonly isSelectable: boolean; 1514 | isSelected: boolean; 1515 | readonly level: number; 1516 | readonly object: Object; 1517 | readonly parent: TreeNode | null; 1518 | readonly rootNode: TreeNode; 1519 | } 1520 | 1521 | // TypeIdentifier 1522 | 1523 | declare namespace TypeIdentifier { 1524 | const URL: TypeIdentifier; 1525 | const binaryPropertyList: TypeIdentifier; 1526 | const csv: TypeIdentifier; 1527 | const editableTypes: Array; 1528 | const gif: TypeIdentifier; 1529 | const image: TypeIdentifier; 1530 | const jpeg: TypeIdentifier; 1531 | const json: TypeIdentifier; 1532 | const ofocus: TypeIdentifier; 1533 | const pdf: TypeIdentifier; 1534 | const plainText: TypeIdentifier; 1535 | const png: TypeIdentifier; 1536 | const propertyList: TypeIdentifier; 1537 | const readableTypes: Array; 1538 | const rtf: TypeIdentifier; 1539 | const rtfd: TypeIdentifier; 1540 | const taskPaper: TypeIdentifier; 1541 | const tasks: TypeIdentifier; 1542 | const tasksAndFolders: TypeIdentifier; 1543 | const tiff: TypeIdentifier; 1544 | const writableTypes: Array; 1545 | const xmlPropertyList: TypeIdentifier; 1546 | } 1547 | 1548 | declare class TypeIdentifier { 1549 | constructor (identifier: string); 1550 | conformsTo(other: TypeIdentifier): boolean; 1551 | readonly displayName: string; 1552 | readonly identifier: string; 1553 | readonly pathExtensions: Array; 1554 | } 1555 | 1556 | // URL 1557 | 1558 | declare namespace URL { 1559 | function choose(types: Array): URL | null; 1560 | function chooseFolder(): URL | null; 1561 | function fromString(string: string): URL | null; 1562 | function tellScript(app: string, js: string, arg: Object | null): URL | null; 1563 | function tellFunction(app: string, jsFunction: Function, arg: Object | null): URL | null; 1564 | const currentAppScheme: string; 1565 | } 1566 | 1567 | declare class URL { 1568 | fetch(success: Function, failure: Function | null); 1569 | call(success: Function, failure: Function | null); 1570 | open(); 1571 | find(types: Array, recurse: boolean | null): Promise>; 1572 | toString(): string; 1573 | appendingPathComponent(component: string): URL; 1574 | deletingLastPathComponent(): URL; 1575 | readonly string: string; 1576 | readonly toObject: Object | null; 1577 | } 1578 | 1579 | // URL.Access 1580 | 1581 | declare namespace URL { 1582 | class Access { 1583 | readonly url: URL; 1584 | } 1585 | } 1586 | 1587 | // URL.Bookmark 1588 | 1589 | declare namespace URL.Bookmark { 1590 | function fromURL(url: URL): URL.Bookmark; 1591 | } 1592 | 1593 | declare namespace URL { 1594 | class Bookmark { 1595 | access(): Promise; 1596 | } 1597 | } 1598 | 1599 | // URL.FetchRequest 1600 | 1601 | declare namespace URL.FetchRequest { 1602 | function fromString(string: string): URL.FetchRequest | null; 1603 | } 1604 | 1605 | declare namespace URL { 1606 | class FetchRequest { 1607 | constructor (); 1608 | fetch(): Promise; 1609 | bodyData: Data | null; 1610 | bodyString: string | null; 1611 | cache: string | null; 1612 | headers: object; 1613 | method: string | null; 1614 | url: URL | null; 1615 | } 1616 | } 1617 | 1618 | // URL.FetchResponse 1619 | 1620 | declare namespace URL { 1621 | class FetchResponse { 1622 | readonly bodyData: Data | null; 1623 | readonly bodyString: string | null; 1624 | readonly headers: object; 1625 | readonly mimeType: string | null; 1626 | readonly statusCode: number; 1627 | readonly textEncodingName: string | null; 1628 | readonly url: URL | null; 1629 | } 1630 | } 1631 | 1632 | // UnderlineAffinity 1633 | 1634 | declare namespace UnderlineAffinity { 1635 | const ByWord: UnderlineAffinity; 1636 | const None: UnderlineAffinity; 1637 | const all: Array; 1638 | } 1639 | 1640 | declare class UnderlineAffinity { 1641 | } 1642 | 1643 | // UnderlinePattern 1644 | 1645 | declare namespace UnderlinePattern { 1646 | const Dash: UnderlinePattern; 1647 | const DashDot: UnderlinePattern; 1648 | const DashDotDot: UnderlinePattern; 1649 | const Dot: UnderlinePattern; 1650 | const Solid: UnderlinePattern; 1651 | const all: Array; 1652 | } 1653 | 1654 | declare class UnderlinePattern { 1655 | } 1656 | 1657 | // UnderlineStyle 1658 | 1659 | declare namespace UnderlineStyle { 1660 | const Double: UnderlineStyle; 1661 | const None: UnderlineStyle; 1662 | const Single: UnderlineStyle; 1663 | const Thick: UnderlineStyle; 1664 | const all: Array; 1665 | } 1666 | 1667 | declare class UnderlineStyle { 1668 | } 1669 | 1670 | // Version 1671 | 1672 | declare class Version { 1673 | constructor (versionString: string); 1674 | equals(version: Version): boolean; 1675 | atLeast(version: Version): boolean; 1676 | isAfter(version: Version): boolean; 1677 | isBefore(version: Version): boolean; 1678 | readonly versionString: string; 1679 | } 1680 | 1681 | // Window 1682 | 1683 | declare class Window { 1684 | close(); 1685 | } 1686 | 1687 | // DocumentWindow 1688 | 1689 | declare class DocumentWindow extends Window { 1690 | selectObjects(objects: Array); 1691 | forecastDayForDate(date: Date): ForecastDay; 1692 | selectForecastDays(days: Array); 1693 | readonly content: ContentTree | null; 1694 | focus: SectionArray | null; 1695 | readonly isTab: boolean; 1696 | perspective: Perspective.BuiltIn | Perspective.Custom | null; 1697 | readonly selection: Selection; 1698 | readonly sidebar: SidebarTree | null; 1699 | readonly tabGroupWindows: Array; 1700 | } 1701 | -------------------------------------------------------------------------------- /Project from Template.draftsAction: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/Project from Template.draftsAction -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ofplugins 2 | 3 | A selection of Omnifocus plugins I use on a regular basis. A work in progress... 4 | 5 | For details of the OmniAutomation API and how to install and use a plugin see the [reference documentation](https://omni-automation.com/omnifocus/index.html). The Omni Slack channel is also a very useful resource. 6 | 7 | ## [Template Plugin](template.omnifocusjs) 8 | 9 | A template project is simply an active or paused project with placeholder variables. It can be used 10 | as a template to create new active projects. In the new project any template variables will have been 11 | replaced with real values. The template project can be on hold to avoid cluttering up your active tasks. 12 | 13 | Select a template project in Project view, run the plugin action and it will: 14 | 15 | - Open a form to ask for values for your variables (if any). 16 | - Duplicate the template to create a new project below the template. 17 | - Replace the variables in the copy with values you provided. 18 | - Make the new project active. 19 | 20 | The plugin provides one action. 21 | 22 | **Select Template**: This action expands the selected template project. 23 | 24 | Variables are fragments of text like: 25 | 26 | - `[[Name]]` 27 | - `[[Phone Number]]` 28 | - `[[Pizza Toppings]]` 29 | - `[[Number Of Widgets]]` 30 | 31 | But they can also have types which the input form can use: 32 | 33 | - `[[The Date:date]]` a date picker defaulting to today. 34 | - `[[Start Time:time]]` a date/time picker defaulting to now. 35 | - `[[Year:date:yyyy]]` a date picker that produces just the year. See [formatting rules](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#formatting-dates). The date/time types are actually the same but with different default formats. 36 | - `[[Something:text]]` a longer form of `[[Something]]`. 37 | - `[[Choose:option:One,Two,Three]]` results in the chosen option. 38 | - `[[Checkbox:checkbox]]` results in Yes or No. 39 | - `[[Checkbox1:checkbox:On,Off]]` results in On or Off. 40 | 41 | Variable replacement occurs both in the name and note of a project and all it's tasks. 42 | 43 | Note that text expansion is performed after internally converting the template 44 | to [OmniFocus TaskPaper format](https://support.omnigroup.com/omnifocus-taskpaper-reference) 45 | 46 | This means that it's possible to add text to task/project titles that will be interpreted by 47 | OmniFocus during TaskPaper processing. For example the @due or @defer directives can be placed in the project/task titles with have relative/absolute dates, and these values will become the due/defer date. 48 | 49 | An example template project in OmniFocus might be: 50 | 51 | ![](2022-10-02-065309.png) 52 | When this project is selected and the action executed, a form will be presented: 53 | 54 | ![](2022-10-02-065739.png) 55 | 56 | After the form is populated and OK is pressed, a new project is created below the template project, with the template values populated: 57 | 58 | ![](2022-10-02-065802.png) 59 | 60 | Limitations: 61 | 62 | If a variable with the same name but multiple differing types or defaults is used in a template 63 | then it will be presented multiple times in the input form and each version will get it's own value 64 | during expansion. 65 | 66 | ## [Toggle Plugin](toggle.omnifocusjs) 67 | 68 | This plugin allows all the projects in a folder to be put on hold or re-activated, toggling from one state to the other. You might, for example want to deactivate a Work folder on a Friday night and reactivate it on a 69 | Monday morning. 70 | 71 | The plugin will descend into the selected folder structure changing active projects to on-hold and vice versa. 72 | 73 | Note: a DEACTIVATED tag is added to active projects when they are put on hold and removed when they are reactivated. This is so the plug can 74 | identify which projects to re-activate by only re-activating ones that it de-activated in the first place. This means that projects that were on-hold when their enclosing folder was de-activated remain on-hold when the folder is reactivated. 75 | 76 | Actions are provided to activate or put on hold the selected folder or to present a choice from the list of all folders. 77 | 78 | ## [Sort Plugin](sort.omnifocusjs) 79 | 80 | This plugin performs a "smart" sort of the tasks in a project accounting for flagged, status, defer and due dates. 81 | 82 | ## [Copy Skip Repeat](copySkipRepeat.omnifocusjs) 83 | 84 | Often I have a repeating task that I want to modify for the current repetition, but not affect the next repetition. 85 | 86 | This plugin takes the selected task (if it's in a project and has a repeat), copies it and removes the repeat on the copy (leaving the original unaffected). 87 | 88 | The original task is completed and the copy can be modified without affecting the original task. 89 | 90 | ## [Sentinel Task](sentinel.omnifocusjs) 91 | 92 | Often I have several tasks, like a shopping list or a bunch of chores, that I want to do today. I don't want to give all of them the Forecast tag, as that would be too much clutter. Instead I want to have a single [sentinel](https://www.dictionary.com/browse/sentinel) task with the Forecast tag that represents them. 93 | 94 | With this plugin, if you select an individual representative task like "Shopping" or the tag itself, it will create a single task where the name is "Shopping", and a note with the url of the shopping tag. The current Forcast tag will also be added. 95 | -------------------------------------------------------------------------------- /copySkipRepeat.omnifocusjs/Resources/copySkipRepeat.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | var action = new PlugIn.Action((selection, sender) => { 3 | 4 | var task = selection.tasks[0]; 5 | 6 | // Clear selection? 7 | document.windows[0].selectObjects([]); 8 | 9 | // Create new task 10 | var taskCopy = duplicateTasks([task], task.containingProject)[0]; 11 | taskCopy.name = taskCopy.name + ' (copy)'; 12 | taskCopy.repetitionRule = null; 13 | 14 | // Complete the current one 15 | task.markComplete(); 16 | 17 | document.windows[0].selectObjects([taskCopy]); 18 | }); 19 | 20 | action.validate = (selection, sender) => { 21 | return selection.tasks 22 | && selection.tasks.length == 1 23 | && selection.tasks[0].repetitionRule != null 24 | && selection.tasks[0].containingProject != null 25 | }; 26 | 27 | return action; 28 | })(); 29 | _; 30 | -------------------------------------------------------------------------------- /copySkipRepeat.omnifocusjs/Resources/en.lproj/copySkipRepeat.strings: -------------------------------------------------------------------------------- 1 | "label" = "Copy and Skip Repeat: Selected Task"; 2 | "shortLabel" = "Copy and Skip Repeat: Selected Task"; 3 | "mediumLabel" = "Copy and Skip Repeat: Selected Task"; 4 | "longLabel" = "Copy and Skip Repeat: Selected Task"; 5 | -------------------------------------------------------------------------------- /copySkipRepeat.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.CopySkipRepeat" = "Copy Skip Repeat"; 2 | -------------------------------------------------------------------------------- /copySkipRepeat.omnifocusjs/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/copySkipRepeat.omnifocusjs/Resources/icon.png -------------------------------------------------------------------------------- /copySkipRepeat.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.CopySkipRepeat", 4 | "author": "Paul Sidnell", 5 | "description": "Duplicate task and Skip", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"icon.png", "identifier": "copySkipRepeat" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /draftstemplate.omnifocusjs/Resources/draftstemplate.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | const action = new PlugIn.Action(async function(selection, sender){ 4 | console.log('Action running'); 5 | 6 | // Should contain com.omnigroup.omnifocus2.export-filetype.plain-text 7 | // But doesn't in OF4 on iOS 8 | var types = document.writableTypes.map(type => { 9 | return "\"" + type + "\"" 10 | }) 11 | types = "[" + types.join(",\n") + "]" 12 | console.log("Supported file types: " + types); 13 | 14 | if (app.userVersion.versionString.startsWith("3.")) { 15 | const fileTypeID = "com.omnigroup.omnifocus2.export-filetype.plain-text" 16 | const baseName = "Tasky-Export" 17 | const wrapper = await document.makeFileWrapper(baseName, fileTypeID); 18 | const taskPaper = wrapper.contents.toString() 19 | const urlStr = "drafts://x-callback-url/" + 20 | "create" + 21 | "?action=Project%20from%20Template" + 22 | "&text=" + encodeURIComponent(taskPaper); 23 | const scriptURL = URL.fromString(urlStr); 24 | console.log(scriptURL); 25 | scriptURL.open() 26 | } else { 27 | // In OF4 must copy the project as TaskPaper to the clipboard 28 | // ourselves and use special Drafts syntax to take that as input. 29 | const taskPaper = '||clipboard||'; 30 | const urlStr = "drafts://x-callback-url/" + 31 | "create" + 32 | "?action=Project%20from%20Template" + 33 | "&text=" + encodeURIComponent(taskPaper); 34 | const scriptURL = URL.fromString(urlStr); 35 | console.log(scriptURL); 36 | scriptURL.open(); 37 | } 38 | }); 39 | 40 | action.validate = function(selection, sender){ 41 | return (selection.projects.length === 1 && 42 | selection.folders.length == 0); 43 | }; 44 | 45 | return action; 46 | })(); 47 | _; 48 | -------------------------------------------------------------------------------- /draftstemplate.omnifocusjs/Resources/draftstemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/draftstemplate.omnifocusjs/Resources/draftstemplate.png -------------------------------------------------------------------------------- /draftstemplate.omnifocusjs/Resources/en.lproj/draftstemplate.strings: -------------------------------------------------------------------------------- 1 | "label" = "Expand Template With Drafts"; 2 | "shortLabel" = "Expand Template With Drafts"; 3 | "mediumLabel" = "Expand Template With Drafts"; 4 | "longLabel" = "Expand Template With Drafts"; 5 | -------------------------------------------------------------------------------- /draftstemplate.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.Sort" = "Drafts Template"; 2 | -------------------------------------------------------------------------------- /draftstemplate.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.DraftsTemplate", 4 | "author": "Paul Sidnell", 5 | "description": "Expand Template With Drafts", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"draftstemplate.png", "identifier": "draftstemplate" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /duration.omnifocusjs/Resources/duration.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | var action = new PlugIn.Action(async function(selection, sender) { 4 | var totalMinutes = 0; 5 | var tasksWithDuration = 0; 6 | var tasksWithoutDuration = 0; 7 | flattenedTasks.forEach(task => { 8 | if (task.taskStatus != Task.Status.Completed && task.taskStatus != Task.Status.Dropped && 9 | task.taskStatus != Task.Status.Blocked && task.taskStatus != Task.Status.Blocked) { 10 | if (task.estimatedMinutes) { 11 | totalMinutes += task.estimatedMinutes; 12 | tasksWithDuration++; 13 | console.log(task.name + " " + task.estimatedMinutes); 14 | } else { 15 | tasksWithoutDuration++; 16 | } 17 | } 18 | }); 19 | 20 | var hours = Math.floor(totalMinutes / 60); 21 | var minutes = totalMinutes % (hours * 60); 22 | var duration = "" + hours + "h " + minutes + "m"; 23 | console.log(totalMinutes); 24 | 25 | var inputForm = new Form(); 26 | inputForm.addField(new Form.Field.String("f1", "Duration", "" + duration), 0) 27 | inputForm.addField(new Form.Field.String("f2", "Tasks With Duration", "" + tasksWithDuration), 1) 28 | inputForm.addField(new Form.Field.String("f2", "Tasks Without Duration", "" + tasksWithoutDuration), 2) 29 | inputForm.show("Duration Summary", "OK").then( 30 | (form) => {}, 31 | (error) => console.log(error)); 32 | }); 33 | 34 | action.validate = function(selection, sender){ 35 | return true; 36 | }; 37 | 38 | return action; 39 | })(); 40 | _; 41 | -------------------------------------------------------------------------------- /duration.omnifocusjs/Resources/duration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/duration.omnifocusjs/Resources/duration.png -------------------------------------------------------------------------------- /duration.omnifocusjs/Resources/en.lproj/duration.strings: -------------------------------------------------------------------------------- 1 | "label" = "Calculate Duration"; 2 | "shortLabel" = "Calculate Duration"; 3 | "mediumLabel" = "Calculate Duration"; 4 | "longLabel" = "Calculate Duration"; 5 | -------------------------------------------------------------------------------- /duration.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.Duration" = "Calculate Duration"; 2 | -------------------------------------------------------------------------------- /duration.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.Duration", 4 | "author": "Paul Sidnell", 5 | "description": "Calculate Duration", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"duration.png", "identifier": "duration" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /focus-home.omnifocusjs/Resources/en.lproj/focus.strings: -------------------------------------------------------------------------------- 1 | "label" = "Focus on Home"; 2 | "shortLabel" = "Focus on Home"; 3 | "mediumLabel" = "Focus on Home"; 4 | "longLabel" = "Focus on Home"; 5 | -------------------------------------------------------------------------------- /focus-home.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.FocusHome" = "Focus on Home"; 2 | -------------------------------------------------------------------------------- /focus-home.omnifocusjs/Resources/focus.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | var action = new PlugIn.Action(async function(selection, sender){ 4 | 5 | const SEARCH_FOR = "home"; 6 | 7 | var startingPerspective = document.windows[0].perspective; 8 | 9 | var matches = []; 10 | flattenedFolders.forEach(folder => { 11 | if ((!folder.parent) && 12 | folder.status == Folder.Status.Active && 13 | (folder.name.toLowerCase().indexOf(SEARCH_FOR) != -1 || folder.name.toLowerCase().indexOf("misc") != -1)) { 14 | matches.push(folder); 15 | // console.log("Matched Folder " + folder.name); 16 | } 17 | }); 18 | 19 | flattenedProjects.forEach(project => { 20 | if ((!project.parentFolder) && 21 | project.status != Project.Status.Dropped && 22 | project.status != Project.Status.Done && 23 | (project.name.toLowerCase().indexOf(SEARCH_FOR) != -1 || project.name.toLowerCase().indexOf("misc") != -1)) { 24 | matches.push(project); 25 | // console.log("Matched Project " + project.name); 26 | } 27 | }); 28 | 29 | var win = document.windows[0] 30 | win.perspective = Perspective.BuiltIn.Projects 31 | win.focus = matches; 32 | 33 | // Restore original perspective 34 | 35 | // Crashes on the phone: 36 | // document.windows[0].perspective = startingPerspective; 37 | var delay = Device.current.mac ? 0 : 2; 38 | Timer.once(delay, () => document.windows[0].perspective = startingPerspective); 39 | }); 40 | 41 | action.validate = function(selection, sender){ 42 | return true; 43 | }; 44 | 45 | return action; 46 | })(); 47 | _; 48 | -------------------------------------------------------------------------------- /focus-home.omnifocusjs/Resources/focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/focus-home.omnifocusjs/Resources/focus.png -------------------------------------------------------------------------------- /focus-home.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.FocusHome", 4 | "author": "Paul Sidnell", 5 | "description": "Focus on Home Projects", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"focus.png", "identifier": "focus" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /focus-not-alarms.omnifocusjs/Resources/en.lproj/focus.strings: -------------------------------------------------------------------------------- 1 | "label" = "Focus Not Alarms"; 2 | "shortLabel" = "Focus Not Alarms"; 3 | "mediumLabel" = "Focus Not Alarms"; 4 | "longLabel" = "Focus Not Alarms"; 5 | -------------------------------------------------------------------------------- /focus-not-alarms.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.FocusNotAlarms" = "Focus Not Alarms"; 2 | -------------------------------------------------------------------------------- /focus-not-alarms.omnifocusjs/Resources/focus.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | var action = new PlugIn.Action(async function(selection, sender){ 4 | 5 | const SEARCH_FOR = "alarms"; 6 | 7 | var startingPerspective = document.windows[0].perspective; 8 | 9 | var matches = []; 10 | flattenedFolders.forEach(folder => { 11 | if ((!folder.parent) && 12 | folder.status == Folder.Status.Active && 13 | (folder.name.toLowerCase().indexOf(SEARCH_FOR) == -1 || folder.name.toLowerCase().indexOf("misc") != -1)) { 14 | matches.push(folder); 15 | // console.log("Matched Folder " + folder.name); 16 | } 17 | }); 18 | 19 | flattenedProjects.forEach(project => { 20 | if ((!project.parentFolder) && 21 | project.status != Project.Status.Dropped && 22 | project.status != Project.Status.Done && 23 | (project.name.toLowerCase().indexOf(SEARCH_FOR) == -1 || project.name.toLowerCase().indexOf("misc") != -1)) { 24 | matches.push(project); 25 | // console.log("Matched Project " + project.name); 26 | } 27 | }); 28 | 29 | var win = document.windows[0] 30 | win.perspective = Perspective.BuiltIn.Projects 31 | win.focus = matches; 32 | 33 | // Restore original perspective 34 | 35 | // Crashes on the phone: 36 | // document.windows[0].perspective = startingPerspective; 37 | var delay = Device.current.mac ? 0 : 2; 38 | Timer.once(delay, () => document.windows[0].perspective = startingPerspective); 39 | }); 40 | 41 | action.validate = function(selection, sender){ 42 | return true; 43 | }; 44 | 45 | return action; 46 | })(); 47 | _; 48 | -------------------------------------------------------------------------------- /focus-not-alarms.omnifocusjs/Resources/focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/focus-not-alarms.omnifocusjs/Resources/focus.png -------------------------------------------------------------------------------- /focus-not-alarms.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.FocusNotAlarms", 4 | "author": "Paul Sidnell", 5 | "description": "Focus on Not Alarms", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"focus.png", "identifier": "focus" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /focus-work.omnifocusjs/Resources/en.lproj/focus.strings: -------------------------------------------------------------------------------- 1 | "label" = "Focus on Work"; 2 | "shortLabel" = "Focus on Work"; 3 | "mediumLabel" = "Focus on Work"; 4 | "longLabel" = "Focus on Work"; 5 | -------------------------------------------------------------------------------- /focus-work.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.FocusWork" = "Focus on Work"; 2 | -------------------------------------------------------------------------------- /focus-work.omnifocusjs/Resources/focus.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | var action = new PlugIn.Action(async function(selection, sender){ 4 | 5 | const SEARCH_FOR = "work"; 6 | 7 | var startingPerspective = document.windows[0].perspective; 8 | 9 | var matches = []; 10 | flattenedFolders.forEach(folder => { 11 | if ((!folder.parent) && 12 | folder.status == Folder.Status.Active && 13 | (folder.name.toLowerCase().indexOf(SEARCH_FOR) != -1 || folder.name.toLowerCase().indexOf("misc") != -1)) { 14 | matches.push(folder); 15 | // console.log("Matched Folder " + folder.name); 16 | } 17 | }); 18 | 19 | flattenedProjects.forEach(project => { 20 | if ((!project.parentFolder) && 21 | project.status != Project.Status.Dropped && 22 | project.status != Project.Status.Done && 23 | (project.name.toLowerCase().indexOf(SEARCH_FOR) != -1 || project.name.toLowerCase().indexOf("misc") != -1)) { 24 | matches.push(project); 25 | // console.log("Matched Project " + project.name); 26 | } 27 | }); 28 | 29 | var win = document.windows[0] 30 | win.perspective = Perspective.BuiltIn.Projects 31 | win.focus = matches; 32 | 33 | // Restore original perspective 34 | 35 | // Crashes on the phone: 36 | // document.windows[0].perspective = startingPerspective; 37 | var delay = Device.current.mac ? 0 : 2; 38 | Timer.once(delay, () => document.windows[0].perspective = startingPerspective); 39 | }); 40 | 41 | action.validate = function(selection, sender){ 42 | return true; 43 | }; 44 | 45 | return action; 46 | })(); 47 | _; 48 | -------------------------------------------------------------------------------- /focus-work.omnifocusjs/Resources/focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/focus-work.omnifocusjs/Resources/focus.png -------------------------------------------------------------------------------- /focus-work.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.FocusWork", 4 | "author": "Paul Sidnell", 5 | "description": "Focus on Work Projects", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"focus.png", "identifier": "focus" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /repeatTag.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.RepeatTag" = "Apply Repeat Tag"; 2 | -------------------------------------------------------------------------------- /repeatTag.omnifocusjs/Resources/en.lproj/repeat.strings: -------------------------------------------------------------------------------- 1 | "label" = "Apply Repeat Tag Globally"; 2 | "shortLabel" = "Apply Repeat Tag Globally"; 3 | "mediumLabel" = "Apply Repeat Tag Globally"; 4 | "longLabel" = "Apply Repeat Tag Globally"; 5 | -------------------------------------------------------------------------------- /repeatTag.omnifocusjs/Resources/repeat.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | const tagName = "♻️"; 4 | 5 | var applyTag = (taskOrProject, repeatTag) => { 6 | if (taskOrProject.repetitionRule) { 7 | if (!taskOrProject.tags.includes(repeatTag)) { 8 | console.log("Adding to: " + taskOrProject.name); 9 | taskOrProject.addTag(repeatTag); 10 | } 11 | } else { 12 | if (taskOrProject.tags.includes(repeatTag)) { 13 | console.log("Removing from: " + taskOrProject.name); 14 | taskOrProject.removeTag(repeatTag); 15 | } 16 | } 17 | } 18 | 19 | var applyToTask = (task, repeatTag) => { 20 | if (task.taskStatus != Task.Status.Completed && task.taskStatus != Task.Status.Dropped) { 21 | //console.log("Task: " + task.name); 22 | applyTag(task, repeatTag); 23 | } 24 | } 25 | 26 | var applyToProject = (project, repeatTag) => { 27 | if (project.taskStatus != Task.Status.Completed && project.taskStatus != Task.Status.Dropped) { 28 | //console.log("Project: " + project.name); 29 | applyTag(project, repeatTag); 30 | project.flattenedTasks.forEach(task => { 31 | applyToTask(task, repeatTag); 32 | }); 33 | } 34 | } 35 | 36 | var applyToFolder = (folder, repeatTag) => { 37 | if (folder.status != Folder.Status.Dropped) { 38 | //console.log("Folder: " + folder.name); 39 | folder.flattenedProjects.forEach(project => { 40 | applyToProject(project, repeatTag); 41 | }); 42 | } 43 | } 44 | 45 | var action = new PlugIn.Action(function(selection, sender){ 46 | var repeatTag = flattenedTags.byName(tagName) || new Tag(tagName, tags.end); 47 | 48 | selection.folders.forEach(folder => applyToFolder(folder, repeatTag)); 49 | selection.projects.forEach(project => applyToProject(project, repeatTag)); 50 | selection.tasks.forEach(task => applyToTask(task, repeatTag)); 51 | 52 | console.log("Applied " + tagName); 53 | }); 54 | 55 | action.validate = function(selection, sender){ 56 | return (selection.folders.length > 0) || 57 | (selection.projects.length > 0) || 58 | (selection.tasks.length > 0); 59 | }; 60 | 61 | return action; 62 | })(); 63 | _; 64 | -------------------------------------------------------------------------------- /repeatTag.omnifocusjs/Resources/repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/repeatTag.omnifocusjs/Resources/repeat.png -------------------------------------------------------------------------------- /repeatTag.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.RepeatTag", 4 | "author": "Paul Sidnell", 5 | "description": "Apply Repeat Tag", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"repeat.png", "identifier": "repeat" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /sentinel.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.Sentinel" = "Sentinel"; 2 | -------------------------------------------------------------------------------- /sentinel.omnifocusjs/Resources/en.lproj/selectionSentinel.strings: -------------------------------------------------------------------------------- 1 | "label" = "Create Sentinal Task"; 2 | "shortLabel" = "Create Sentinal Task"; 3 | "mediumLabel" = "Create Sentinal Task"; 4 | "longLabel" = "Create Sentinal Task"; 5 | -------------------------------------------------------------------------------- /sentinel.omnifocusjs/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/sentinel.omnifocusjs/Resources/icon.png -------------------------------------------------------------------------------- /sentinel.omnifocusjs/Resources/selectionSentinel.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | var action = new PlugIn.Action((selection, sender) => { 3 | 4 | var tags = []; 5 | 6 | // Tags from any selected tasks 7 | selection.tasks.forEach(task => { 8 | task.tags.forEach(tag => { 9 | if (!tags.includes(tag)) { 10 | tags.push(tag); 11 | } 12 | }) 13 | }); 14 | 15 | // Any selected tags 16 | selection.tags.forEach(tag => { 17 | if (!tags.includes(tag)) { 18 | tags.push(tag); 19 | } 20 | }); 21 | 22 | // Add sentinel tag 23 | //var sentinalTag = flattenedTags.byName("➡️") || new Tag("➡️"); 24 | //tags.push(sentinalTag); 25 | 26 | var tagNameList = []; 27 | tags.forEach((tag)=>{ 28 | tagNameList.push(tag.name); 29 | }); 30 | var title = '## ' + tagNameList.join(', '); 31 | 32 | var noteLines = []; 33 | tags.forEach((tag)=>{ 34 | noteLines.push('omnifocus:///tag/' + encodeURIComponent(tag.name)); 35 | }); 36 | var note = noteLines.join('\n'); 37 | 38 | var task = new Task(title, inbox.beginning); 39 | task.addTag(Tag.forecastTag); 40 | task.flagged = true; 41 | tags.forEach(tag => { 42 | task.addTag(tag); 43 | }); 44 | task.note = note; 45 | }); 46 | 47 | action.validate = (selection, sender) => { 48 | return selection.tasks.length >= 1 || selection.tags.length >= 1; 49 | }; 50 | 51 | return action; 52 | })(); 53 | _; 54 | -------------------------------------------------------------------------------- /sentinel.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.Sentinel", 4 | "author": "Paul Sidnell", 5 | "description": "Create Sentinal Task from Selection", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"icon.png", "identifier": "selectionSentinel" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /sort.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.Sort" = "Sort Tasks"; 2 | -------------------------------------------------------------------------------- /sort.omnifocusjs/Resources/en.lproj/sort.strings: -------------------------------------------------------------------------------- 1 | "label" = "Sort Project Tasks by Due/Defer"; 2 | "shortLabel" = "Sort Project Tasks by Due/Defer"; 3 | "mediumLabel" = "Sort Project Tasks by Due/Defer"; 4 | "longLabel" = "Sort Project Tasks by Due/Defer"; 5 | -------------------------------------------------------------------------------- /sort.omnifocusjs/Resources/sort.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | var smartTime = (item, now) => { 4 | var time; 5 | if (item.dueDate) { 6 | time = item.dueDate.getTime() 7 | } else if (item.deferDate) { 8 | time = item.deferDate.getTime(); 9 | } else { 10 | time = now.getTime(); 11 | } 12 | // console.log ('smartTime ' + item.name + ' ' + new Date(time) + ' = ' + time); 13 | return time; 14 | } 15 | 16 | var rank = (item) => { 17 | /* Statuses to consider 18 | Available (Task.Status r/o) • The task is available to work on. 19 | Blocked (Task.Status r/o) • The task is not available to work on currently, due to a future defer date, a preceeding task in a sequential project, or having an on-hold tag associated. 20 | Completed (Task.Status r/o) • The task is already completed. 21 | Dropped (Task.Status r/o) • The task will not be worked on. 22 | DueSoon (Task.Status r/o) • The task is incomplete and due soon. 23 | Next (Task.Status r/o) • The task is the first available task in a project. 24 | Overdue (Task.Status r/o) • The task is incomplete overdue. 25 | */ 26 | var rank = 0; 27 | var status = item.taskStatus; 28 | switch (status) { 29 | case Task.Status.Overdue: 30 | case Task.Status.DueSoon: 31 | case Task.Status.Available: 32 | case Task.Status.Next: 33 | rank += item.flagged ? Math.pow(10, 4) : 0; 34 | break; 35 | } 36 | 37 | rank += status === Task.Status.Overdue ? Math.pow(10, 3): 0; 38 | rank += status === Task.Status.DueSoon ? Math.pow(10, 2) : 0; 39 | rank += status === Task.Status.Available ? Math.pow(10, 1) : 0; 40 | rank += status === Task.Status.Next ? Math.pow(10, 0) : 0; 41 | // console.log('rank ' + item.name + ' ' + item.taskStatus + ' f=' + item.flagged + ' = ' + rank); 42 | return rank; 43 | } 44 | 45 | var compare = (left, right) => { 46 | const now = new Date(); 47 | // Compare on rank 48 | const leftRank = rank(left); 49 | const rightRank = rank (right); 50 | if (leftRank != rightRank) { 51 | return rightRank - leftRank; 52 | } 53 | 54 | // Compare on smart times 55 | const leftSmartTime = smartTime(left, now); 56 | const rightSmartTime = smartTime(right, now); 57 | if (leftSmartTime !== rightSmartTime) { 58 | return leftSmartTime - rightSmartTime; 59 | } 60 | 61 | // Compare on name 62 | var cmp = left.name.localeCompare(right.name); 63 | if (cmp != 0) { 64 | return cmp; 65 | }; 66 | 67 | // Compare on id 68 | return left.id.primaryKey.localeCompare(right.id.primaryKey); 69 | }; 70 | 71 | var action = new PlugIn.Action(function(selection, sender){ 72 | project = selection.projects[0]; 73 | var tasks = []; 74 | for (var i = 0; i < project.task.children.length; i++) { 75 | tasks.push(project.task.children[i]); 76 | } 77 | tasks.sort(compare); 78 | moveTasks(tasks, project); 79 | }); 80 | 81 | action.validate = function(selection, sender){ 82 | return (selection.projects.length === 1) 83 | }; 84 | 85 | return action; 86 | })(); 87 | _; 88 | -------------------------------------------------------------------------------- /sort.omnifocusjs/Resources/sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/sort.omnifocusjs/Resources/sort.png -------------------------------------------------------------------------------- /sort.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.Sort", 4 | "author": "Paul Sidnell", 5 | "description": "Sort Project Tasks", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"sort.png", "identifier": "sort" } 9 | ], 10 | "libraries": [ 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /template.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.Template" = "Expand Template"; 2 | -------------------------------------------------------------------------------- /template.omnifocusjs/Resources/en.lproj/selection.strings: -------------------------------------------------------------------------------- 1 | "label" = "Expand: Selected Template"; 2 | "shortLabel" = "Expand: Selected Template"; 3 | "mediumLabel" = "Expand: Selected Template"; 4 | "longLabel" = "Expand: Selected Template"; 5 | -------------------------------------------------------------------------------- /template.omnifocusjs/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/template.omnifocusjs/Resources/icon.png -------------------------------------------------------------------------------- /template.omnifocusjs/Resources/lib.js: -------------------------------------------------------------------------------- 1 | var _ = function() { 2 | var lib = new PlugIn.Library(new Version("0.1")); 3 | 4 | // Find variables in text (thanks to Thomas Kern) 5 | lib.findVariables = (text) => { 6 | var variablesFound = []; 7 | substrings = text.match(/\[\[[^\]]*\]\]/gm); 8 | if (substrings != null) { 9 | for (var i = 0; i < substrings.length; i++) { 10 | var variableSpec = substrings[i] 11 | .replaceAll('[[', '') 12 | .replaceAll(']]', ''); 13 | if (!variablesFound.includes(variableSpec)) { 14 | // console.log("Found variable " + variableSpec); 15 | variablesFound.push(variableSpec); 16 | } 17 | } 18 | } 19 | return variablesFound; 20 | }; 21 | 22 | // Replace variables in the string with those from the form 23 | lib.replaceVariables = (text, templateVariables, variableValues) => { 24 | var result = text; 25 | for (var i = 0; i < templateVariables.length; i++) { 26 | var variableSpec = templateVariables[i]; 27 | var parts = variableSpec.split(':'); 28 | var variableName = parts[0]; 29 | var variableType = parts.length > 0 ? parts[1] : 'text'; 30 | var options = parts.length > 1 ? parts[2] : null; 31 | var value = variableValues[variableSpec]; 32 | switch (variableType) { 33 | case 'date' : { 34 | var formatStr = options == null ? 'yyyy-MM-dd' : options; 35 | var format = Formatter.Date.withFormat(formatStr); 36 | value = format.stringFromDate(value); 37 | break; 38 | } 39 | case 'time' : { 40 | var formatStr = options == null ? 'yyyy-MM-dd HH:mm' : options; 41 | var format = Formatter.Date.withFormat(formatStr); 42 | value = format.stringFromDate(value); 43 | break; 44 | } 45 | case 'checkbox' : { 46 | var choices = (options != null && options.indexOf(',') != -1) ? 47 | options.split(',') : ["Yes", "No"]; 48 | value = value ? choices[0] : choices[1]; 49 | break; 50 | } 51 | default: 52 | break; 53 | } 54 | console.log("replacing " + variableSpec + " with " + value); 55 | result = result.replaceAll("[[" + variableSpec + "]]", value); 56 | } 57 | return result; 58 | }; 59 | 60 | lib.buildFolderPath = (folder) => { 61 | var names = []; 62 | while (folder != null) { 63 | names.unshift(folder.name); 64 | folder = folder.parent; 65 | } 66 | return names.length == 0 ? 'projects' : '/folder/' + names.join(':'); 67 | } 68 | 69 | // Process the template 70 | lib.expandTaskPaper = (template, taskPaper, templateVariables, variableValues) => { 71 | // console.log('Processing Template ' + template.name); 72 | var expandedTaskPaper = lib.replaceVariables(taskPaper, templateVariables, variableValues); 73 | // console.log(expandedTaskPaper); 74 | 75 | var path = lib.buildFolderPath(template.parentFolder); 76 | 77 | encodeURIComponent(template.assignedContainer); 78 | var urlStr = "omnifocus:///paste" + 79 | "?target=" + encodeURIComponent(path) + 80 | "&content=" + encodeURIComponent(expandedTaskPaper) 81 | // console.log(urlStr); 82 | // Create and open the new project 83 | var url = URL.fromString(urlStr); 84 | url.call(reply => {}); 85 | }; 86 | 87 | lib.addFormField = (inputForm, variableSpec, position) => { 88 | var parts = variableSpec.split(':'); 89 | var variableName = parts[0]; 90 | var variableType = parts.length > 0 ? parts[1] : 'text'; 91 | var options = parts.length > 1 ? parts[2] : null; 92 | var now = new Date(); 93 | // https://unicode-org.github.io/icu/userguide/format_parse/datetime/#formatting-dates 94 | switch (variableType) { 95 | case 'date' : { 96 | var formatStr = options == null ? 'yyyy-MM-dd' : options; 97 | var format = Formatter.Date.withFormat(formatStr); 98 | inputForm.addField(new Form.Field.Date(variableSpec, variableName, new Date(), format), position); 99 | break; 100 | } 101 | case 'time' : { 102 | var formatStr = options == null ? 'yyyy-MM-dd HH:mm' : options; 103 | var format = Formatter.Date.withFormat(formatStr); 104 | inputForm.addField(new Form.Field.Date(variableSpec, variableName, new Date(), format), position); 105 | break; 106 | } 107 | case 'option' : { 108 | var choices = options != null ? options.split(',') : [""]; 109 | inputForm.addField(new Form.Field.Option(variableSpec, variableName, choices, choices, choices[0]), position); 110 | break; 111 | } 112 | case 'checkbox' : { 113 | inputForm.addField(new Form.Field.Checkbox(variableSpec, variableName, null), position); 114 | break; 115 | } 116 | case 'text': 117 | default: 118 | var value = options; 119 | inputForm.addField(new Form.Field.String(variableSpec, variableName, value), position); 120 | break; 121 | } 122 | } 123 | 124 | 125 | lib.expand = (template, taskPaper) => { 126 | var templateVariables = lib.findVariables(taskPaper); 127 | 128 | if (templateVariables.length == 0) { 129 | lib.expandTaskPaper(template, taskPaper, templateVariables, new Object()) 130 | } else { 131 | // Open a form to collect values for variables in the template 132 | var inputForm = new Form(); 133 | for (var i = 0; i < templateVariables.length; i++) { 134 | var variable = templateVariables[i]; 135 | lib.addFormField(inputForm, variable, i); 136 | } 137 | var formPrompt = template.name; 138 | var buttonTitle = "OK"; 139 | inputForm.show(formPrompt, buttonTitle).then( 140 | (form) => lib.expandTaskPaper(template, taskPaper, templateVariables, form.values), 141 | (error) => console.log(error)); 142 | } 143 | }; 144 | 145 | return lib; 146 | }(); 147 | _; 148 | -------------------------------------------------------------------------------- /template.omnifocusjs/Resources/selection.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | 3 | const action = new PlugIn.Action(async function(selection, sender){ 4 | var template = selection.projects[0]; 5 | 6 | // Open the template project exclusively in project view to avoid 7 | // issues collecting the TaskPaper 8 | var win = document.windows[0] 9 | win.perspective = Perspective.BuiltIn.Projects 10 | win.focus = [template]; 11 | 12 | // Should contain com.omnigroup.omnifocus2.export-filetype.plain-text 13 | // But doesn't in OF4 on iOS 14 | var types = document.writableTypes.map(type => { 15 | return "\"" + type + "\"" 16 | }) 17 | types = "[" + types.join(",\n") + "]"; 18 | // console.log("Supported file types: " + types); 19 | 20 | // Where supported fetch the taskpaper of the selected project 21 | // otherwise (e.g. the test flight of OF4 on iOS) you must copy 22 | // the project as taskpsper 23 | var taskPaper; 24 | if (types.indexOf("com.omnigroup.omnifocus2.export-filetype.plain-text") != -1) { 25 | const fileTypeID = "com.omnigroup.omnifocus2.export-filetype.plain-text" 26 | const baseName = "Tasky-Export" 27 | const wrapper = await document.makeFileWrapper(baseName, fileTypeID); 28 | taskPaper = wrapper.contents.toString(); 29 | } else { 30 | taskPaper = Pasteboard.general.string; 31 | } 32 | 33 | // Unfocus again 34 | win.focus = []; 35 | 36 | var lib = PlugIn.find("com.PaulSidnell.Template").library("lib"); 37 | 38 | lib.expand(template, taskPaper); 39 | }); 40 | 41 | action.validate = (selection, sender) => { 42 | return selection.projects.length === 1 && 43 | (selection.projects[0].status === Project.Status.OnHold || 44 | selection.projects[0].status === Project.Status.Active); 45 | //&& selection.projects[0].name.indexOf('[[') != -1; 46 | }; 47 | 48 | return action; 49 | })(); 50 | _; 51 | -------------------------------------------------------------------------------- /template.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.Template", 4 | "author": "Paul Sidnell", 5 | "description": "Expand a Template Project", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"icon.png", "identifier": "selection" } 9 | ], 10 | "libraries": [ 11 | {"identifier": "lib"} 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/chooseActivate.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | var action = new PlugIn.Action((selection, sender) => { 3 | 4 | var toggleLib = PlugIn.find('com.PaulSidnell.Toggle').library('toggleLib'); 5 | 6 | var inputForm = new Form(); 7 | 8 | // Find folders 9 | var folders = []; 10 | var names = []; 11 | library.apply(item => { 12 | if (item instanceof Folder && item.active) { 13 | folders.push(item); 14 | names.push(item.name); 15 | } 16 | }); 17 | 18 | if (folders.length > 0) { 19 | // Choose which folder to toggle 20 | inputForm.addField(new Form.Field.Option( 21 | 'choice', 22 | 'Folder', 23 | folders, 24 | names, 25 | folders[0] 26 | )); 27 | 28 | var formPrompt = 'Choose a folder'; 29 | var buttonTitle = 'OK'; 30 | inputForm.show(formPrompt, buttonTitle).then( 31 | (form) => toggleLib.toggleFolder(form.values['choice'], true), 32 | (error) => console.log(error)); 33 | } 34 | }); 35 | 36 | action.validate = (selection, sender) => { 37 | return true; 38 | }; 39 | 40 | return action; 41 | })(); 42 | _; 43 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/chooseOnHold.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | var action = new PlugIn.Action((selection, sender) => { 3 | 4 | var toggleLib = PlugIn.find('com.PaulSidnell.Toggle').library('toggleLib'); 5 | 6 | var inputForm = new Form(); 7 | 8 | // Find folders 9 | var folders = []; 10 | var names = []; 11 | library.apply(item => { 12 | if (item instanceof Folder && item.active) { 13 | folders.push(item); 14 | names.push(item.name); 15 | } 16 | }); 17 | 18 | if (folders.length > 0) { 19 | // Choose which folder to toggle 20 | inputForm.addField(new Form.Field.Option( 21 | 'choice', 22 | 'Folder', 23 | folders, 24 | names, 25 | folders[0] 26 | )); 27 | 28 | var formPrompt = 'Choose a folder'; 29 | var buttonTitle = 'OK'; 30 | inputForm.show(formPrompt, buttonTitle).then( 31 | (form) => toggleLib.toggleFolder(form.values['choice'], false), 32 | (error) => console.log(error)); 33 | } 34 | }); 35 | 36 | action.validate = (selection, sender) => { 37 | return true; 38 | }; 39 | 40 | return action; 41 | })(); 42 | _; 43 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/en.lproj/chooseActivate.strings: -------------------------------------------------------------------------------- 1 | "label" = "Activate: Choose Folder..."; 2 | "shortLabel" = "Activate: Choose Folder..."; 3 | "mediumLabel" = "Activate: Choose Folder..."; 4 | "longLabel" = "Activate: Choose Folder..."; 5 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/en.lproj/chooseOnHold.strings: -------------------------------------------------------------------------------- 1 | "label" = "On Hold: Choose Folder..."; 2 | "shortLabel" = "On Hold: Choose Folder..."; 3 | "mediumLabel" = "On Hold: Choose Folder..."; 4 | "longLabel" = "On Hold: Choose Folder..."; 5 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/en.lproj/manifest.strings: -------------------------------------------------------------------------------- 1 | "com.PaulSidnell.Toggle" = "Toggle Folder"; 2 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/en.lproj/selectionActivate.strings: -------------------------------------------------------------------------------- 1 | "label" = "Activate: Selected Folder"; 2 | "shortLabel" = "Activate: Selected Folder"; 3 | "mediumLabel" = "Activate: Selected Folder"; 4 | "longLabel" = "Activate: Selected Folder"; 5 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/en.lproj/selectionOnHold.strings: -------------------------------------------------------------------------------- 1 | "label" = "On Hold: Selected Folder"; 2 | "shortLabel" = "On Hold: Selected Folder"; 3 | "mediumLabel" = "On Hold: Selected Folder"; 4 | "longLabel" = "On Hold: Selected Folder"; 5 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/toggle.omnifocusjs/Resources/pause.png -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psidnell/ofplugins/e7d04a54bfdbd1fe631e47e129ad71190587e109/toggle.omnifocusjs/Resources/play.png -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/selectionActivate.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | var action = new PlugIn.Action((selection, sender) => { 3 | 4 | var toggleLib = PlugIn.find('com.PaulSidnell.Toggle').library('toggleLib'); 5 | 6 | selection.folders.forEach(folder => { 7 | if (folder.active) { 8 | toggleLib.toggleFolder(folder, true); 9 | } 10 | }); 11 | 12 | }); 13 | 14 | action.validate = (selection, sender) => { 15 | return selection.folders.length > 0; 16 | }; 17 | 18 | return action; 19 | })(); 20 | _; 21 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/selectionOnHold.js: -------------------------------------------------------------------------------- 1 | var _ = (function() { 2 | var action = new PlugIn.Action((selection, sender) => { 3 | 4 | var toggleLib = PlugIn.find('com.PaulSidnell.Toggle').library('toggleLib'); 5 | 6 | selection.folders.forEach(folder => { 7 | if (folder.active) { 8 | toggleLib.toggleFolder(folder, false); 9 | } 10 | }); 11 | }); 12 | 13 | action.validate = (selection, sender) => { 14 | return selection.folders.length > 0 15 | }; 16 | 17 | return action; 18 | })(); 19 | _; 20 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/Resources/toggleLib.js: -------------------------------------------------------------------------------- 1 | var _ = function() { 2 | 3 | var toggleLib = new PlugIn.Library(new Version('0.1')); 4 | 5 | toggleLib.createDeactivatedTag = () => { 6 | var deactivatedTagName = 'DEACTIVATED'; 7 | var deactivatedTag = tagNamed(deactivatedTagName) || new Tag(deactivatedTagName); 8 | 9 | return deactivatedTag; 10 | }; 11 | 12 | toggleLib.toggle = function(child, deactivatedTag, activate) { 13 | if (child instanceof Project) { 14 | if (activate && child.status === Project.Status.OnHold 15 | && child.task.tags.includes(deactivatedTag)) { 16 | child.task.removeTag(deactivatedTag); 17 | child.status = Project.Status.Active; 18 | } else if (!activate && child.status === Project.Status.Active) { 19 | child.task.addTag(deactivatedTag); 20 | child.status = Project.Status.OnHold; 21 | } 22 | } 23 | }; 24 | 25 | toggleLib.toggleFolder = function(folder, activate) { 26 | var deactivatedTag = toggleLib.createDeactivatedTag(); 27 | folder.apply(child => { 28 | toggleLib.toggle(child, deactivatedTag, activate); 29 | }); 30 | 31 | // Open the folder 32 | var url = URL.fromString('omnifocus:///folder/' + encodeURIComponent(folder.name)); 33 | url.call(reply => {}); 34 | }; 35 | 36 | return toggleLib; 37 | }(); 38 | _; 39 | -------------------------------------------------------------------------------- /toggle.omnifocusjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultLocale": "en", 3 | "identifier": "com.PaulSidnell.Toggle", 4 | "author": "Paul Sidnell", 5 | "description": "Toggle a folder hierarchy active/on-hold", 6 | "version": "1.0", 7 | "actions": [ 8 | { "image":"play.png", "identifier": "chooseActivate" }, 9 | { "image":"pause.png", "identifier": "chooseOnHold" }, 10 | { "image":"play.png", "identifier": "selectionActivate" }, 11 | { "image":"pause.png", "identifier": "selectionOnHold" } 12 | ], 13 | "libraries": [ 14 | {"identifier": "toggleLib"} 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // "module": "system", 4 | // "noImplicitAny": true, 5 | // "removeComments": true, 6 | // "preserveConstEnums": true, 7 | // "outFile": "dist/comiled.js", 8 | // "sourceMap": true 9 | "lib": ["es7"] 10 | }, 11 | "include": [ 12 | "**/*" 13 | ], 14 | "exclude": [ 15 | "node_modules", 16 | "**/*.spec.ts", 17 | ".obsidian" 18 | ] 19 | } --------------------------------------------------------------------------------