Merge pull request #339 from olofhellman/master
Support for ‘permalink’ and ‘external url’ properties of 'article'
This commit is contained in:
commit
668f614aad
|
@ -171,6 +171,17 @@
|
||||||
D5D1751220020B980047B29D /* Evergreen.sdef in Resources */ = {isa = PBXBuildFile; fileRef = D5D175012002039D0047B29D /* Evergreen.sdef */; };
|
D5D1751220020B980047B29D /* Evergreen.sdef in Resources */ = {isa = PBXBuildFile; fileRef = D5D175012002039D0047B29D /* Evergreen.sdef */; };
|
||||||
D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */; };
|
D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */; };
|
||||||
D5E4CC64202C1AC1009B4FFC /* MainWindowController+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */; };
|
D5E4CC64202C1AC1009B4FFC /* MainWindowController+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */; };
|
||||||
|
D5E4CCB320300024009B4FFC /* uiScriptingTestSetup.applescript in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CCB220300024009B4FFC /* uiScriptingTestSetup.applescript */; };
|
||||||
|
D5E4CCB420300033009B4FFC /* uiScriptingTestSetup.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5E4CCB220300024009B4FFC /* uiScriptingTestSetup.applescript */; };
|
||||||
|
D5E4CCC520300537009B4FFC /* AppleScriptXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CCC3203004EA009B4FFC /* AppleScriptXCTestCase.swift */; };
|
||||||
|
D5E4CCD720303823009B4FFC /* selectAnArticle.applescript in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CCD520303823009B4FFC /* selectAnArticle.applescript */; };
|
||||||
|
D5E4CCD820303823009B4FFC /* selectAFeed.applescript in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CCD620303823009B4FFC /* selectAFeed.applescript */; };
|
||||||
|
D5E4CCDA2030382A009B4FFC /* establishMainWindowStartingState.applescript in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CCD92030382A009B4FFC /* establishMainWindowStartingState.applescript */; };
|
||||||
|
D5E4CCDB20303A52009B4FFC /* selectAFeed.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5E4CCD620303823009B4FFC /* selectAFeed.applescript */; };
|
||||||
|
D5E4CCDC20303A52009B4FFC /* selectAnArticle.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5E4CCD520303823009B4FFC /* selectAnArticle.applescript */; };
|
||||||
|
D5E4CCDD20303A59009B4FFC /* establishMainWindowStartingState.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5E4CCD92030382A009B4FFC /* establishMainWindowStartingState.applescript */; };
|
||||||
|
D5E4CCDE20303A66009B4FFC /* testCurrentArticleIsNil.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5E4CCD12030260E009B4FFC /* testCurrentArticleIsNil.applescript */; };
|
||||||
|
D5E4CCDF20303A66009B4FFC /* testURLsOfCurrentArticle.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5E4CCCF203025FF009B4FFC /* testURLsOfCurrentArticle.applescript */; };
|
||||||
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */; };
|
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */; };
|
||||||
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; };
|
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; };
|
||||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
|
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
|
||||||
|
@ -485,6 +496,12 @@
|
||||||
dstPath = TestScripts;
|
dstPath = TestScripts;
|
||||||
dstSubfolderSpec = 7;
|
dstSubfolderSpec = 7;
|
||||||
files = (
|
files = (
|
||||||
|
D5E4CCDE20303A66009B4FFC /* testCurrentArticleIsNil.applescript in CopyFiles */,
|
||||||
|
D5E4CCDF20303A66009B4FFC /* testURLsOfCurrentArticle.applescript in CopyFiles */,
|
||||||
|
D5E4CCDD20303A59009B4FFC /* establishMainWindowStartingState.applescript in CopyFiles */,
|
||||||
|
D5E4CCDB20303A52009B4FFC /* selectAFeed.applescript in CopyFiles */,
|
||||||
|
D5E4CCDC20303A52009B4FFC /* selectAnArticle.applescript in CopyFiles */,
|
||||||
|
D5E4CCB420300033009B4FFC /* uiScriptingTestSetup.applescript in CopyFiles */,
|
||||||
D5A267C220131BA000A8D3C0 /* testFeedOPML.applescript in CopyFiles */,
|
D5A267C220131BA000A8D3C0 /* testFeedOPML.applescript in CopyFiles */,
|
||||||
D5A2679D201313A200A8D3C0 /* testNameOfAuthors.applescript in CopyFiles */,
|
D5A2679D201313A200A8D3C0 /* testNameOfAuthors.applescript in CopyFiles */,
|
||||||
D5F4EDE920075C6700B9E363 /* testNameAndUrlOfEveryFeed.applescript in CopyFiles */,
|
D5F4EDE920075C6700B9E363 /* testNameAndUrlOfEveryFeed.applescript in CopyFiles */,
|
||||||
|
@ -664,6 +681,13 @@
|
||||||
D5D175012002039D0047B29D /* Evergreen.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = Evergreen.sdef; path = ../Resources/Evergreen.sdef; sourceTree = "<group>"; };
|
D5D175012002039D0047B29D /* Evergreen.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = Evergreen.sdef; path = ../Resources/Evergreen.sdef; sourceTree = "<group>"; };
|
||||||
D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Scriptability.swift"; sourceTree = "<group>"; };
|
D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Scriptability.swift"; sourceTree = "<group>"; };
|
||||||
D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainWindowController+Scriptability.swift"; sourceTree = "<group>"; };
|
D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainWindowController+Scriptability.swift"; sourceTree = "<group>"; };
|
||||||
|
D5E4CCB220300024009B4FFC /* uiScriptingTestSetup.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = uiScriptingTestSetup.applescript; sourceTree = "<group>"; };
|
||||||
|
D5E4CCC3203004EA009B4FFC /* AppleScriptXCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppleScriptXCTestCase.swift; path = EvergreenTests/ScriptingTests/AppleScriptXCTestCase.swift; sourceTree = SOURCE_ROOT; };
|
||||||
|
D5E4CCCF203025FF009B4FFC /* testURLsOfCurrentArticle.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testURLsOfCurrentArticle.applescript; sourceTree = "<group>"; };
|
||||||
|
D5E4CCD12030260E009B4FFC /* testCurrentArticleIsNil.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testCurrentArticleIsNil.applescript; sourceTree = "<group>"; };
|
||||||
|
D5E4CCD520303823009B4FFC /* selectAnArticle.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = selectAnArticle.applescript; sourceTree = "<group>"; };
|
||||||
|
D5E4CCD620303823009B4FFC /* selectAFeed.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = selectAFeed.applescript; sourceTree = "<group>"; };
|
||||||
|
D5E4CCD92030382A009B4FFC /* establishMainWindowStartingState.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = establishMainWindowStartingState.applescript; sourceTree = "<group>"; };
|
||||||
D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptingObject.swift; sourceTree = "<group>"; };
|
D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptingObject.swift; sourceTree = "<group>"; };
|
||||||
D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = "<group>"; };
|
D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = "<group>"; };
|
||||||
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
|
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -1281,6 +1305,7 @@
|
||||||
children = (
|
children = (
|
||||||
D5558FCF20021C590066386B /* scripts */,
|
D5558FCF20021C590066386B /* scripts */,
|
||||||
D5558FD22002245C0066386B /* ScriptingTests.swift */,
|
D5558FD22002245C0066386B /* ScriptingTests.swift */,
|
||||||
|
D5E4CCC3203004EA009B4FFC /* AppleScriptXCTestCase.swift */,
|
||||||
);
|
);
|
||||||
path = ScriptingTests;
|
path = ScriptingTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1288,6 +1313,9 @@
|
||||||
D5558FCF20021C590066386B /* scripts */ = {
|
D5558FCF20021C590066386B /* scripts */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D5E4CCD92030382A009B4FFC /* establishMainWindowStartingState.applescript */,
|
||||||
|
D5E4CCD620303823009B4FFC /* selectAFeed.applescript */,
|
||||||
|
D5E4CCD520303823009B4FFC /* selectAnArticle.applescript */,
|
||||||
D5907C9D20023249005947E5 /* testGenericScript.applescript */,
|
D5907C9D20023249005947E5 /* testGenericScript.applescript */,
|
||||||
D5A267B220131B8300A8D3C0 /* testFeedOPML.applescript */,
|
D5A267B220131B8300A8D3C0 /* testFeedOPML.applescript */,
|
||||||
D5558FD1200223F60066386B /* testGetURL.applescript */,
|
D5558FD1200223F60066386B /* testGetURL.applescript */,
|
||||||
|
@ -1295,6 +1323,9 @@
|
||||||
D5A2679B201312F900A8D3C0 /* testNameOfAuthors.applescript */,
|
D5A2679B201312F900A8D3C0 /* testNameOfAuthors.applescript */,
|
||||||
D5F4EDD720075C1300B9E363 /* testNameOfEveryFolder.applescript */,
|
D5F4EDD720075C1300B9E363 /* testNameOfEveryFolder.applescript */,
|
||||||
D55373A02018797B006D8857 /* testTitleOfArticlesWhose.applescript */,
|
D55373A02018797B006D8857 /* testTitleOfArticlesWhose.applescript */,
|
||||||
|
D5E4CCD12030260E009B4FFC /* testCurrentArticleIsNil.applescript */,
|
||||||
|
D5E4CCCF203025FF009B4FFC /* testURLsOfCurrentArticle.applescript */,
|
||||||
|
D5E4CCB220300024009B4FFC /* uiScriptingTestSetup.applescript */,
|
||||||
);
|
);
|
||||||
path = scripts;
|
path = scripts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1952,10 +1983,15 @@
|
||||||
D5A267C120131B8300A8D3C0 /* testFeedOPML.applescript in Sources */,
|
D5A267C120131B8300A8D3C0 /* testFeedOPML.applescript in Sources */,
|
||||||
D5A2679C201312F900A8D3C0 /* testNameOfAuthors.applescript in Sources */,
|
D5A2679C201312F900A8D3C0 /* testNameOfAuthors.applescript in Sources */,
|
||||||
849C64761ED37A5D003D8FC0 /* EvergreenTests.swift in Sources */,
|
849C64761ED37A5D003D8FC0 /* EvergreenTests.swift in Sources */,
|
||||||
|
D5E4CCC520300537009B4FFC /* AppleScriptXCTestCase.swift in Sources */,
|
||||||
|
D5E4CCB320300024009B4FFC /* uiScriptingTestSetup.applescript in Sources */,
|
||||||
D5558FD32002245C0066386B /* ScriptingTests.swift in Sources */,
|
D5558FD32002245C0066386B /* ScriptingTests.swift in Sources */,
|
||||||
|
D5E4CCDA2030382A009B4FFC /* establishMainWindowStartingState.applescript in Sources */,
|
||||||
D5F4EDE820075C1800B9E363 /* testNameAndUrlOfEveryFeed.applescript in Sources */,
|
D5F4EDE820075C1800B9E363 /* testNameAndUrlOfEveryFeed.applescript in Sources */,
|
||||||
D5907CA0200232A1005947E5 /* testGenericScript.applescript in Sources */,
|
D5907CA0200232A1005947E5 /* testGenericScript.applescript in Sources */,
|
||||||
D5F4EDE620075C1300B9E363 /* testNameOfEveryFolder.applescript in Sources */,
|
D5F4EDE620075C1300B9E363 /* testNameOfEveryFolder.applescript in Sources */,
|
||||||
|
D5E4CCD720303823009B4FFC /* selectAnArticle.applescript in Sources */,
|
||||||
|
D5E4CCD820303823009B4FFC /* selectAFeed.applescript in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -139,9 +139,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||||
feedIconDownloader = FeedIconDownloader(imageDownloader: imageDownloader)
|
feedIconDownloader = FeedIconDownloader(imageDownloader: imageDownloader)
|
||||||
|
|
||||||
updateSortMenuItems()
|
updateSortMenuItems()
|
||||||
createAndShowMainWindow()
|
createAndShowMainWindow()
|
||||||
|
installAppleEventHandlers()
|
||||||
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(AppDelegate.getURL(_:_:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||||
|
@ -185,25 +184,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||||
saveState()
|
saveState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: GetURL Apple Event
|
|
||||||
|
|
||||||
@objc func getURL(_ event: NSAppleEventDescriptor, _ withReplyEvent: NSAppleEventDescriptor) {
|
|
||||||
|
|
||||||
guard let urlString = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let normalizedURLString = urlString.rs_normalizedURL()
|
|
||||||
if !normalizedURLString.rs_stringMayBeURL() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
|
|
||||||
self.addFeed(normalizedURLString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
@objc func unreadCountDidChange(_ note: Notification) {
|
@objc func unreadCountDidChange(_ note: Notification) {
|
||||||
|
|
|
@ -6,6 +6,13 @@
|
||||||
<dictionary title="sa;lfsdf" xmlns:xi="http://www.w3.org/2003/XInclude">
|
<dictionary title="sa;lfsdf" xmlns:xi="http://www.w3.org/2003/XInclude">
|
||||||
|
|
||||||
<suite name="Standard Suite" code="core" description="Subset of the Standard Suite.">
|
<suite name="Standard Suite" code="core" description="Subset of the Standard Suite.">
|
||||||
|
<access-group identifier="com.ranchero.Evergreen" access="rw"/>
|
||||||
|
<command name="exists" code="coredoex" description="Verify that an object exists.">
|
||||||
|
<cocoa class="Evergreen.EvergreenExistsCommand"/>
|
||||||
|
<direct-parameter type="any" requires-access="r" description="The object(s) to check."/>
|
||||||
|
<result type="boolean" description="Does the object(s) exist?"/>
|
||||||
|
</command>
|
||||||
|
|
||||||
<class name="application" code="capp" description="The application's top-level scripting object.">
|
<class name="application" code="capp" description="The application's top-level scripting object.">
|
||||||
<cocoa class="NSApplication"/>
|
<cocoa class="NSApplication"/>
|
||||||
<property name="name" code="pnam" type="text" access="r" description="The name of the application."/>
|
<property name="name" code="pnam" type="text" access="r" description="The name of the application."/>
|
||||||
|
@ -14,6 +21,7 @@
|
||||||
</suite>
|
</suite>
|
||||||
|
|
||||||
<suite name="Evergreen Suite" code="Geod" description="The Evergreen Application Suite.">
|
<suite name="Evergreen Suite" code="Geod" description="The Evergreen Application Suite.">
|
||||||
|
<access-group identifier="com.ranchero.Evergreen" access="rw"/>
|
||||||
<class name="application" code="capp"
|
<class name="application" code="capp"
|
||||||
description="Evergreen Application" inherits="application">
|
description="Evergreen Application" inherits="application">
|
||||||
|
|
||||||
|
@ -142,9 +150,15 @@
|
||||||
<property name="title" code="titl" type="text" access="r" description="The article title">
|
<property name="title" code="titl" type="text" access="r" description="The article title">
|
||||||
<cocoa key="title"/>
|
<cocoa key="title"/>
|
||||||
</property>
|
</property>
|
||||||
<property name="url" code="URL " type="text" access="r" description="url for the article">
|
<property name="url" code="URL " type="text" access="r" description="url for the article. This will be the permalink if available, or the external url">
|
||||||
<cocoa key="url"/>
|
<cocoa key="url"/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="external url" code="eURL" type="text" access="r" description="the external url for the article, if known">
|
||||||
|
<cocoa key="externalUrl"/>
|
||||||
|
</property>
|
||||||
|
<property name="permalink" code="pLnk" type="text" access="r" description="a permalink for the article, if known">
|
||||||
|
<cocoa key="permalink"/>
|
||||||
|
</property>
|
||||||
<property name="contents" code="Cnts" type="text" access="r" description="text of the article">
|
<property name="contents" code="Cnts" type="text" access="r" description="text of the article">
|
||||||
<cocoa key="contents"/>
|
<cocoa key="contents"/>
|
||||||
</property>
|
</property>
|
||||||
|
@ -182,9 +196,9 @@
|
||||||
|
|
||||||
</suite>
|
</suite>
|
||||||
|
|
||||||
<suite name="Internet Suite" code="GURL"
|
<suite name="Internet Suite" code="GURL" description="Standard Internet Suite.">
|
||||||
description="Standard Internet Suite.">
|
<access-group identifier="com.ranchero.Evergreen" access="rw"/>
|
||||||
|
|
||||||
<command name="open location" code="GURLGURL" description="opens the given url.">
|
<command name="open location" code="GURLGURL" description="opens the given url.">
|
||||||
<direct-parameter type="text"/>
|
<direct-parameter type="text"/>
|
||||||
</command>
|
</command>
|
||||||
|
|
|
@ -19,10 +19,60 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Data
|
import Data
|
||||||
|
|
||||||
|
protocol AppDelegateAppleEvents {
|
||||||
|
func installAppleEventHandlers()
|
||||||
|
func getURL(_ event: NSAppleEventDescriptor, _ withReplyEvent: NSAppleEventDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
protocol ScriptingAppDelegate {
|
protocol ScriptingAppDelegate {
|
||||||
var scriptingCurrentArticle: Article? {get}
|
var scriptingCurrentArticle: Article? {get}
|
||||||
var scriptingSelectedArticles: [Article] {get}
|
var scriptingSelectedArticles: [Article] {get}
|
||||||
var scriptingMainWindowController:ScriptingMainWindowController? {get}
|
var scriptingMainWindowController:ScriptingMainWindowController? {get}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AppDelegate : AppDelegateAppleEvents {
|
||||||
|
|
||||||
|
// MARK: GetURL Apple Event
|
||||||
|
|
||||||
|
func installAppleEventHandlers() {
|
||||||
|
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(AppDelegate.getURL(_:_:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func getURL(_ event: NSAppleEventDescriptor, _ withReplyEvent: NSAppleEventDescriptor) {
|
||||||
|
|
||||||
|
guard let urlString = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalizedURLString = urlString.rs_normalizedURL()
|
||||||
|
if !normalizedURLString.rs_stringMayBeURL() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
|
||||||
|
self.addFeed(normalizedURLString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EvergreenExistsCommand : NSExistsCommand {
|
||||||
|
|
||||||
|
// cocoa default behavior doesn't work here, because of cases where we define an object's property
|
||||||
|
// to be another object type. e.g., 'permalink of the current article' parses as
|
||||||
|
// <property> of <property> of <top level object>
|
||||||
|
// cocoa would send the top level object (the app) a doesExist message for a nested property, and
|
||||||
|
// it errors out because it doesn't know how to handle that
|
||||||
|
// What we do instead is simply see if the defaultImplementation errors, and if it does, the object
|
||||||
|
// must not exist. Otherwise, we return the result of the defaultImplementation
|
||||||
|
// The wrinkle is that it is possible that the direct object is a list, so we need to
|
||||||
|
// handle that case as well
|
||||||
|
|
||||||
|
override func performDefaultImplementation() -> Any? {
|
||||||
|
guard let result = super.performDefaultImplementation() else { return NSNumber(booleanLiteral:false) }
|
||||||
|
return result
|
||||||
|
// return NSNumber(booleanLiteral:true)
|
||||||
|
// scriptingContainer.handleDoObjectsExist(command:self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,20 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
||||||
// MARK: --- Scriptable properties ---
|
// MARK: --- Scriptable properties ---
|
||||||
|
|
||||||
@objc(url)
|
@objc(url)
|
||||||
var url:String {
|
var url:String? {
|
||||||
return article.url ?? ""
|
return article.url ?? article.externalURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc(permalink)
|
||||||
|
var permalink:String? {
|
||||||
|
return article.url
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(externalUrl)
|
||||||
|
var externalUrl:String? {
|
||||||
|
return article.externalURL
|
||||||
|
}
|
||||||
|
|
||||||
@objc(uniqueId)
|
@objc(uniqueId)
|
||||||
var uniqueId:String {
|
var uniqueId:String {
|
||||||
return article.uniqueID
|
return article.uniqueID
|
||||||
|
|
|
@ -12,9 +12,8 @@ protocol ScriptingObjectContainer: ScriptingObject {
|
||||||
var scriptingClassDescription:NSScriptClassDescription { get }
|
var scriptingClassDescription:NSScriptClassDescription { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension ScriptingObjectContainer {
|
extension ScriptingObjectContainer {
|
||||||
|
|
||||||
func makeFormNameScriptObjectSpecifier(forObject object:NamedScriptingObject) -> NSScriptObjectSpecifier? {
|
func makeFormNameScriptObjectSpecifier(forObject object:NamedScriptingObject) -> NSScriptObjectSpecifier? {
|
||||||
let containerClassDescription = self.scriptingClassDescription
|
let containerClassDescription = self.scriptingClassDescription
|
||||||
let containerScriptObjectSpecifier = self.objectSpecifier
|
let containerScriptObjectSpecifier = self.objectSpecifier
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// AppleScriptXCTestCase.swift
|
||||||
|
// EvergreenUITests
|
||||||
|
//
|
||||||
|
// Created by Olof Hellman on 2/10/18.
|
||||||
|
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class AppleScriptXCTestCase: XCTestCase {
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@function doIndividualScript
|
||||||
|
@param filename -- name of a .applescript (sans extention) in the test bundle's
|
||||||
|
Resources/TestScripts directory
|
||||||
|
@brief given a file, loads the script and runs it. Expects a result from running
|
||||||
|
the script of the form
|
||||||
|
{test_result:true, script_result:<anything>}
|
||||||
|
if the test_result is false or is missing, the test fails
|
||||||
|
@return the value of script_result, if any
|
||||||
|
*/
|
||||||
|
func doIndividualScript(filename:String) -> NSAppleEventDescriptor? {
|
||||||
|
var errorDict: NSDictionary? = nil
|
||||||
|
let testBundle = Bundle(for: type(of: self))
|
||||||
|
let url = testBundle.url(forResource:filename, withExtension:"applescript", subdirectory:"TestScripts")
|
||||||
|
guard let testScriptUrl = url else {
|
||||||
|
XCTFail("Failed Getting script URL")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let testScript = NSAppleScript(contentsOf: testScriptUrl, error: &errorDict) else {
|
||||||
|
print ("error is \(String(describing: errorDict))")
|
||||||
|
XCTFail("Failed initializing NSAppleScript")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let scriptResult = testScript.executeAndReturnError(&errorDict)
|
||||||
|
if (errorDict != nil) {
|
||||||
|
print ("error is \(String(describing: errorDict))")
|
||||||
|
XCTFail("Failed executing script")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let usrfDictionary = scriptResult.usrfDictionary()
|
||||||
|
guard let testResult = usrfDictionary["test_result"] else {
|
||||||
|
XCTFail("test script didn't return test result in usrf")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testResult.booleanValue != true) {
|
||||||
|
print("test_result was \(testResult)")
|
||||||
|
print("script_result was \(String(describing: usrfDictionary["script_result"]))")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssert(testResult.booleanValue == true, "test_result should be true")
|
||||||
|
return usrfDictionary["script_result"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class ScriptingTests: XCTestCase {
|
class ScriptingTests: AppleScriptXCTestCase {
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
@ -18,52 +18,6 @@ class ScriptingTests: XCTestCase {
|
||||||
super.tearDown()
|
super.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@function doIndividualScript
|
|
||||||
@param filename -- name of a .applescript (sans extention) in the test bundle's
|
|
||||||
Resources/TestScripts directory
|
|
||||||
@brief given a file, loads the script and runs it. Expects a result from running
|
|
||||||
the script of the form
|
|
||||||
{test_result:true, script_result:<anything>}
|
|
||||||
if the test_result is false or is missing, the test fails
|
|
||||||
@return the value of script_result, if any
|
|
||||||
*/
|
|
||||||
func doIndividualScript(filename:String) -> NSAppleEventDescriptor? {
|
|
||||||
var errorDict: NSDictionary? = nil
|
|
||||||
let testBundle = Bundle(for: type(of: self))
|
|
||||||
let url = testBundle.url(forResource:filename, withExtension:"applescript", subdirectory:"TestScripts")
|
|
||||||
guard let testScriptUrl = url else {
|
|
||||||
XCTFail("Failed Getting script URL")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let testScript = NSAppleScript(contentsOf: testScriptUrl, error: &errorDict) else {
|
|
||||||
XCTFail("Failed initializing NSAppleScript")
|
|
||||||
print ("error is \(String(describing: errorDict))")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let scriptResult = testScript.executeAndReturnError(&errorDict)
|
|
||||||
if (errorDict != nil) {
|
|
||||||
XCTFail("Failed executing script")
|
|
||||||
print ("error is \(String(describing: errorDict))")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let usrfDictionary = scriptResult.usrfDictionary()
|
|
||||||
guard let testResult = usrfDictionary["test_result"] else {
|
|
||||||
XCTFail("test script didn't return test result in usrf")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
XCTAssert(testResult.booleanValue == true, "test_result should be true")
|
|
||||||
if (testResult.booleanValue != true )
|
|
||||||
{
|
|
||||||
print("test_result was \(testResult)")
|
|
||||||
print("script_result was \(String(describing: usrfDictionary["script_result"]))")
|
|
||||||
}
|
|
||||||
return usrfDictionary["script_result"]
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@function testGenericScript
|
@function testGenericScript
|
||||||
|
@ -102,4 +56,32 @@ class ScriptingTests: XCTestCase {
|
||||||
_ = doIndividualScript(filename: "testTitleOfArticlesWhose")
|
_ = doIndividualScript(filename: "testTitleOfArticlesWhose")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doIndividualScriptWithExpectation(filename:String) {
|
||||||
|
let queue = DispatchQueue(label:"testQueue")
|
||||||
|
let scriptExpectation = self.expectation(description: filename+"expectation")
|
||||||
|
queue.async {
|
||||||
|
_ = self.doIndividualScript(filename:filename)
|
||||||
|
scriptExpectation.fulfill()
|
||||||
|
}
|
||||||
|
self.wait(for:[scriptExpectation], timeout:60)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@function testCurrentArticleScripts
|
||||||
|
@brief the pices of the test are broken up into smaller pieces because of the
|
||||||
|
way events are delivered to the app -- I tried one single script with all the
|
||||||
|
actions and the keystrokes aren't delivered to the app right away, so the ui
|
||||||
|
isn't updated in time for 'current article' to be set. But, breaking them up
|
||||||
|
in this way seems to work.
|
||||||
|
*/
|
||||||
|
func testCurrentArticleScripts() {
|
||||||
|
doIndividualScriptWithExpectation(filename: "uiScriptingTestSetup")
|
||||||
|
doIndividualScriptWithExpectation(filename: "establishMainWindowStartingState")
|
||||||
|
doIndividualScriptWithExpectation(filename: "selectAFeed")
|
||||||
|
doIndividualScriptWithExpectation(filename: "testCurrentArticleIsNil")
|
||||||
|
doIndividualScriptWithExpectation(filename: "selectAnArticle")
|
||||||
|
doIndividualScriptWithExpectation(filename: "testURLsOfCurrentArticle")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
property uparrowKeyCode : 126
|
||||||
|
property downarrowKeyCode : 125
|
||||||
|
property rightarrowKeyCode : 124
|
||||||
|
property leftarrowKeyCode : 123
|
||||||
|
|
||||||
|
to activateEvergreen()
|
||||||
|
tell application "Evergreen"
|
||||||
|
activate
|
||||||
|
end tell
|
||||||
|
end activateEvergreen
|
||||||
|
|
||||||
|
to multipleKeyCodes(keycode, numberOfKeys)
|
||||||
|
tell application "System Events"
|
||||||
|
tell process "Evergreen"
|
||||||
|
repeat numberOfKeys times
|
||||||
|
key code keycode
|
||||||
|
end repeat
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
end multipleKeyCodes
|
||||||
|
|
||||||
|
to establishMainWindowStartingState()
|
||||||
|
activateEvergreen()
|
||||||
|
multipleKeyCodes(downarrowKeyCode, 2)
|
||||||
|
multipleKeyCodes(rightarrowKeyCode, 2)
|
||||||
|
multipleKeyCodes(leftarrowKeyCode, 2)
|
||||||
|
multipleKeyCodes(uparrowKeyCode, 50)
|
||||||
|
end establishMainWindowStartingState
|
||||||
|
|
||||||
|
try
|
||||||
|
establishMainWindowStartingState()
|
||||||
|
-- hit the down arrow a few times to get into the feeds
|
||||||
|
on error message
|
||||||
|
return {test_result:false, script_result:message}
|
||||||
|
end try
|
||||||
|
|
||||||
|
return {test_result:true, script_result:"established starting state"}
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
|
||||||
|
property uparrowKeyCode : 126
|
||||||
|
property downarrowKeyCode : 125
|
||||||
|
property rightarrowKeyCode : 124
|
||||||
|
property leftarrowKeyCode : 123
|
||||||
|
|
||||||
|
to activateEvergreen()
|
||||||
|
tell application "Evergreen"
|
||||||
|
activate
|
||||||
|
end tell
|
||||||
|
end activateEvergreen
|
||||||
|
|
||||||
|
to multipleKeyCodes(keycode, numberOfKeys)
|
||||||
|
tell application "System Events"
|
||||||
|
tell process "Evergreen"
|
||||||
|
repeat numberOfKeys times
|
||||||
|
key code keycode
|
||||||
|
end repeat
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
end multipleKeyCodes
|
||||||
|
|
||||||
|
try
|
||||||
|
activateEvergreen()
|
||||||
|
multipleKeyCodes(downarrowKeyCode, 9)
|
||||||
|
multipleKeyCodes(uparrowKeyCode, 1)
|
||||||
|
on error message
|
||||||
|
return {test_result:false, script_result:message}
|
||||||
|
end try
|
||||||
|
|
||||||
|
return {test_result:true, script_result:"selected feed"}
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
|
||||||
|
property uparrowKeyCode : 126
|
||||||
|
property downarrowKeyCode : 125
|
||||||
|
property rightarrowKeyCode : 124
|
||||||
|
property leftarrowKeyCode : 123
|
||||||
|
|
||||||
|
to activateEvergreen()
|
||||||
|
tell application "Evergreen"
|
||||||
|
activate
|
||||||
|
end tell
|
||||||
|
end activateEvergreen
|
||||||
|
|
||||||
|
to multipleKeyCodes(keycode, numberOfKeys)
|
||||||
|
tell application "System Events"
|
||||||
|
tell process "Evergreen"
|
||||||
|
repeat numberOfKeys times
|
||||||
|
key code keycode
|
||||||
|
end repeat
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
end multipleKeyCodes
|
||||||
|
|
||||||
|
try
|
||||||
|
activateEvergreen()
|
||||||
|
multipleKeyCodes(rightarrowKeyCode, 1)
|
||||||
|
on error message
|
||||||
|
return {test_result:false, script_result:message}
|
||||||
|
end try
|
||||||
|
|
||||||
|
return {test_result:true, script_result:"selected an article"}
|
|
@ -0,0 +1,23 @@
|
||||||
|
-- this script tests that it is possible to get the url property of the current article
|
||||||
|
-- it uses system event accessibility scripting to set up the main window
|
||||||
|
-- one needs to authorize scripting accessibility control in the System Preferences'
|
||||||
|
-- Privacy and security pane
|
||||||
|
|
||||||
|
try
|
||||||
|
tell application "Evergreen"
|
||||||
|
set shouldBeMissingValue to current article
|
||||||
|
end tell
|
||||||
|
|
||||||
|
--verify that the current article is in fact 'missing vcalue'
|
||||||
|
if shouldBeMissingValue is missing value then
|
||||||
|
set the_message to "passed tests"
|
||||||
|
set the_result to true
|
||||||
|
else
|
||||||
|
set the_message to "expected current article to be 'missing value'"
|
||||||
|
set the_result to false
|
||||||
|
end if
|
||||||
|
on error message
|
||||||
|
return {test_result:false, script_result:message}
|
||||||
|
end try
|
||||||
|
|
||||||
|
return {test_result:the_result, script_result:the_message}
|
|
@ -0,0 +1,14 @@
|
||||||
|
-- this script tests that it is possible to get the url property of the current article
|
||||||
|
-- it uses system event accessibility scripting to set up the main window
|
||||||
|
-- one needs to authorize scripting accessibility control in the System Preferences'
|
||||||
|
-- Privacy and security pane
|
||||||
|
|
||||||
|
try
|
||||||
|
tell application "Evergreen"
|
||||||
|
{url, permalink, external url} of current article
|
||||||
|
end tell
|
||||||
|
on error message
|
||||||
|
return {test_result:false, script_result:message}
|
||||||
|
end try
|
||||||
|
|
||||||
|
return {test_result:true, script_result:"tests passed"}
|
|
@ -0,0 +1,18 @@
|
||||||
|
-- this script sets up Evergreen so it is ready to be tested
|
||||||
|
-- to get a current article
|
||||||
|
|
||||||
|
to activateEvergreen()
|
||||||
|
tell application "Evergreen"
|
||||||
|
activate
|
||||||
|
end tell
|
||||||
|
end activateEvergreen
|
||||||
|
|
||||||
|
tell application "System Events"
|
||||||
|
set isFrontmost to frontmost of process "Evergreen"
|
||||||
|
repeat while isFrontmost is false
|
||||||
|
my activateEvergreen()
|
||||||
|
set isFrontmost to frontmost of process "Evergreen"
|
||||||
|
end repeat
|
||||||
|
end tell
|
||||||
|
|
||||||
|
return {test_result:true, script_result:"finished"}
|
Loading…
Reference in New Issue