├── Automating macOS with Javascript for Automation (JXA).md
├── Automating macOS with Javascript for Automation (JXA).pdf
├── README.md
├── media
├── gifs
│ ├── applescript.gif
│ └── javascript.gif
├── screenshots
│ ├── 2011.jpg
│ ├── Aperture.jpg
│ ├── ExportApplet.jpg
│ ├── FinderServices.jpg
│ ├── MojaveSidebar.jpg
│ ├── PhotosSdef.jpg
│ ├── QLScpt.jpg
│ ├── SafariDebugger.jpg
│ ├── SafariPrefs.jpg
│ ├── ScriptEditor1.jpg
│ ├── ScriptEditor2.jpg
│ ├── Scripts.jpg
│ ├── Service.jpg
│ ├── TouchBar.jpg
│ └── scpt.jpg
└── video
│ └── REPL.mp4
└── src
├── args
└── args.sh
├── calculator
├── Calculator.app
│ └── Contents
│ │ ├── Info.plist
│ │ ├── MacOS
│ │ └── applet
│ │ ├── PkgInfo
│ │ ├── Resources
│ │ ├── Scripts
│ │ │ └── main.scpt
│ │ ├── applet.icns
│ │ ├── applet.rsrc
│ │ └── description.rtfd
│ │ │ └── TXT.rtf
│ │ └── _CodeSignature
│ │ └── CodeResources
├── Calculator.js
└── Calculator.scpt
├── crop image
└── Crop Image.workflow
│ └── Contents
│ ├── Info.plist
│ ├── QuickLook
│ └── Preview.png
│ └── document.wflow
├── define
└── define.sh
└── safari
└── Create Tab.js
/Automating macOS with Javascript for Automation (JXA).md:
--------------------------------------------------------------------------------
1 | # JavaScript for Automation (JXA) ⚡️💻
2 | ## @joshparnham
3 |
4 | ---
5 |
6 | ^ I'm Josh, a developer working on iOS and JS stuff at Xero.
7 |
8 | ^ Talk was originally scheduled for May, but moved a month later. Since some of the example code and copyright information hasn't been touched since 2011 I was really worried that Apple would deprecate the scripting architecture at WWDC - thankfully the opposite happened!
9 |
10 | # 😅
11 |
12 | ---
13 |
14 | 
15 |
16 | ---
17 |
18 | ^ Aperture was discontinued in 2014/15
19 | ^ A `.scptd` file is a "Script Bundle"
20 |
21 | 
22 |
23 | ---
24 |
25 | ## Mac Automation ❓
26 |
27 | ^ macOS has set of rich built-in frameworks for automation.
28 |
29 | - Automating tasks on macOS
30 | - Scripting supported Applications
31 | - UI element automation
32 | - System APIs
33 |
34 | ---
35 |
36 | ^ Traditionally macOS scripting has been accomplished with AppleScript, which is a language developed by Apple in 1993 - its goal was to create a programming language that featured an "English-like" syntax. As such, it can be very verbose and can feature a large learning curve for those who are more familiar with traditional languages.
37 |
38 | # AppleScript
39 |
40 | ---
41 |
42 | # AppleScript
43 |
44 | > "If you're used to a programming language, AppleScript will drive you crazy"
45 | -- Sal Soghoian
46 |
47 | ---
48 |
49 | ^ That is not ideal.
50 |
51 | 
52 |
53 | ---
54 |
55 | ^ However thankfully, you can use JavaScript as an alternative to AppleScript, and since you're all sitting in this room with me tonight I'll assume that you also think this is absolutely awesome.
56 |
57 | # JavaScript
58 |
59 | ---
60 |
61 | 
62 |
63 | ---
64 |
65 | ## History 📚
66 |
67 | ^ This JavaScript scripting support was added in OS X Yosemite.
68 |
69 | - OS X 10.10 Yosemite (2014)
70 |
71 | ^ The OSA makes use of Apple Events, which is an IPC mechanism that encapsulates commands and arbitrary data.
72 |
73 | - Open Scripting Architecture
74 | - `osascript`/`osacompile` and friends
75 |
76 | ^ JavaScriptCore is a wrapper around WebKit's JavaScript engine
77 |
78 | - JavaScript implementation based on `JavaScriptCore`
79 |
80 | ---
81 |
82 | ## Basics 👩🏫
83 |
84 | ```js
85 | const Safari = Application('Safari')
86 | const firstWindow = Safari.windows[0]
87 | const tab = Safari.Tab({url: "https://joshparnham.com"})
88 | firstWindow.tabs.push(tab)
89 | firstWindow.currentTab = tab
90 | ```
91 |
92 | ---
93 |
94 | # Script Editor.app 📜
95 |
96 | ^ The main IDE that you'll be using to write your automation scripts is Script Editor, just make sure you've selected JavaScript as the language.
97 |
98 | ---
99 |
100 | 
101 |
102 | ---
103 |
104 | 
105 |
106 | ---
107 |
108 | # Documentation 📕
109 |
110 | ^ The Script Editor is also where you can read documentation for both the scriptable apps and the system scripting features.
111 |
112 | ^ Can drag an application onto Script Editor, and if it has a `.sdef` file, those contents will be displayed. A `sdef` file is an XML-based file type which maps OSA commands to cocoa methods.
113 |
114 | ^ Viewing the Library (Window > Library) also lists documentation about various applications and system libraries.
115 |
116 | 
117 |
118 | ---
119 |
120 | ## Debugging 🐛
121 |
122 | 
123 |
124 | ---
125 |
126 | ## Debugging 🐛
127 |
128 | - `debugger;` statement in Script Editor
129 |
130 | ---
131 |
132 | ## Debugging 🐛
133 |
134 | 
135 |
136 | ---
137 |
138 | # Invocation ❇️
139 |
140 | ^ Can open a `.scpt` file from Finder, etc
141 | ^ An scpt file is a binary representation of an AppleScript or JavaScript file
142 | ^ A plain-text AppleScript or JavaScript file can be converted into a compiled `.scpt` file from the Script Editor IDE or from the command line with `osacompile` (and visa-versa with `osadecompile`)
143 |
144 | - `.scpt` file
145 |
146 | 
147 |
148 | ---
149 |
150 | # Invocation ❇️
151 |
152 | ^ You can package your script as an applet which is treated like a regular application (eg. can drag items onto it when in the dock)
153 |
154 | - Applet
155 |
156 | 
157 |
158 | ---
159 |
160 | # Invocation ❇️
161 |
162 | ^ Using Automator, you can create a macOS service which executes your JXA wherever an application services menu is. For example, you can create a workflow which accepts folders from the Finder and invokes the script with that.
163 |
164 | - Services menu
165 |
166 | 
167 |
168 | ---
169 |
170 | # Invocation ❇️
171 |
172 | 
173 |
174 | ---
175 |
176 |
177 | # Invocation ❇️
178 |
179 | ^ This adds a menu bar item which gives you access to scripts in predefined paths on the system.
180 |
181 | - System-wide Scripts menu
182 |
183 | 
184 |
185 | ---
186 |
187 | # Invocation ❇️
188 |
189 | ^ https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/WatchFolders.html#//apple_ref/doc/uid/TP40016239-CH39-SW1
190 |
191 | - Folder contents changed
192 | - Calendar alarms
193 | - Dictation command
194 | - and others 🚀
195 |
196 | ---
197 |
198 | # Even Better Invocation 🏜
199 |
200 | ^ Next macOS improves the accessibility of automation scripts in the Finder.
201 |
202 | 
203 |
204 | ---
205 |
206 | # Even Better Invocation 🏜
207 |
208 | 
209 |
210 | ---
211 |
212 | # REPL ⌨️
213 |
214 | ```sh
215 | $ osascript -i -l JavaScript
216 | ```
217 |
218 | ---
219 |
220 | 
221 |
222 | ---
223 |
224 | ^ Moving on from there, the `osascript` command can also be used in scripts.
225 |
226 | # Scripting ⚡️
227 | ## Example: `args.sh`
228 | ---
229 |
230 | ^ `padStart` is actually from ES2017 as well
231 |
232 | ```js
233 | #!/usr/bin/env osascript -l JavaScript
234 |
235 | run = argv => console.log(
236 | argv.map(x => x.padStart(10)).join('\n')
237 | )
238 | ```
239 |
240 | ---
241 |
242 | ```sh
243 | $ ./args.sh Hello there MelbJS! 👋
244 |
245 | Hello
246 | there
247 | MelbJS!
248 | 👋
249 | ```
250 |
251 | ---
252 |
253 | # Interoperability ☕️
254 |
255 | ^ Can use the standard JavaScript `replace` method...
256 |
257 | ```js
258 | const result = 'hello'.replace('e', '3')
259 | ```
260 |
261 | ---
262 |
263 | # Interoperability ☕️
264 |
265 | ^ ... or the much nicer Objective-C equivalent.
266 |
267 | ```js
268 | const result = ObjC.wrap('hello')
269 | .stringByReplacingOccurrencesOfStringWithString('e', '3').js
270 | ```
271 |
272 | ---
273 |
274 | # Interoperability Examples ☕️
275 |
276 | ```js
277 | #!/usr/bin/env osascript -l JavaScript
278 |
279 | ObjC.import('DictionaryServices')
280 |
281 | run = argv => {
282 | const word = argv[0]
283 | const range = { 'location': 0, 'length': word.length }
284 | let definition = $.DCSCopyTextDefinition(null, word, range)
285 | console.log(definition.js.split(" | ").join("\n"))
286 | }
287 | ```
288 |
289 | ---
290 |
291 | # Interoperability Examples ☕️
292 |
293 | ```sh
294 | $ ./define.sh JavaScript
295 |
296 | JavaScript
297 | ˈdʒɑːvəˌskrɪpt
298 | noun [mass noun] trademark an object-oriented computer programming
299 | language commonly used to create interactive effects within web browsers.
300 | ORIGIN 1990s: from Java2 + script1.
301 | ```
302 |
303 | ---
304 |
305 | # Interoperability ☕️
306 |
307 | - Called the `JavaScriptObjC` bridge
308 |
309 | ---
310 |
311 | ### JavaScript Context ➡️ Objective-C Context
312 |
313 | - Convert primitive JS type to ObjC object
314 |
315 | ```js
316 | ObjC.wrap(...)
317 | ```
318 |
319 | ^ For some good old jQuery throwbacks, this is also aliased to $
320 |
321 | ```js
322 | $(...)
323 | ```
324 |
325 | ---
326 |
327 | ### Objective-C Context ➡️ JavaScript Context
328 |
329 | - Convert ObjC object to JS type
330 |
331 | ```js
332 | ObjC.unwrap(...)
333 | ```
334 |
335 | ^ Aliased to appending `.js` at the end of your line
336 |
337 | ```js
338 | .js
339 | ```
340 |
341 | ---
342 |
343 | ## Applications 🖥
344 |
345 | ^ Massive props to Tyler Gaw for his fantastic example repo, from which this example is adapted: https://github.com/tylergaw/js-osx-app-examples
346 | ^ There is an explanatory blog post here: https://tylergaw.com/articles/building-osx-apps-with-js/
347 |
348 | - Interoperability with `AppKit`
349 | - Native UIs purely in (bridged) JavaScript
350 |
351 | ---
352 |
353 | ^ Cocoa encompasses Foundation, AppKit, and Core Data
354 |
355 | ```js
356 | ObjC.import('Cocoa')
357 |
358 | const resultString = (result) => `Result: ${ result ? result.toString() : ''}`
359 |
360 | ObjC.registerSubclass({
361 | name: 'AppDelegate',
362 | methods: {
363 | 'calculate:': {
364 | types: ['void', ['id']],
365 | implementation: function (sender) {
366 | var total =
367 | Number(textField1.stringValue.js) +
368 | Number(textField2.stringValue.js)
369 | resultTextFieldLabel.stringValue = resultString(total)
370 | }
371 | }
372 | }
373 | })
374 | ```
375 |
376 | ---
377 |
378 | ```js
379 | var appDelegate = $.AppDelegate.alloc.init
380 |
381 | const styleMask =
382 | $.NSTitledWindowMask |
383 | $.NSClosableWindowMask |
384 | $.NSMiniaturizableWindowMask
385 |
386 | var window =
387 | $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
388 | $.NSMakeRect(0, 0, 250, 120),
389 | styleMask,
390 | $.NSBackingStoreBuffered,
391 | false
392 | )
393 | ```
394 |
395 | ---
396 |
397 | ```js
398 | var calculateButton =
399 | $.NSButton.alloc.initWithFrame(
400 | $.NSMakeRect(25, 20, 200, 25)
401 | )
402 | calculateButton.title = 'Calculate 🧙♂️'
403 | calculateButton.bezelStyle = $.NSRoundedBezelStyle
404 | calculateButton.buttonType = $.NSMomentaryLightButton
405 | calculateButton.target = appDelegate
406 | calculateButton.action = 'calculate:'
407 | calculateButton.keyEquivalent = '\r'
408 | window.contentView.addSubview(calculateButton)
409 | ```
410 |
411 | ---
412 | ```js
413 | var resultTextFieldLabel =
414 | $.NSTextField.alloc.initWithFrame(
415 | $.NSMakeRect(25, 45, 200, 24)
416 | )
417 | resultTextFieldLabel.stringValue = resultString()
418 | resultTextFieldLabel.drawsBackground = false
419 | resultTextFieldLabel.editable = false
420 | resultTextFieldLabel.bezeled = false
421 | resultTextFieldLabel.selectable = true
422 | window.contentView.addSubview(resultTextFieldLabel)
423 | ```
424 |
425 | ---
426 |
427 | ```js
428 | var textField1 =
429 | $.NSTextField.alloc.initWithFrame(
430 | $.NSMakeRect(25, 80, 90, 24)
431 | )
432 | textField1.stringValue = ''
433 | window.contentView.addSubview(textField1)
434 | ```
435 |
436 | ---
437 |
438 | ```js
439 | var plusTextFieldLabel =
440 | $.NSTextField.alloc.initWithFrame(
441 | $.NSMakeRect(120, 80, 24, 24)
442 | )
443 | plusTextFieldLabel.stringValue = '+'
444 | plusTextFieldLabel.drawsBackground = false
445 | plusTextFieldLabel.editable = false
446 | plusTextFieldLabel.bezeled = false
447 | plusTextFieldLabel.selectable = true
448 | window.contentView.addSubview(plusTextFieldLabel)
449 | ```
450 |
451 | ---
452 |
453 | ```js
454 | var textField2 =
455 | $.NSTextField.alloc.initWithFrame(
456 | $.NSMakeRect(135, 80, 90, 24)
457 | )
458 | textField2.stringValue = ''
459 | window.contentView.addSubview(textField2)
460 | ```
461 |
462 | ---
463 |
464 | ```js
465 | window.center
466 | window.title = 'Scientific Calculator 🙈'
467 | window.makeKeyAndOrderFront(window)
468 | ```
469 |
470 | ^ You can save a script as an Application in Script Editor, or do the below.
471 | ^ Need to tick "Stay open after run handler" when exporting in Script Editor.
472 |
473 | ---
474 |
475 | # Packaging 📦
476 |
477 | Either through `Script Editor.app` or `osacompile`.
478 |
479 |
480 |
481 | ```sh
482 | osacompile -l JavaScript -o Calculator.app -s Calculator.js
483 | ```
484 |
485 | ---
486 |
487 | # *Demo*
488 |
489 | ---
490 |
491 | ## PS 🐙
492 |
493 | QLScpt
494 |
495 | ```sh
496 | brew cask install josh-/qlscpt/qlscpt
497 | ```
498 |
499 | 
500 |
501 | ---
502 |
503 | ## Caveats ✋
504 |
505 | ^ The documentation is lacking - the main documentation reference contains only two entries for old macOS releases: https://developer.apple.com/library/archive/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/Introduction.html#//apple_ref/doc/uid/TP40014508-CH111-SW1
506 |
507 | ^ The best documentation is probably the "About Mac Scripting" guide: https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/index.html#//apple_ref/doc/uid/TP40016239-CH56-SW1
508 |
509 | ^ Sample code can also be wrong as well
510 |
511 | ^ The JXA cookbook has a lot of extra information not covered in the official docs: https://github.com/JXA-Cookbook/JXA-Cookbook
512 |
513 | - Limited documentation
514 |
515 | ^ I've found that `const` variables can persist across invocations within Script Editor, as well as a variety of other issues.
516 |
517 | - Buggy
518 |
519 | ^ Most of the sample code and open source code is written in AppleScript, and it can be non-trivial to convert between the languages.
520 |
521 | - Majority of automation code is still AppleScript
522 |
523 | - 🐲 **Here be dragons** 🐉
524 |
525 | ---
526 |
527 | # Thanks ✌️
528 | ## @joshparnham
529 |
530 | ^ These slides will be put up in a repo on my GitHub
531 |
532 | ## github.com/josh-
533 |
--------------------------------------------------------------------------------
/Automating macOS with Javascript for Automation (JXA).pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/Automating macOS with Javascript for Automation (JXA).pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Automating macOS with Javascript for Automation (JXA)
2 |
3 | Made with [Deckset](https://www.decksetapp.com/).
4 |
5 | Resources:
6 | - [PDF of the slides](https://github.com/josh-/automating-macOS-with-JXA-presentation/blob/master/Automating%20macOS%20with%20Javascript%20for%20Automation%20%28JXA%29.pdf)
7 | - [Sample Code](https://github.com/josh-/automating-macOS-with-JXA-presentation/tree/master/src)
8 |
--------------------------------------------------------------------------------
/media/gifs/applescript.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/gifs/applescript.gif
--------------------------------------------------------------------------------
/media/gifs/javascript.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/gifs/javascript.gif
--------------------------------------------------------------------------------
/media/screenshots/2011.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/2011.jpg
--------------------------------------------------------------------------------
/media/screenshots/Aperture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/Aperture.jpg
--------------------------------------------------------------------------------
/media/screenshots/ExportApplet.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/ExportApplet.jpg
--------------------------------------------------------------------------------
/media/screenshots/FinderServices.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/FinderServices.jpg
--------------------------------------------------------------------------------
/media/screenshots/MojaveSidebar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/MojaveSidebar.jpg
--------------------------------------------------------------------------------
/media/screenshots/PhotosSdef.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/PhotosSdef.jpg
--------------------------------------------------------------------------------
/media/screenshots/QLScpt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/QLScpt.jpg
--------------------------------------------------------------------------------
/media/screenshots/SafariDebugger.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/SafariDebugger.jpg
--------------------------------------------------------------------------------
/media/screenshots/SafariPrefs.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/SafariPrefs.jpg
--------------------------------------------------------------------------------
/media/screenshots/ScriptEditor1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/ScriptEditor1.jpg
--------------------------------------------------------------------------------
/media/screenshots/ScriptEditor2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/ScriptEditor2.jpg
--------------------------------------------------------------------------------
/media/screenshots/Scripts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/Scripts.jpg
--------------------------------------------------------------------------------
/media/screenshots/Service.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/Service.jpg
--------------------------------------------------------------------------------
/media/screenshots/TouchBar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/TouchBar.jpg
--------------------------------------------------------------------------------
/media/screenshots/scpt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/screenshots/scpt.jpg
--------------------------------------------------------------------------------
/media/video/REPL.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/media/video/REPL.mp4
--------------------------------------------------------------------------------
/src/args/args.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env osascript -l JavaScript
2 |
3 | run = argv => console.log(
4 | argv.map(x => x.padStart(10)).join('\n')
5 | )
6 |
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleAllowMixedLocalizations
6 |
7 | CFBundleDevelopmentRegion
8 | English
9 | CFBundleExecutable
10 | applet
11 | CFBundleIconFile
12 | applet
13 | CFBundleIdentifier
14 | com.apple.ScriptEditor.id.Calculator
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | Calculator
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | aplt
25 | LSMinimumSystemVersionByArchitecture
26 |
27 | x86_64
28 | 10.6
29 |
30 | LSRequiresCarbon
31 |
32 | WindowState
33 |
34 | bundleDividerCollapsed
35 |
36 | bundlePositionOfDivider
37 | 0.0
38 | dividerCollapsed
39 |
40 | eventLogLevel
41 | 2
42 | name
43 | ScriptWindowState
44 | positionOfDivider
45 | 593
46 | savedFrame
47 | 0 33 720 844 0 0 1440 877
48 | selectedTab
49 | description
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/MacOS/applet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/src/calculator/Calculator.app/Contents/MacOS/applet
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/PkgInfo:
--------------------------------------------------------------------------------
1 | APPLaplt
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/Resources/Scripts/main.scpt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/src/calculator/Calculator.app/Contents/Resources/Scripts/main.scpt
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/Resources/applet.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/src/calculator/Calculator.app/Contents/Resources/applet.icns
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/Resources/applet.rsrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/src/calculator/Calculator.app/Contents/Resources/applet.rsrc
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/Resources/description.rtfd/TXT.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400
2 | {\fonttbl}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\expandedcolortbl;;}
5 | }
--------------------------------------------------------------------------------
/src/calculator/Calculator.app/Contents/_CodeSignature/CodeResources:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | files
6 |
7 | Resources/Scripts/main.scpt
8 |
9 | 8X58jMCdcZohOuteaTlUIWT18T8=
10 |
11 | Resources/applet.icns
12 |
13 | sINd6lbiqHD5dL8c6u79cFvVXhw=
14 |
15 | Resources/applet.rsrc
16 |
17 | 03GMg1LK/YSdtQnC5JmZVcrcSmg=
18 |
19 | Resources/description.rtfd/TXT.rtf
20 |
21 | lcAGO33IpiFXFKH4zArtBM/qDMo=
22 |
23 |
24 | files2
25 |
26 | Resources/Scripts/main.scpt
27 |
28 | hash
29 |
30 | 8X58jMCdcZohOuteaTlUIWT18T8=
31 |
32 | hash2
33 |
34 | fKcZbvQMzPnuKVOFiguOLzVfmItoI469Hg5VGLSjBDo=
35 |
36 |
37 | Resources/applet.icns
38 |
39 | hash
40 |
41 | sINd6lbiqHD5dL8c6u79cFvVXhw=
42 |
43 | hash2
44 |
45 | J7weZ6vlnv9r32tS5HFcyuPXl2StdDnfepLxAixlryk=
46 |
47 |
48 | Resources/applet.rsrc
49 |
50 | hash
51 |
52 | 03GMg1LK/YSdtQnC5JmZVcrcSmg=
53 |
54 | hash2
55 |
56 | HvDodrjydmQenhDjgBXv+i+agDtK3j7Hw1UQHq3xpfY=
57 |
58 |
59 | Resources/description.rtfd/TXT.rtf
60 |
61 | hash
62 |
63 | lcAGO33IpiFXFKH4zArtBM/qDMo=
64 |
65 | hash2
66 |
67 | xSJq5q+RSjvZKxm6uOcpcm7cV6u5Y/qiw98pqGnO8wA=
68 |
69 |
70 |
71 | rules
72 |
73 | ^Resources/
74 |
75 | ^Resources/.*\.lproj/
76 |
77 | optional
78 |
79 | weight
80 | 1000
81 |
82 | ^Resources/.*\.lproj/locversion.plist$
83 |
84 | omit
85 |
86 | weight
87 | 1100
88 |
89 | ^Resources/Base\.lproj/
90 |
91 | weight
92 | 1010
93 |
94 | ^version.plist$
95 |
96 |
97 | rules2
98 |
99 | .*\.dSYM($|/)
100 |
101 | weight
102 | 11
103 |
104 | ^(.*/)?\.DS_Store$
105 |
106 | omit
107 |
108 | weight
109 | 2000
110 |
111 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
112 |
113 | nested
114 |
115 | weight
116 | 10
117 |
118 | ^.*
119 |
120 | ^Info\.plist$
121 |
122 | omit
123 |
124 | weight
125 | 20
126 |
127 | ^PkgInfo$
128 |
129 | omit
130 |
131 | weight
132 | 20
133 |
134 | ^Resources/
135 |
136 | weight
137 | 20
138 |
139 | ^Resources/.*\.lproj/
140 |
141 | optional
142 |
143 | weight
144 | 1000
145 |
146 | ^Resources/.*\.lproj/locversion.plist$
147 |
148 | omit
149 |
150 | weight
151 | 1100
152 |
153 | ^Resources/Base\.lproj/
154 |
155 | weight
156 | 1010
157 |
158 | ^[^/]+$
159 |
160 | nested
161 |
162 | weight
163 | 10
164 |
165 | ^embedded\.provisionprofile$
166 |
167 | weight
168 | 20
169 |
170 | ^version\.plist$
171 |
172 | weight
173 | 20
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/src/calculator/Calculator.js:
--------------------------------------------------------------------------------
1 | ObjC.import('Cocoa')
2 |
3 | const resultString = (result) => `Result: ${ result ? result.toString() : ''}`
4 |
5 | ObjC.registerSubclass({
6 | name: 'AppDelegate',
7 | methods: {
8 | 'calculate:': {
9 | types: ['void', ['id']],
10 | implementation: function (sender) {
11 | var total = Number(textField1.stringValue.js) + Number(textField2.stringValue.js)
12 | resultTextFieldLabel.stringValue = resultString(total)
13 | }
14 | }
15 | }
16 | })
17 |
18 | var appDelegate = $.AppDelegate.alloc.init
19 |
20 | const styleMask = $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask
21 | var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
22 | $.NSMakeRect(0, 0, 250, 120),
23 | styleMask,
24 | $.NSBackingStoreBuffered,
25 | false
26 | )
27 |
28 | var calculateButton = $.NSButton.alloc.initWithFrame($.NSMakeRect(25, 20, 200, 25))
29 | calculateButton.title = 'Calculate 🧙♂️'
30 | calculateButton.bezelStyle = $.NSRoundedBezelStyle
31 | calculateButton.buttonType = $.NSMomentaryLightButton
32 | calculateButton.target = appDelegate
33 | calculateButton.action = 'calculate:'
34 | calculateButton.keyEquivalent = '\r'
35 |
36 | window.contentView.addSubview(calculateButton)
37 |
38 | var resultTextFieldLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, 45, 200, 24))
39 | resultTextFieldLabel.stringValue = resultString()
40 | resultTextFieldLabel.drawsBackground = false
41 | resultTextFieldLabel.editable = false
42 | resultTextFieldLabel.bezeled = false
43 | resultTextFieldLabel.selectable = true
44 | window.contentView.addSubview(resultTextFieldLabel)
45 |
46 | var textField1 = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, 80, 90, 24))
47 | textField1.stringValue = ''
48 | window.contentView.addSubview(textField1)
49 |
50 | var plusTextFieldLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(120, 80, 24, 24))
51 | plusTextFieldLabel.stringValue = '+'
52 | plusTextFieldLabel.drawsBackground = false
53 | plusTextFieldLabel.editable = false
54 | plusTextFieldLabel.bezeled = false
55 | plusTextFieldLabel.selectable = true
56 | window.contentView.addSubview(plusTextFieldLabel)
57 |
58 | var textField2 = $.NSTextField.alloc.initWithFrame($.NSMakeRect(135, 80, 90, 24))
59 | textField2.stringValue = ''
60 | window.contentView.addSubview(textField2)
61 |
62 | window.center
63 | window.title = 'Scientific Calculator 🙈'
64 | window.makeKeyAndOrderFront(window)
65 |
--------------------------------------------------------------------------------
/src/calculator/Calculator.scpt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/src/calculator/Calculator.scpt
--------------------------------------------------------------------------------
/src/crop image/Crop Image.workflow/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSServices
6 |
7 |
8 | NSMenuItem
9 |
10 | default
11 | Crop Image
12 |
13 | NSMessage
14 | runWorkflowAsService
15 | NSRequiredContext
16 |
17 | NSApplicationIdentifier
18 | com.apple.finder
19 |
20 | NSSendFileTypes
21 |
22 | public.item
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/crop image/Crop Image.workflow/Contents/QuickLook/Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josh-/automating-macOS-with-JXA-presentation/f0f019ad2a827625fd875c761c99fed57c93d337/src/crop image/Crop Image.workflow/Contents/QuickLook/Preview.png
--------------------------------------------------------------------------------
/src/crop image/Crop Image.workflow/Contents/document.wflow:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AMApplicationBuild
6 | 444.7
7 | AMApplicationVersion
8 | 2.8
9 | AMDocumentVersion
10 | 2
11 | actions
12 |
13 |
14 | action
15 |
16 | AMAccepts
17 |
18 | Container
19 | List
20 | Optional
21 |
22 | Types
23 |
24 | com.apple.applescript.object
25 |
26 |
27 | AMActionVersion
28 | 1.0
29 | AMApplication
30 |
31 | Automator
32 |
33 | AMParameterProperties
34 |
35 | source
36 |
37 |
38 | AMProvides
39 |
40 | Container
41 | List
42 | Types
43 |
44 | com.apple.applescript.object
45 |
46 |
47 | ActionBundlePath
48 | /System/Library/Automator/Run JavaScript.action
49 | ActionName
50 | Run JavaScript
51 | ActionParameters
52 |
53 | source
54 | const SystemEvents = Application('System Events')
const ImageEvents = Application('Image Events')
55 |
56 | run = (input, parameters) => {
57 | for (var filePath of input) {
58 | const file = ImageEvents.open(filePath.toString())
59 | file.crop({toDimensions: [400, 400]})
file.save({in: filePath.toString()})
60 | }
return input;
}
61 |
62 | BundleIdentifier
63 | com.apple.Automator.RunJavaScript
64 | CFBundleVersion
65 | 1.0
66 | CanShowSelectedItemsWhenRun
67 |
68 | CanShowWhenRun
69 |
70 | Category
71 |
72 | AMCategoryUtilities
73 |
74 | Class Name
75 | RunJavaScriptAction
76 | InputUUID
77 | BDA8FF6C-A73A-4410-AA15-60D8EAE3E732
78 | Keywords
79 |
80 | Run
81 | JavaScript
82 |
83 | OutputUUID
84 | 614A4317-86FE-4219-B38B-BFB100091E4D
85 | UUID
86 | 1F3ABE58-4D10-4A36-90D5-B67486012FB7
87 | UnlocalizedApplications
88 |
89 | Automator
90 |
91 | arguments
92 |
93 | 0
94 |
95 | default value
96 | function run(input, parameters) {
97 |
98 | // Your script goes here
99 |
100 | return input;
101 | }
102 | name
103 | source
104 | required
105 | 0
106 | type
107 | 0
108 | uuid
109 | 0
110 |
111 |
112 | isViewVisible
113 |
114 | location
115 | 297.500000:599.000000
116 | nibPath
117 | /System/Library/Automator/Run JavaScript.action/Contents/Resources/Base.lproj/main.nib
118 |
119 | isViewVisible
120 |
121 |
122 |
123 | connectors
124 |
125 | workflowMetaData
126 |
127 | serviceApplicationBundleID
128 | com.apple.finder
129 | serviceApplicationPath
130 | /System/Library/CoreServices/Finder.app
131 | serviceInputTypeIdentifier
132 | com.apple.Automator.fileSystemObject
133 | serviceOutputTypeIdentifier
134 | com.apple.Automator.nothing
135 | serviceProcessesInput
136 | 0
137 | workflowTypeIdentifier
138 | com.apple.Automator.servicesMenu
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/src/define/define.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env osascript -l JavaScript
2 |
3 | ObjC.import('DictionaryServices')
4 |
5 | run = argv => {
6 | const word = argv[0]
7 | const range = { 'location': 0, 'length': word.length }
8 | let definition = $.DCSCopyTextDefinition(null, word, range)
9 | console.log(definition.js.split(" | ").join("\n"))
10 | }
11 |
--------------------------------------------------------------------------------
/src/safari/Create Tab.js:
--------------------------------------------------------------------------------
1 | const Safari = Application('Safari')
2 | const firstWindow = Safari.windows[0]
3 | const tab = Safari.Tab({url: "https://joshparnham.com"})
4 | firstWindow.tabs.push(tab)
5 | firstWindow.currentTab = tab
6 |
--------------------------------------------------------------------------------