C++string容器-字串比較
- JXA-ObjC
- demo
JXA-ObjC
$
$ 是所有函式呼叫的主要訪問點
str = $ 。NSString 。ALLOC 。initWithUTF8String ('foo' )
str 。writeToFileAtomically ('/ tmp / foo' , true )
引入框架
預設情況下直接使用$.NSBeep(),系統會報錯,原因是 NSBeep 是 ObjC 的一個框架下的方法,呼叫時需要引入
轉換 ObjC 的語法
ObjeCinit(contentRect:styleMask:backing:defer:)
=>JXAinitContentRectStyleMaskBackingDefer()
在 ObjectiveC 定義中,每個冒號前面的每個“名稱”代表函式期望的特定變數型別。我們可以在宣告中看到以下每種型別:
init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool)
賦予函式’contentRect’的第一個引數應為type NSRect
賦予函式’styleMask’的第二個引數應為type NSWindow.StyleMask
賦予函式“ backing”的第三個引數應為type NSWindow.BackingStoreType
給我們的函式’defer’的第四個引數應該是型別Boolean
因此,當我們呼叫函式時,我們需要按照宣告中定義的順序提供這些引數。
在目標 C 中,通常以以下方式執行上述功能:
NSRect frame = NSMakeRect(0, 0, 200, 200);
NSWindow* window = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO]
判斷是否是資料夾
BOOL isDir;
NSFileManager *fileManager = [[NSFileManager alloc] init];
if ([fileManager fileExistsAtPath:fontPath isDirectory:&isDir] ...
轉換為 jxa 語法
isDir = {}
$.NSFileManager.alloc.init.fileExistsAtPathIsDirectory(
'/Users/luomingyang/Desktop/1元搶199-100大額券.png',
isDir
)
console.log(Object.keys(isDir)) //==> []
isDir = Ref() //set up a variable which can be passed by reference to ObjC functions.
$.NSFileManager.alloc.init.fileExistsAtPathIsDirectory(
'/Users/luomingyang/Desktop/1元搶199-100大額券.png',
isDir
)
isDir[0] //get the data from the variable
demo
新建終端視窗
var app = Application.currentApplication()
var Terminal = Application('Terminal')
app.includeStandardAdditions = true
termWindow = Terminal.windows[0]
termTabs = termWindow.tabs
newTab = new Terminal.Tab()
try {
termTabs.push(newTab)
} catch (e) {
app.say(stringifyObj(e))
}
在 finder 右鍵狀態列
var app = Application.currentApplication()
app.includeStandardAdditions = true
var se = Application('System Events')
se.processes['Finder'].windows[0].toolbars[0].actions['AXShowMenu'].perform()
================================
通過 apple 指令碼的形式解析並執行 js 檔案
var osascript = require('osascript')
var fs = require('fs');
fs.createReadStream('/Users/luomingyang/Luo_MingYang/JS&JSA/note_JSA.js')
.pipe(osascript())
.pipe(process.stdout);
=========================
var osascript = require('osascript').eval;
Run JavaScript text through OSA
osascript('console.log("Hello, world!");').pipe(process.stdout);
=========================
var osascript = require('osascript').file
Run JavaScript file through OSA
osascript(
'/Users/luomingyang/Library/Mobile Documents/iCloud~com~coderforart~iOS~MWeb/Documents/MD_All/draft.js',
function (err, data) {
console.log(err, data)
}
)
如何轉換報錯資訊
function stringifyObj(specifier) {
return Automation.getDisplayString(specifier)
}
doShell
var app = Application.currentApplication()
var host = 'digitalocean.com'
app.doShellScript('ping -cl' + host)
var Calendar = Application('Calendar')
var projectCalendars = Calendar.calendars.whose({ name: '日曆' }) //在日曆中查詢名稱為'日曆'的日曆表
var projectCalendar = projectCalendars[0] //專案日曆為第一個結果
var events = projectCalendar.events.whose({ summary: 'JXA' }) //選中描述日程中
var event = events[0] //第一個日程
var attendee = Calendar.Attendee({ email: '[email protected]' }) //新增參會者
event.attendees.push(attendee)
Calendar.reloadCalendars()
將檔案複製到剪貼簿
使用該命令後,系統會有卡頓
ObjC.import('AppKit')
/** @param {string} path */
function copyPathToClipboard(path) {
const pasteboard = $.NSPasteboard.generalPasteboard
pasteboard.clearContents
return pasteboard.setPropertyListForType($([path]), $.NSFilenamesPboardType)
}
copyPathToClipboard('/Users/luomingyang/Desktop/1元搶199-100大額券.png')
開啟檔案選擇器
檔案選擇框 var require = function (path) {
app = Application.currentApplication()
app.includeStandardAdditions = true
app.chooseFile()
app.chooseFile({ withPrompt: 'Please select the first image' })
// => Path("/Users/dtinth/npm-debug.log")
// OR !! Error on line 1: Error: User canceled.
// 檔案型別過濾
app.chooseFile({
withPrompt: 'Select the first image',
ofType: ['public.jpeg', 'public.png'],
})
獲得指定目錄下的所有檔案列表
最簡單的方法
var strPath = '/Users/luomingyang/Alfred_Workflow-1.40.0.dist-info'
var appSys = Application('System Events'),
lstHarvest = appSys.folders.byName(strPath).diskItems.name()
console.log(lstHarvest)
速度提升 40%的方法
var strPath = '/Users/luomingyang/Alfred_Workflow-1.40.0.dist-info'
var fm = $.NSFileManager.defaultManager,
oURL = $.NSURL.fileURLWithPathIsDirectory(strPath, true),
lstFiles = ObjC.unwrap(
fm.contentsOfDirectoryAtURLIncludingPropertiesForKeysOptionsError(
oURL,
[],
1 << 2,
null
)
),
lstHarvest = []
lstFiles.forEach(function (oItem) {
lstHarvest.push(ObjC.unwrap(oItem.path))
})
console.log(lstHarvest)
速度提升 300%的方法
;(function () {
// listDirectory :: FilePath -> [FilePath]
function listDirectory(strPath) {
fm = fm || $.NSFileManager.defaultManager
return ObjC.unwrap(
fm.contentsOfDirectoryAtPathError(
$(strPath).stringByExpandingTildeInPath,
null
)
).map(ObjC.unwrap)
}
var fm = $.NSFileManager.defaultManager
return listDirectory('~/Desktop')
})()
獲取指定資料夾下的檔案資訊
var app = Application.currentApplication()
app.includeStandardAdditions = true
var finderApp = Application('Finder')
var itemList = finderApp.selection()
var oItem = itemList[0]
var oItemPaths = getPathInfo(oItem)
/* --- oItemPaths Object Keys ---
oItemPaths.itemClass
oItemPaths.fullPath
oItemPaths.parentPath
oItemPaths.itemName
*/
console.log(JSON.stringify(oItemPaths, undefined, 4))
//--- EXAMPLE RESULTS ---
/* {
"itemClass": "documentFile",
"fullPath": "/Users/luomingyang/Luo_MingYang/JS&JSA/note_JSA.md",
"parentPath": "/Users/luomingyang/Luo_MingYang/JS&JSA",
"itemName": "note_JSA.md"
} */
//~~~~~~~~~~~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function getPathInfo(pFinderItem) {
// @Path @Finder @File @Folder
/* Ver 2.0 2017-08-01
---------------------------------------------------------------------------------
PURPOSE: Get Object/Record of Finder Item Path Info
PARAMETERS:
• pFinderItem | object | Finder Item object
RETURNS: Object with these Keys:
itemClass
fullPath
parentPath
itemName
AUTHOR: JMichaelTX
—————————————————————————————————————————————————————————————————————————————————
*/
var itemClass = pFinderItem.class() // returns "folder" if item is a folder.
var itemURL = pFinderItem.url()
var fullPath = decodeURIComponent(itemURL).slice(7)
//--- Remove Trailing "/", if any, to handle folder item ---
var pathElem = fullPath.replace(/\/$/, '').split('/')
var itemName = pathElem.pop()
var parentPath = pathElem.join('/')
return {
itemClass: itemClass,
fullPath: fullPath,
parentPath: parentPath,
itemName: itemName,
}
}
網路請求
$.NSURLSession.sharedSession.dataTaskWithURLCompletionHandler(
$.NSURL.URLWithString('https://www.baidu.com'),
(data, resp) => {
console.log(resp.statusCode)
console.log(
ObjC.unwrap(
$.NSString.alloc.initWithDataEncoding(data, $.NSASCIIStringEncoding)
).split('\n')[0]
)
}
).resume
var urlStr = 'https://stackoverflow.com/'
var htmlStr = getHTMLSource(urlStr)
htmlStr.substring(0, 200)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function getHTMLSource(pURL) {
// @HTML @Web @URL @OBjC
/* Ver 1.0 2017-06-24
---------------------------------------------------------------------------------
PURPOSE: Get the HTML Source Code for the specified URL.
PARAMETERS:
• pURL | string | URL of Page to get HTML of
RETURNS: | string | html source of web page
REF:
1. AppleScript Handler by @ccstone
on getUrlSource:urlStr
—————————————————————————————————————————————————————————————————————————————————
*/
// framework "Foundation" is built-in to JXA, and is referenced by the "$"
var nsURL = $.NSURL.URLWithString(pURL)
var nsHTML = $.NSData.dataWithContentsOfURL(nsURL)
var nsHTMLStr = $.NSString.alloc.initWithDataEncoding(
nsHTML,
$.NSUTF8StringEncoding
)
var htmlStr = ObjC.unwrap(nsHTMLStr)
return htmlStr
}
準備
在 safari 中除錯指令碼
-
開啟 safari 的
開發
、macbook
和自動顯示
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-vcex6PUn-1612578945245)(https://files.catbox.moe/mnbhtd.png)] -
開啟指令碼編輯器,輸入以下程式碼,執行之後,safari 會出現 debug 視窗
var x = false
debugger
if (x) {
console.log("Why isn't this being called?")
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-I7y8R0FO-1612578945256)(https://files.catbox.moe/x8e9ek.png)]
在終端中執行 JavaScript
輸入命令,進入互動編輯介面
osascript -i -l JavaScript
執行 js 檔案
/usr/bin/env osascript -l JavaScript "/Users/path/demo.js"
詞典
System Events
keystrokes
Complete list of AppleScript key codes
var se = Application('System Events')
se.keyCode(36) // Press Enter
se.keystroke('hello') //模擬輸入子母,僅支援英文,中文亂碼
se.keystroke('a', { using: 'command down' }) // Press Cmd-A
se.keystroke(' ', { using: ['option down', 'command down'] }) // Press Opt-Cmd-Space
剪貼簿
//--- GET A REF TO CURRENT APP WITH STD ADDITONS ---
var app = Application.currentApplication()
app.includeStandardAdditions = true
//--- Set the Clipboard so we can test for no selection ---
app.setTheClipboardTo('[NONE]')
//--- Get the Text on the Clipboard ---
var clipStr = app.theClipboard()
console.log(clipStr) //[NONE]
網路連線
se.connect(se.networkPreferences.services["WI-FI"])
選單欄
>> var ts=se.processes.byName('終端')
>> ts.menuBars[0].menuBarItems.length //頂部選單欄的數量
=> 7
>> ts.menuBars[0].menuBarItems[0].name()
=> "Apple"
>> ts.menuBars[0].menuBarItems[1].name()
=> "終端"
>> ts.menuBars[0].menuBarItems[1].menus[0].menuItems[0].click()
=> Application("System Events").applicationProcesses.byName("Terminal").menuBars.at(0).menuBarItems.byName("終端").menus.byName("終端").menuItems.byName("關於終端")
var fileMenu=ts.menuBars[0].menuBarItems[1]
var items = fileMenu.menus[0].menuItems(); // Note ()
for (var item, i = 0, j = items.length; i < j; i++) {
item = items[i];
if (item.enabled() &&
/^Log out myname/.test(item.title()) { // 去除分割線
item.click();
}
}
操作 UI 元素
finder
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-EPaxdFRJ-1612578945258)(https://files.catbox.moe/b4lt2z.png)]
se.processes['Finder'].windows[0].toolbars[0].actions['AXShowMenu'].perform()
dock
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-Vu4PFzlS-1612578945264)(https://files.catbox.moe/70leav.png)]
se.processes['Dock'].lists[0].uiElements[0].actions['AXShowMenu'].perform()
// 無效的索引
se.processes['Dock'].lists[0].uiElements[0].menus[0].menuItems()
// Click 'Hide'
se.processes['Dock'].lists[0].uiElements[0].menus[0].menuItems['Hide'].click()
使用ui Brower
檢視 ui 結構
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-5uMHr3aa-1612578945265)(https://files.catbox.moe/b47ud5.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-DbOU7GRX-1612578945279)(https://90-cucc-dd.tv002.com/down/06b059c90d8adb8cd456801bceb1ce48/UI%20Browser%203.0.2_www.imacapp.cn.dmg?cts=wt-f-D101A206A254A90F6f8d6&ctp=101A206A254A90&ctt=1612261806&limit=1&spd=550000&ctk=06b059c90d8adb8cd456801bceb1ce48&chk=3a187f04c101eb2d6b935e2c1693a6a6-4151190&mtd=1)]
se.processes['WeChat'].windows[0].splitterGroups[0].splitterGroups[0].scrollAreas[0].tables[0].rows[33].uiElements.uiElements[0].staticTexts[0].value()
=>["你的身份證號碼是多少"]
// 獲取並設定輸入框的值
se.processes['WeChat'].windows[0].splitterGroups[0].splitterGroups[0].scrollAreas[1].textAreas[0].value='input'
// 獲取使用者名稱
>> se.processes['WeChat'].windows[0].splitterGroups[0].scrollAreas[0].tables[0].rows[10].uiElements[0].staticTexts[0].value()
=> "小七"
Application('Safari').activate()
// Access the Safari process of System Events
var SystemEvents = Application('System Events')
var Safari = SystemEvents.processes['Safari']
// Call the click command, sending an array of coordinates [x, y]
Safari.click({ at: [46, 50] })
物件結構
- Application
- properties
- appearancePreferences
- applicationSupportFolder[應用資原始檔夾路徑]
- applicationsFolder
- currentDesktop
- currentScreenSaver
- [螢幕保護程式:用法 se.start(se.currentScreenSaver)]
- currentUser
- Application(“System Events”).users.byName(“luomingyang”)
- desktopFolder
- desktopPicturesFolder
- folderActionScriptsFolder
- [指令碼存放資料夾:’/Users/luomingyang/Library/Scripts/Folder Action Scripts’]
- fontsFolder
- frontmost[是否位於最前面]
- networkPreferences
- se.connect(se.networkPreferences.services[“WI-FI”])
- elements
- alias
- applicationProces
- commands
- properties
luo-mingyang:JS&JSA luomingyang$ osascript -i -l JavaScript
>> let mail=Application('Mail')
=> undefined
>> mail.name()
=> "郵件"
>> mail.version()
=> "14.0"
>> mail.frontmost()
=> false
>> mail.windows.length
=> 1
>> mail.windows[0].name()
=> "垃圾郵件 — QQ"
>> mail.windows[0].id()
=> 11098
>> mail.windows[0].index()
=> 1
>> mail.windows[0].bounds()
=> {"x":172, "y":26, "width":1011, "height":1025}
>> mail.windows[0].closeable()
=> true
>> mail.windows[0].miniaturized()
=> false
>> mail.windows[0].visible()
=> true
日曆
元素結構
- Application
- calendars
- properties
- color(get/set)
- description(get/set)
- name(get/set)
- element
- event
- properties
- summary
- 主標題
- description
- endDate
- startDate
- alldayEvent
- status
- url
- location
- summary
- elements
- attendee
- email(get)
- displayName(get)
- participationStatus(get)[是否接受邀約]
- display alarm
- mail alarm
- open file alarm
- sound alarm
- attendee
- properties
- commands
- show()
- event
- properties
- documents
- windows
- calendars
code
>> let cal=Application('日曆')
=> undefined
>> cal.name()
=> "日曆"
>> cal.calendars.length
=> 15
>> cal.calendars[0].name()
=> "Demo"
>> cal.calendars[0].color()
=> [0.9893491864204407, 0.7216296792030334, 0.05394064262509346]
>> cal.calendars[0].writable()
=> true
>> cal.calendars[0].description()
=> "新日曆"
# 給第一個日曆的第一個物件新增郵箱
cal.calendars[0].events[0].attendees.push(cal.Attendee({email:'[email protected]'}))
# 建立日曆
cal.Calendar({name:'日曆'}).make()
# result:Application("日曆").calendars.byId("FA9B832C-E85F-4E67-B6AC-87D352DDF110")
# 定位日曆
cal.calendars.whose({name:'Demo'}).name()
=> ["Demo"]
# 對比dom元素
# document.getElementsByName
# set 日曆的屬性
>> cal.calendars[0].color=[0.5,0.5,0.5]
=> [0.5, 0.5, 0.5]
# 刪除日曆
cal.delete(cal.calendars[1])
example
簡單的示意
Mail = Application('Mail')
Mail.id()
Mail.running()
全域性屬性
JavaScript for Automation 是 JavaScript 的宿主環境,它添加了以下的全域性屬性
- Automation
- Application
- Library
- Path
- Progress
- ObjectSpecifier
- delay
- console.log
- ObjC
- Red
- $
訪問 Application
- 通過名稱
Application('Mail')
- 通過 id
Application('com.apple.mail')
- 當前應用
Application.currentApplication()
獲得應用的資訊
- 訪問屬性
Mail.name
- 訪問元素
Mail.outgoingMessages[0]
- 執行方法
Mail.open()
- 新建物件
Mail.OutgoingMessage(...)
- 列印物件的屬性資訊
var kme = Application('Keyboard Maestro Engine')
var kmeProps = kme.properties()
console.log(JSON.stringify(kmeProps, null, '\t'))
// {
// "frontmost": false,
// "pcls": "application",
// "name": "Keyboard Maestro Engine",
// "version": "9.2"
// }
- 檢視物件的類
//"application"```
### 獲得和設定屬性(Properties)
`subject = Mail.inbox.messages[0].subject()`
`Mail.outgoingMessages[0].subject = 'Hello world'`
### 元素索引
index
```js
window = Mail.windows.at(0)
window = Mail.windows[0]
name
window = Mail.windows.byName('New Message')
window = Mail.windows['New Message']
id
window = Mail.windows.byId(412)
過濾器
過濾名稱
{ name: 'JavaScript for Automation' }
>> se.processes.byName('Adobe Photoshop 2021').menuBars[0].menuBarItems[2].menus[0].menuItems.whose({name:'開啟...'}).length
=> 1
內容過濾
whose({name:{_contains:'開啟'}}).name()
>> se.processes.byName('Adobe Photoshop 2021').menuBars[0].menuBarItems[2].menus[0].menuItems.whose({name:{_contains:'開啟'}}).name()
=> ["開啟...", "開啟為智慧物件...", "最近開啟檔案"]
開頭結尾過濾
{ name: {_beginsWith: 'JavaScript' } }
示意
Mail = Application('Mail')
Mail.inbox.messages.whose({
\_or: [
{ subject: { _contains: "JavaScript" } },
{ subject: { _contains: "Automation" } }
]
})"
執行命令
message.open()
Safari.doJavaScript('alert("Hello world")', {
in: Safari.windows[0].tabs[0],
})
建立物件
message = Mail.OutgoingMessage().make()
新建郵件,並輸入描述
message = Mail.OutgoingMessage({
subject: 'Hello world',
visible: true,
})
Mail.outgoingMessages.push(message)
para = TextEdit.Paragraph({}, 'Some text')
TextEdit.documents[0].paragraphs.push(para)
message = Mail.OutgoingMessage().make()
message.subject = 'Hello world'
彈窗
app = Application.currentApplication()
app.includeStandardAdditions = true
app.say('Hello world')
app.displayDialog('Please enter your email address', {
withTitle: 'Email',
defaultAnswer: '[email protected]',
})
谷歌瀏覽器開啟文件
// grab the chrome object
var chrome = Application('Google Chrome')
// create a new chrome window
var window = chrome.Window().make()
// set the links you want to open
var links = [
'https://gmail.com',
'https://soundcloud.com',
'https://twitter.com',
'https://aws.amazon.com',
]
// loop through the links
for (i = 0; i < links.length; i++) {
// set the url for the tab
window.tabs[i].url = links[i]
// check to see if we have a next tab
if (links[i + 1] !== undefined) {
// create a new tab and push it to the window
window.tabs.push(new chrome.Tab())
}
}
對谷歌瀏覽器的第一個視窗的第一個標籤頁執行 js
chrome.windows[0].tabs[0].execute({javascript:'alert(1)'})
// grab the chrome object
var chrome = Application('Google Chrome')
// grab all of the chrome windows
var windows = chrome.windows
// loop through the chrome windows
for (i = 0; i < windows.length; i++) {
// grab all of the tabs for the window
var tabs = windows[i].tabs
// loop through the tabs
for (j = 0; j < tabs.length; j++) {
// grab the url for the tab
var url = tabs[j].url()
// check to see if the tab url matches "soundcloud.com"
if (url.match(/soundcloud.com/)) {
// in the tab lets execute some javascript
tabs[j].execute({
// select the .playControl div and click it!
javascript: "document.querySelector('.playControl').click();",
})
}
}
}
自動點選 youtube 網頁的按鈕
var chrome = Application("Google Chrome");
var windows = chrome.windows;
for(i = 0; i < windows.length; i++){
var tabs = windows[i].tabs;
for(j = 0; j < tabs.length; j++){
var url = tabs[j].url();
if(url.match(/youtube.com/)){
tabs[j].execute({ javascript: "document.querySelector('.ytp-play-button').click();" });
}
}
日曆
尋找日程並新增受邀人的名稱
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-10mFJ5ew-1612578945286)(https://files.catbox.moe/j5757e.png)]
var app = Application.currentApplication()
var Calendar = Application('Calendar')
var projectCalendars = Calendar.calendars.whose({ name: '日曆' }) //在日曆中查詢名稱為'日曆'的日曆表
var projectCalendar = projectCalendars[0] //專案日曆為第一個結果
var events = projectCalendar.events.whose({ summary: 'JXA' }) //選中描述日程中
var event = events[0] //第一個日程
var attendee = Calendar.Attendee({ email: '[email protected]' }) //新增參會者
event.attendees.push(attendee)
Calendar.reloadCalendars()
在 mac 上新建日曆
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-y2nqTa3x-1612578945288)(https://files.catbox.moe/i00awm.png)]
var Calendar = Application('Calendar')
var calendarName = '日曆'
var calendarDescription = '新日曆'
var newCalendar = Calendar.Calendar({
name: calendarName,
description: calendarDescription,
}).make()
在今天新建日程
var app = Application.currentApplication()
app.includeStandardAdditions = true
var Calendar = Application('Calendar')
var eventStart = app.currentDate()
eventStart = eventStart
eventStart.setDate(eventStart.getDate())
eventStart.setHours(15)
eventStart.setMinutes(0)
eventStart.setSeconds(0)
var eventEnd = new Date(eventStart.getTime())
eventEnd.setHours(16)
var projectCalendars = Calendar.calendars.whose({ name: '日曆' })
//Use the first result that matches name search
var projectCalendar = projectCalendars[0]
var event = Calendar.Event({
summary: 'Important Meeting!',
startDate: eventStart,
endDate: eventEnd,
})
projectCalendar.events.push(event)
新增 1 小時後的日程
// Sometimes on first run it fails with invalid index, but unable to repeat
var app = Application.currentApplication()
app.includeStandardAdditions = true
Requires "Demo" calendar with events
//Create new event starting now and ending in one hour
//Set Calendar to be the Application Calendar
var Calendar = Application('Calendar')
//Set name of Calendar you are using
var calendarUsed = '日曆'
//use Demo as the used Calendar by searching for it.
var projectCalendars = Calendar.calendars.whose({ name: calendarUsed })
//Use the first result that matches name search
var projectCalendar = projectCalendars[0]
//You need the time now as an object
var timeNow = new Date()
//inAnHour as well
var inAnHour = new Date()
//Create Date object one hour ago for search criteria
inAnHour.setHours(timeNow.getHours() + 1)
//Create new event with values from pervious event and end time of timeNow
newEvent = Calendar.Event({
summary: 'Demo Event',
startDate: timeNow,
endDate: inAnHour,
})
//Fails with invalid index on first run.
projectCalendar.events.push(newEvent)
刪除日曆中特定項
Requires "Demo" calendar with events
//Creates new Event with same startDate as current event and endDate as Now
// Deletes the event it replaces
// Add standard library for dialog boxes
var app = Application.currentApplication()
app.includeStandardAdditions = true
//Set Calendar to be the Application Calendar
var Calendar = Application('Calendar')
//Set name of Calendar you are using
var calendarUsed = 'Demo'
//use Demo as the used Calendar by searching for it.
var projectCalendars = Calendar.calendars.whose({ name: calendarUsed })
//Use the first result that matches name search
var projectCalendar = projectCalendars[0]
var testEvents = projectCalendar.events.whose({ summary: 'Demo Event' })
//set recentEvent to be the first in the results of search.
//TODO deal with more than one search result
var testEvent = testEvents[0]
eventId = testEvent.id()
//Delete the event with the old end time.
Calendar.delete(projectCalendar.events.byId(eventId))
展示特定日程
var Calendar = Application('Calendar')
var projectCalendars = Calendar.calendars.whose({ name: '日曆' })
var projectCalendar = projectCalendars[0]
var events = projectCalendar.events.whose({ summary: 'Important Meeting!' })
var event = events[0]
event.show()
檢視最近的日程
//Event elements can be added, properties cannot be changed
Requires "Demo" calendar with events
//Set Calendar to be the Application Calendar
var Calendar = Application('Calendar')
//use Demo as the used Calendar by searching for it.
var projectCalendars = Calendar.calendars.whose({ name: 'Demo' })
//Use the first result that matches name search
var projectCalendar = projectCalendars[0]
//new keyword makes Date object
var timeNow = new Date()
//Creation of a new event with same start and end time as the pulled event
newEvent = Calendar.Event({
summary: 'TEST',
startDate: timeNow,
endDate: timeNow,
})
// Push the event to the calendar
projectCalendar.events.push(newEvent)
//Choose which event I will edit. In this case first one. Could also be .last or array value []
var event = projectCalendar.events.last()
// attendee is made as a function. Class Event contains element attendeess, which is why I can add(push) attendees.
var attendee = Calendar.Attendee({ email: '[email protected]' })
event.attendees.push(attendee)
event.attendees[0]()
event.startDate()
//new Event(myEvent)
//projectCalendar.events.length;
日曆基礎資訊
//array of object's enumerable string properties
Object.keys(Application('Calendar').calendars[0].events)
//Result : ["0", "1", "2", "3", "4"]
//Get name of calendar 12
Application('Calendar').calendars[0].name()
//Result: "Demo"
//Summary is the name of the event
Application('Calendar').calendars[0].events.summary()
//Result: ["Important Meeting!", "Important Meeting!", "TEST", "Important Meeting!", "Important Meeting!"]
郵件
Mail = Application('Mail')
Mail.name()
Mail.name
Mail.outgoingMessages[0]
//Mail.accounts[0].mailboxes[8].messages[0]()
//Mail.accounts[0].mailboxes[8]()
Mail.activate() //bring Mail to foreground
Mail.accounts['iCloud'].checkForNewMail()
// show latest message in inbox
var latestMsg = Mail.inbox.messages[0]
Mail.open(latestMsg)
keynote
啟動 keynote 並新建特定主體和尺寸的 PPT
Keynote = Application('Keynote')
Keynote.launch()
//document class, make it a method, and add make verb at the end.
//newDoc = Keynote.Document().make()
//can create with properties set as record
doc = Keynote.Document({
documentTheme: Keynote.themes['基本顏色'],
width: 1024,
height: 768,
autoPlay: true,
autoLoop: true,
autoRestart: true,
maximumIdleDuration: 3,
}).make()
文字編輯
在文字編輯器的第一個視窗輸入文字
TextEdit = Application('TextEdit')
para = TextEdit.Paragraph({}, 'Some text')
TextEdit.documents[0].paragraphs.push(para)
互動提示框
// Include Apple's UI elements
var app = Application.currentApplication()
app.includeStandardAdditions = true
var response = app.displayDialog("What's your name?", {
defaultAnswer: '',
withIcon: 'note',
buttons: ['Cancel', 'Continue'],
defaultButton: 'Continue',
})
// Result: {"buttonReturned":"Continue", "textReturned":"Jen"}
app.displayDialog('Hello, ' + response.textReturned + '.')
// Maybe from JXA cookbook
;(function () {
'use strict'
// GENERIC FUNCTIONS ------------------------------------------------------
// doesFileExist :: String -> Bool
function doesFileExist(strPath) {
var error = $()
return (
$.NSFileManager.defaultManager.attributesOfItemAtPathError(
$(strPath).stringByStandardizingPath,
error
),
error.code === undefined
)
}
// lines :: String -> [String]
function lines(s) {
return s.split(/[\r\n]/)
}
// readFile :: FilePath -> maybe String
function readFile(strPath) {
var error = $(),
str = ObjC.unwrap(
$.NSString.stringWithContentsOfFileEncodingError(
$(strPath).stringByStandardizingPath,
$.NSUTF8StringEncoding,
error
)
),
blnValid = typeof error.code !== 'string'
return {
nothing: !blnValid,
just: blnValid ? str : undefined,
error: blnValid ? '' : error.code,
}
}
// show :: a -> String
function show(x) {
return JSON.stringify(x, null, 2)
}
// TEST -------------------------------------------------------------------
var strPath = '/Users/luomingyang/Desktop/test.txt'
return doesFileExist(strPath)
? (function () {
var dctMaybe = readFile(strPath)
return dctMaybe.nothing ? dctMaybe.error : show(lines(dctMaybe.just))
})()
: 'File not found:\n\t' + strPath
})()
檔案選擇框
var app = Application.currentApplication()
app.includeStandardAdditions = true
var file = app.chooseFile({
ofType: 'txt',
withPrompt: 'Please select a text file to read:',
})
本地檔案 IO
開啟檔案
app = Application.currentApplication()
app.includeStandardAdditions = true
app.openLocation('smb://xxxxxxxx')
讀取 plist 檔案
var app = Application.currentApplication()
app.includeStandardAdditions = true
var SystemEvents = Application('System Events')
var cats = SystemEvents.propertyListFiles
.byName(
'/Users/luomingyang/Luo_MingYang/Application/Alfred/Alfred.alfredpreferences/workflows/user.workflow.2AEFDEB9-2892-417C-A47C-3DEF9135C8A0/info.plist'
)
.contents.value()['uidata']
var res = JSON.stringify(cats)
console.log(res)
var app = Application.currentApplication()
app.includeStandardAdditions = true
function writeTextToFile(text, file, overwriteExistingContent) {
try {
// Convert the file to a string
var fileString = file.toString()
// Open the file for writing
var openedFile = app.openForAccess(Path(fileString), {
writePermission: true,
})
// Clear the file if content should be overwritten
if (overwriteExistingContent) {
app.setEof(openedFile, { to: 0 })
}
// Write the new content to the file
app.write(text, { to: openedFile, startingAt: app.getEof(openedFile) })
// Close the file
app.closeAccess(openedFile)
// Return a boolean indicating that writing was successful
return true
} catch (error) {
try {
// Close the file
app.closeAccess(file)
} catch (error) {
// Report the error is closing failed
console.log(`Couldn't close file: ${error}`)
}
// Return a boolean indicating that writing was successful
return false
}
}
writeTextToFile('test', '/Users/luomingyang/Desktop/test.txt', false) //false 追加,true:覆蓋原有內容
檢視當前 osacript 路徑
var app = Application.currentApplication()
app.includeStandardAdditions = true
thePath = app.pathTo(this)
// Path("/usr/bin/osascript")
路徑的轉換
// Looking at the properties of diskItems
var Finder = Application('Finder')
var SystemEvents = Application('System Events')
var sourcePathStr = '~/Demo/sampleDir'
var expandedSourcePathStr = $(sourcePathStr).stringByStandardizingPath.js
//Result: "/Users/adkj/Demo/sampleDir"
// function sourceFolder
var sourceFolder = SystemEvents.aliases.byName(expandedSourcePathStr)
//Result: Application("System Events").aliases.byName("/Users/adkj/Demo/sampleDir")
//function container
var container = sourceFolder.container()
//Result: Application("System Events").folders.byName("Macintosh HD:Users:adkj:Demo")
// String containerPath
var containerPath = container.path()
//Result: "Macintosh HD:Users:adkj:Demo"
// Create an array of items functions to be processed
var items = SystemEvents.aliases.byName(sourcePathStr).diskItems
items[1]()
//Result: Path("/Users/adkj/Demo/sampleDir/samplefile.rtf")
items[1].class()
//Result: "file"
items[1].name()
//Result: "samplefile.rtf"
items[0].path()
//Result: "Macintosh HD:Users:adkj:Demo:sampleDir:.DS_Store";
檔案增刪
var app = Application.currentApplication()
app.includeStandardAdditions = true
var Finder = Application('Finder')
var SystemEvents = Application('System Events')
// Ask a folder to process
var sourcePathStr = '~/Demo/sampleDir'
//Expands ~ to be full user path
var expandedSourcePathStr = $(sourcePathStr).stringByStandardizingPath.js
// Allows for running the script multiple times
var duplicateAvoider = Date.now().toString()
// function sourceFolder
var sourceFolder = SystemEvents.aliases.byName(expandedSourcePathStr)
//Result: Application("System Events").aliases.byName("/Users/adkj/Demo/sampleDir")
// Create the destination folder
//function container
var container = sourceFolder.container()
//Result: Application("System Events").folders.byName("Macintosh HD:Users:adkj:Demo")
// String containerPath
var containerPath = container.path()
//Result: "Macintosh HD:Users:adkj:Demo"
// .make is function/method from Standard Suite
var destinationFolder = Finder.make({
new: 'folder',
at: containerPath,
withProperties: {
name: sourceFolder.name() + ' - New',
},
})
// Create an array of items functions to be processed
var items = SystemEvents.aliases.byName(sourcePathStr).diskItems
items[0]()
//Result: Path("/Users/adkj/Demo/sampleDir/samplefile.rtf")
items[0].class()
//Result: "file"
items[0].name()
//Result: "samplefile.rtf"
items[1].path()
//Result: "Macintosh HD:Users:adkj:Demo:sampleDir:.DS_Store"
//Finder.move("Macintosh HD:Users:adkj:Demo:sampleDir:samplefile.rtf", { to: "Macintosh HD:Users:adkj:Demo:sampleDir - New" });
reveal 資料夾
// Script to Open a Finder Window with a folder and bring to the front
var Finder = Application('Finder')
var demoDir = '~/Desktop'
//Resolves the ~ to be the home directory path
var fullDemoDir = $(demoDir).stringByStandardizingPath.js
//Opens Finder window containing ~/Desktop
// Brings Path object into view
Finder.reveal(Path(fullDemoDir))
//Brings finder to front
Finder.activate()
等價於
var Finder = Application('Finder')
Finder.reveal(Path('/Users/luomingyang/Desktop'))
Finder.activate()
語音反饋
// Loads Apple's UI elements and others.
app = Application.currentApplication()
app.includeStandardAdditions = true
dialogResult = app.displayDialog('Please enter your name', {
withTitle: 'Greetings',
defaultAnswer: '',
buttons: ['Goodbye', 'Hello'],
defaultButton: 2,
})
if (dialogResult.buttonReturned == 'Hello') {
app.say('Hello ' + dialogResult.textReturned)
} else {
app.say('Goodbye' + dialogResult.textReturned)
}
執行 shell 命令
;(() => {
'use strict'
// evalAS :: String -> IO String
const evalAS = (s) => {
const a = Application.currentApplication(),
sa = ((a.includeStandardAdditions = true), a)
return sa.doShellScript(
['osascript -l AppleScript <<OSA_END 2>/dev/null']
.concat([s])
.concat('OSA_END')
.join('\n')
)
}
return evalAS('say "hello world"')
})()
cocoa
當有應用啟動時,則顯示彈窗hello app.name
var me = Application.currentApplication()
me.includeStandardAdditions = true
ObjC.import('Cocoa')
ObjC.registerSubclass({
name: 'MainController',
methods: {
'appDidLaunch:': {
types: ['void', ['id']],
implementation: function (notification) {
var appName = notification.userInfo.objectForKey('NSApplicationName').js
me.activate()
me.displayAlert(`Hello, ${appName}!`, { message: 'Nice to meet you.' })
Application(appName).activate()
},
},
'screensaverDidStop:': {
types: ['void', ['id']],
implementation: function (notification) {
me.activate()
me.displayAlert('Goodbye, screensaver!', {
message: 'It was nice knowing you.',
})
},
},
},
})
var controller = $.MainController.new
$.NSWorkspace.sharedWorkspace.notificationCenter.addObserverSelectorNameObject(
controller,
'appDidLaunch:',
$.NSWorkspaceDidLaunchApplicationNotification,
undefined
)
$.NSDistributedNotificationCenter.defaultCenter.addObserverSelectorNameObject(
controller,
'screensaverDidStop:',
'com.apple.screensaver.didstop',
undefined
)
移動滑鼠到螢幕中央
ObjC.import('stdlib')
ObjC.import('CoreGraphics')
// Move mouse cursor to specified position
function moveMouse(x, y) {
var pos = $.CGPointMake(x, y)
var event = $.CGEventCreateMouseEvent(
null,
$.kCGEventMouseMoved,
pos,
$.kCGMouseButtonLeft
)
$.CGEventPost($.kCGHIDEventTap, event)
}
// Run script
function run() {
var app = Application.currentApplication()
app.includeStandardAdditions = true
var win = app.windows[0],
bounds = win.properties().bounds
// Calculate centre of window
var x = Math.trunc(bounds.x + bounds.width / 2),
y = Math.trunc(bounds.y + bounds.height / 2)
console.log('centre = ' + x + 'x' + y)
moveMouse(x, y)
}
滑鼠點選特定座標點
;(() => {
// mouseclick(); // Clicks at mouse location
mouseclick(89, 74) // Clicks at coordinates {35, 60}
// relative to top-left of screen
})()
function mouseclick(x = null, y = null) {
ObjC.import('Cocoa')
const nil = $()
const mouseLoc = $.NSEvent.mouseLocation
const screenH = 1920
mouseLoc.y = screenH - mouseLoc.y
var coords = mouseLoc
if (x && y) coords = { x: x, y: y }
var mousedownevent = $.CGEventCreateMouseEvent(
nil,
$.kCGEventLeftMouseDown,
coords,
nil
)
var mouseupevent = $.CGEventCreateMouseEvent(
nil,
$.kCGEventLeftMouseUp,
coords,
nil
)
$.CGEventPost($.kCGHIDEventTap, mousedownevent) //滑鼠按下
$.CGEventPost($.kCGHIDEventTap, mouseupevent) //滑鼠鬆開
}
使用 C 建立應用
利用 JavaScript 構建 OSX 應用_hao741100265 的專欄-CSDN 部落格
ObjC.import('Cocoa')
// 實際執行時指令碼會崩潰,原因未知
// 新增視窗的功能,有標題,關閉按鈕,最小化按鈕,|(=or)表示新增多個
var styleMask =
$.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask
var windowHeight = 85
var windowWidth = 600
var ctrlsHeight = 80
var minWidth = 400
var minHeight = 340
var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
$.NSMakeRect(0, 0, windowWidth, windowHeight),
styleMask,
$.NSBackingStoreBuffered,
false
)
window.center
window.title = 'Choose and Display Image'
window.makeKeyAndOrderFront(window)
參考文獻
OS X 10.11 官方文件 > [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-l3esg3K0-1612578945293)(https://files.catbox.moe/ct4ncl.png)] >Home · JXA-Cookbook/JXA-Cookbook Wiki > [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-1hHYOSkJ-1612578945295)(https://files.catbox.moe/4ktfms.png)] >[email protected] | Wiki > User Interaction with Files and Folders · JXA-Cookbook/JXA-Cookbook Wiki > Angelina Fabbro - OSX Automation Using JavaScript - JSConfUY 2015 - YouTube > 在 JXA 中使用 Objective C(ObjC)·JXA-Cookbook / JXA-Cookbook Wiki