1. 程式人生 > 其它 >C++string容器-字串比較

C++string容器-字串比較

技術標籤:效率jsjavascriptguimacos

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()

weChat

使用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

mail

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
          • elements
            • attendee
              • email(get)
              • displayName(get)
              • participationStatus(get)[是否接受邀約]
            • display alarm
            • mail alarm
            • open file alarm
            • sound alarm
        • commands
          • show()
    • documents
    • windows

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