#!/usr/bin/env osascript -l JavaScript /** * @fileOverview Various system functions. */ ObjC.import('Foundation') ObjC.import('AppKit') var sys_events = Application("System Events") var workspace function setWorkspace(w){ workspace = w } function workspaceHome(){ return env("WORKSPACES_HOME") + "/" + workspace } function workspacesHome(){ return env("WORKSPACES_HOME") } function test(){ console.log("workspace name", workspace["name"]) } function sh(cmd) { var me = Application.currentApplication() me.includeStandardAdditions = true me.doShellScript(cmd) } // More keycodes can be added. Keycode reference: // http://www.codemacs.com/coding/applescript/applescript-key-codes-reference.8288271.htm var key_codes = { "→": 124, "←": 123, "↑": 126, "↓": 125, "⏎": 36 } var modifiers = { "⌘": "command down", "^": "control down", "⌥": "option down", "⇧": "shift down" } function running() { var apps = $.NSWorkspace.sharedWorkspace.runningApplications // Note these never take () unless they have arguments apps = ObjC.unwrap(apps) // Unwrap the NSArray instance to a normal JS array var app for (var i = 0, j = apps.length; i < j; i++) { app = apps[i] console.log(ObjC.unwrap(app.bundleIdentifier)) // Another option for comparison is to unwrap app.bundleIdentifier // ObjC.unwrap(app.bundleIdentifier) === 'org.whatever.Name' // Some applications do not have a bundleIdentifier as an NSString // if (typeof app.bundleIdentifier.isEqualToString === 'undefined') { // continue; // } // if (app.bundleIdentifier.isEqualToString('com.apple.iTunes')) { // isRunning = true; // break; // } } } function press(hotkey) { var using = []; while (hotkey.length > 1) { if (modifiers[hotkey[0]] == undefined) { throw new Error(hotkey[0] + " is not a recognized modifier key"); } using.push(modifiers[hotkey[0]]); hotkey = hotkey.slice(1); } if (key_codes[hotkey] != undefined) { sys_events.keyCode(key_codes[hotkey], {using: using}); } else { sys_events.keystroke(hotkey.toLowerCase(), {using: using}); } } function type(text) { for (var i=0; i < text.length; i++) { sys_events.keystroke(text[i]); } } function menu_item() { if (!arguments.length) return; var process = sys_events.processes.whose({"frontmost": true})[0]; var menu_bar = process.menuBars[0].menuBarItems[arguments[0]]; var menu_item = menu_bar; for (var i=1; i < arguments.length; i++) { menu_item = menu_item.menus[0].menuItems[arguments[i]]; } menu_item.click(); } function quit(name) { try{ Application(name).quit() }catch(err){} } function closeWindows(name){ var app = Application(name); for(var i = 0; i < app.windows().length; i++){ var win = app.windows.at(i); if(!win.selectedTab.busy()){ app.close(win, {saving:"no"}) } } } // function safariWindows(){ // windowIDs = [] // var app = Application("Safari") // for(var i = 0; i < app.windows().length; i++){ // var win = app.windows.at(i) // windowIDs.push(win.id()) // } // return windowIDs // } function title(app, win){ return Application(app).windows.byId(win).name() } // function needWindows(app, count, path){ // windowIDs = [] // var app = Application(app) // if(app.windows().length < count){ // needed = count - app.windows().length // console.log(needed) // for(var i = 0; i < needed; i++ ){ // app.open(path) // } // } // delay(1) // for(var i = 0; i < app.windows().length; i++){ // var win = app.windows.at(i) // windowIDs.push(win.id()) // } // return windowIDs // } function writeFile(pPathStr, pOutputStr) { //--- CONVERT TO NS STRING --- var nsStr = $.NSString.alloc.initWithUTF8String(pOutputStr) //--- EXPAND TILDE AND CONVERT TO NS PATH --- var nsPath = $(pPathStr).stringByStandardizingPath //--- WRITE TO FILE --- var successBool = nsStr.writeToFileAtomicallyEncodingError(nsPath, false, $.NSUTF8StringEncoding, null) if (!successBool) { throw new Error("function writeFile ERROR:\nWrite to File FAILED for:\n" + pPathStr) } return successBool } function writeJson(path, obj) { try{ var str = JSON.stringify(obj, null, 2) }catch(err){ return [false, "JSON error"]} try{ writeFile(path, str) }catch(err){ return [false, "file error"] } return [true, obj] } function store(k, v) { var path = workspacePropertiesFile() var result = readJson(path) var obj = {} if(result[0]){ obj = result[1] } obj[k] = v var result = writeJson(path, obj) var success = result[0] if(!success){ console.log("could not write JSON") return result } return [true, v] } function workspacePropertiesFile(){ return workspaceHome() + "/" + "properties.json" } /** @desc Reads objects from JSON file @param {String} key The key to get values from @returns {Array} */ function retrieve(k) { var path = workspacePropertiesFile() var result = readJson(path) var success = result[0] if(!success){ return result } var obj = result[1] var v = obj[k] if(v == undefined) return [false, "key error"] return [true, v] } function retrieve(k) { var path = workspacePropertiesFile() var result = readJson(path) var success = result[0] if(!success){ console.log("retrieve", result[1]) return result } var obj = result[1] var v = obj[k] if(v == undefined){ console.log("retrieve key error") return [false, "key error"] } return [true, v] } function readJson(path) { var data = readFile(path) if(!data[0]){ return [false, "file error"] } try{ var obj = JSON.parse(data[1]) }catch(err){ return [false, "JSON error"] } return [true, obj] } function readFile(file) { var app = Application.currentApplication() app.includeStandardAdditions = true // Convert the file to a string var fileString = file.toString() try{ var p = Path(fileString) }catch(err){ return [false, err] } try{ var str = app.read(p) }catch(err){ return [false, err] } // Read the file and return its contents return [true, str] } function toggleActiveWindowFullScreen(){ sys_events.keystroke('f', { using: ['control down','command down'] }); } /** * * Get environment variable by name * */ function env(name){ var env = $.NSProcessInfo.processInfo.environment // -[[NSProcessInfo processInfo] environment] env = ObjC.unwrap(env) for (var k in env) { if(k == name){ return ObjC.unwrap(env[k]) } } return undefined } /** * * Print available environment variables * */ function env_list(){ var env = $.NSProcessInfo.processInfo.environment // -[[NSProcessInfo processInfo] environment] env = ObjC.unwrap(env) for (var k in env) { console.log('"' + k + '": ' + ObjC.unwrap(env[k])) } } /** @desc Get list of workspace-related windows from file. If there are stored windows that do not exist, they will be removed. @param {String} appName @returns {Array} List of objects in the form {id:2445, } */ function existingWorkspaceWindows(appName) { if(appName == undefined){ console.log("no app name given") return [] } // console.log(appName) // appName = lowerCase(appName) var result = retrieve(appName) var success = result[0] var wlist = result[1] // console.log("window count", wlist.length) if(!success){ return [] } var changed = false for (var i = 0; i < wlist.length; i++) { var id = wlist[i]["id"] // console.log("window exist", id, windowExistWithId(appName, id)) if(!windowExistWithId(appName, id)){ wlist.splice(i, 1) changed = true } } if(changed){ store(appName, wlist) } return wlist } /** @desc Get list of workspace-related windows from file. If there are stored windows that do not exist, they will be removed. @param {String} appName @returns {Array} List of objects in the form {id:2445, } */ function workspaceWindows(appName) { if(appName == undefined){ console.log("no app name given") return [] } // console.log(appName) // appName = lowerCase(appName) var result = retrieve(appName) var success = result[0] var wlist = result[1] // console.log("window count", wlist.length) if(!success){ return [] } var changed = false for (var i = 0; i < wlist.length; i++) { var id = wlist[i]["id"] // console.log("window exist", id, windowExistWithId(appName, id)) if(!windowExistWithId(appName, id)){ wlist.splice(i, 1) changed = true } } if(changed){ store(appName, wlist) } return wlist } /** @param {String} appName @param {Integer} id @returns {Boolean} **/ function windowExistWithId(appName, id) { var wlist = windowIDs(appName) for (var i = 0; i < wlist.length; i++) { var wID = wlist[i] if(id == wID) return true } return false } /** @desc Get list of id's of Safari windows. @param {String} appName @returns {Array} Array of integers **/ function windowIDs(appName){ // console.log(appName) var app = Application(appName) // console.log(app) var wlist = [] for(var i = 0; i < app.windows().length; i++){ var win = app.windows.at(i) wlist.push(win.id()) } return wlist.sort(function(a, b){return a-b}) } /** @desc Get window id by index from list of all windows. @param {Integer} index may be -2, -1, 0, 1, 2, etc. @return {Integer} **/ function windowID(appName, index){ var app = Application(appName) var wlist = windowIDs(appName) if(index < 0){ index = wlist.length + index } return app.windows.byId(wlist[index]).id() } // cannot call safari.foo() here, because // somehow this library cannot import library safari in here function add(appName){ // var wlist = workspaceWindows2(appName) if(appName == "Safari"){ console.log("sys.add", appName) // safari.addWorkspaceWindow() } return false } function prompt(text, defaultAnswer) { var t = Application("Terminal") t.includeStandardAdditions = true var options = { defaultAnswer: defaultAnswer || '' } try { return t.displayDialog(text, options).textReturned } catch (e) { return defaultAnswer } }