Merge pull request #384 from danielpunkass/safari-extension
Safari extension for "Subscribe to Feed"
This commit is contained in:
commit
86b1aef07f
|
@ -7,6 +7,12 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
6581C73820CED60100F4AD34 /* SafariExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */; };
|
||||||
|
6581C73A20CED60100F4AD34 /* SafariExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */; };
|
||||||
|
6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; };
|
||||||
|
6581C74020CED60100F4AD34 /* evergreen-subscribe-to-feed.js in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73F20CED60100F4AD34 /* evergreen-subscribe-to-feed.js */; };
|
||||||
|
6581C74220CED60100F4AD34 /* ToolbarItemIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */; };
|
||||||
|
6581C74620CED60100F4AD34 /* Subscribe to Feed.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D617E2029031C009BC708 /* AppDelegate.swift */; };
|
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D617E2029031C009BC708 /* AppDelegate.swift */; };
|
||||||
840D61812029031C009BC708 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61802029031C009BC708 /* MasterViewController.swift */; };
|
840D61812029031C009BC708 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61802029031C009BC708 /* MasterViewController.swift */; };
|
||||||
840D61832029031C009BC708 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61822029031C009BC708 /* DetailViewController.swift */; };
|
840D61832029031C009BC708 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61822029031C009BC708 /* DetailViewController.swift */; };
|
||||||
|
@ -204,6 +210,13 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
6581C74420CED60100F4AD34 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 849C64581ED37A5D003D8FC0 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 6581C73220CED60000F4AD34;
|
||||||
|
remoteInfo = "Subscribe to Feed";
|
||||||
|
};
|
||||||
840D61922029031D009BC708 /* PBXContainerItemProxy */ = {
|
840D61922029031D009BC708 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 849C64581ED37A5D003D8FC0 /* Project object */;
|
containerPortal = 849C64581ED37A5D003D8FC0 /* Project object */;
|
||||||
|
@ -431,6 +444,17 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
6581C75720CED60100F4AD34 /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
6581C74620CED60100F4AD34 /* Subscribe to Feed.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
84B06F681ED37B9000F0B54B /* Embed Frameworks */ = {
|
84B06F681ED37B9000F0B54B /* Embed Frameworks */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -477,6 +501,15 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
6581C73420CED60100F4AD34 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||||
|
6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionHandler.swift; sourceTree = "<group>"; };
|
||||||
|
6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
6581C73C20CED60100F4AD34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SafariExtensionViewController.xib; sourceTree = "<group>"; };
|
||||||
|
6581C73E20CED60100F4AD34 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
6581C73F20CED60100F4AD34 /* evergreen-subscribe-to-feed.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "evergreen-subscribe-to-feed.js"; sourceTree = "<group>"; };
|
||||||
|
6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ToolbarItemIcon.pdf; sourceTree = "<group>"; };
|
||||||
|
6581C74320CED60100F4AD34 /* Subscribe_to_Feed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Subscribe_to_Feed.entitlements; sourceTree = "<group>"; };
|
||||||
8403E75A201C4A79007F7246 /* FeedListKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListKeyboardDelegate.swift; sourceTree = "<group>"; };
|
8403E75A201C4A79007F7246 /* FeedListKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListKeyboardDelegate.swift; sourceTree = "<group>"; };
|
||||||
840D617C2029031C009BC708 /* Evergreen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Evergreen.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
840D617C2029031C009BC708 /* Evergreen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Evergreen.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
840D617E2029031C009BC708 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
840D617E2029031C009BC708 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
@ -672,6 +705,14 @@
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
6581C73020CED60000F4AD34 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6581C73520CED60100F4AD34 /* Cocoa.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
840D61792029031C009BC708 /* Frameworks */ = {
|
840D61792029031C009BC708 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -719,6 +760,20 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
6581C73620CED60100F4AD34 /* Safari Extension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */,
|
||||||
|
6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */,
|
||||||
|
6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */,
|
||||||
|
6581C73E20CED60100F4AD34 /* Info.plist */,
|
||||||
|
6581C73F20CED60100F4AD34 /* evergreen-subscribe-to-feed.js */,
|
||||||
|
6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */,
|
||||||
|
6581C74320CED60100F4AD34 /* Subscribe_to_Feed.entitlements */,
|
||||||
|
);
|
||||||
|
path = "Safari Extension";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
840D617D2029031C009BC708 /* Evergreen-iOS */ = {
|
840D617D2029031C009BC708 /* Evergreen-iOS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1090,6 +1145,7 @@
|
||||||
840D617D2029031C009BC708 /* Evergreen-iOS */,
|
840D617D2029031C009BC708 /* Evergreen-iOS */,
|
||||||
840D61942029031D009BC708 /* Evergreen-iOSTests */,
|
840D61942029031D009BC708 /* Evergreen-iOSTests */,
|
||||||
840D619F2029031E009BC708 /* Evergreen-iOSUITests */,
|
840D619F2029031E009BC708 /* Evergreen-iOSUITests */,
|
||||||
|
6581C73620CED60100F4AD34 /* Safari Extension */,
|
||||||
84FB9A2C1EDCD6A4003D53B9 /* Frameworks */,
|
84FB9A2C1EDCD6A4003D53B9 /* Frameworks */,
|
||||||
849C64741ED37A5D003D8FC0 /* EvergreenTests */,
|
849C64741ED37A5D003D8FC0 /* EvergreenTests */,
|
||||||
D5907CDA2002F084005947E5 /* xcconfig */,
|
D5907CDA2002F084005947E5 /* xcconfig */,
|
||||||
|
@ -1114,6 +1170,7 @@
|
||||||
840D617C2029031C009BC708 /* Evergreen.app */,
|
840D617C2029031C009BC708 /* Evergreen.app */,
|
||||||
840D61912029031D009BC708 /* Evergreen-iOSTests.xctest */,
|
840D61912029031D009BC708 /* Evergreen-iOSTests.xctest */,
|
||||||
840D619C2029031D009BC708 /* Evergreen-iOSUITests.xctest */,
|
840D619C2029031D009BC708 /* Evergreen-iOSUITests.xctest */,
|
||||||
|
6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1278,6 +1335,7 @@
|
||||||
children = (
|
children = (
|
||||||
847752FE2008879500D93690 /* CoreServices.framework */,
|
847752FE2008879500D93690 /* CoreServices.framework */,
|
||||||
84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */,
|
84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */,
|
||||||
|
6581C73420CED60100F4AD34 /* Cocoa.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
path = Evergreen/Extensions;
|
path = Evergreen/Extensions;
|
||||||
|
@ -1358,6 +1416,23 @@
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
6581C73220CED60000F4AD34 /* Subscribe to Feed */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 6581C75620CED60100F4AD34 /* Build configuration list for PBXNativeTarget "Subscribe to Feed" */;
|
||||||
|
buildPhases = (
|
||||||
|
6581C72F20CED60000F4AD34 /* Sources */,
|
||||||
|
6581C73020CED60000F4AD34 /* Frameworks */,
|
||||||
|
6581C73120CED60000F4AD34 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Subscribe to Feed";
|
||||||
|
productName = "Subscribe to Feed";
|
||||||
|
productReference = 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
840D617B2029031C009BC708 /* Evergreen-iOS */ = {
|
840D617B2029031C009BC708 /* Evergreen-iOS */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 840D61A32029031E009BC708 /* Build configuration list for PBXNativeTarget "Evergreen-iOS" */;
|
buildConfigurationList = 840D61A32029031E009BC708 /* Build configuration list for PBXNativeTarget "Evergreen-iOS" */;
|
||||||
|
@ -1420,6 +1495,7 @@
|
||||||
849C645E1ED37A5D003D8FC0 /* Resources */,
|
849C645E1ED37A5D003D8FC0 /* Resources */,
|
||||||
84C987A52000AC9E0066B150 /* ShellScript */,
|
84C987A52000AC9E0066B150 /* ShellScript */,
|
||||||
84B06F681ED37B9000F0B54B /* Embed Frameworks */,
|
84B06F681ED37B9000F0B54B /* Embed Frameworks */,
|
||||||
|
6581C75720CED60100F4AD34 /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -1553,6 +1629,7 @@
|
||||||
840D617B2029031C009BC708 /* Evergreen-iOS */,
|
840D617B2029031C009BC708 /* Evergreen-iOS */,
|
||||||
840D61902029031D009BC708 /* Evergreen-iOSTests */,
|
840D61902029031D009BC708 /* Evergreen-iOSTests */,
|
||||||
840D619B2029031D009BC708 /* Evergreen-iOSUITests */,
|
840D619B2029031D009BC708 /* Evergreen-iOSUITests */,
|
||||||
|
6581C73220CED60000F4AD34 /* Subscribe to Feed */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
@ -1708,6 +1785,16 @@
|
||||||
/* End PBXReferenceProxy section */
|
/* End PBXReferenceProxy section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
6581C73120CED60000F4AD34 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6581C74220CED60100F4AD34 /* ToolbarItemIcon.pdf in Resources */,
|
||||||
|
6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */,
|
||||||
|
6581C74020CED60100F4AD34 /* evergreen-subscribe-to-feed.js in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
840D617A2029031C009BC708 /* Resources */ = {
|
840D617A2029031C009BC708 /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -1785,6 +1872,15 @@
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
6581C72F20CED60000F4AD34 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6581C73A20CED60100F4AD34 /* SafariExtensionViewController.swift in Sources */,
|
||||||
|
6581C73820CED60100F4AD34 /* SafariExtensionHandler.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
840D61782029031C009BC708 /* Sources */ = {
|
840D61782029031C009BC708 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -1961,6 +2057,11 @@
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
6581C74520CED60100F4AD34 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 6581C73220CED60000F4AD34 /* Subscribe to Feed */;
|
||||||
|
targetProxy = 6581C74420CED60100F4AD34 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
840D61932029031D009BC708 /* PBXTargetDependency */ = {
|
840D61932029031D009BC708 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 840D617B2029031C009BC708 /* Evergreen-iOS */;
|
target = 840D617B2029031C009BC708 /* Evergreen-iOS */;
|
||||||
|
@ -2019,6 +2120,14 @@
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
|
6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
6581C73C20CED60100F4AD34 /* Base */,
|
||||||
|
);
|
||||||
|
name = SafariExtensionViewController.xib;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
840D61842029031C009BC708 /* Main.storyboard */ = {
|
840D61842029031C009BC708 /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -2095,6 +2204,30 @@
|
||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
6581C74720CED60100F4AD34 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = D5907CE02002F0FA005947E5 /* Evergreen_target.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Safari Extension/Subscribe_to_Feed.entitlements";
|
||||||
|
INFOPLIST_FILE = "Safari Extension/Info.plist";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.ranchero.Evergreen.Subscribe-to-Feed";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
6581C74820CED60100F4AD34 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = D5907CE02002F0FA005947E5 /* Evergreen_target.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Safari Extension/Subscribe_to_Feed.entitlements";
|
||||||
|
INFOPLIST_FILE = "Safari Extension/Info.plist";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.ranchero.Evergreen.Subscribe-to-Feed";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
840D61A42029031E009BC708 /* Debug */ = {
|
840D61A42029031E009BC708 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -2545,6 +2678,15 @@
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
6581C75620CED60100F4AD34 /* Build configuration list for PBXNativeTarget "Subscribe to Feed" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
6581C74720CED60100F4AD34 /* Debug */,
|
||||||
|
6581C74820CED60100F4AD34 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
840D61A32029031E009BC708 /* Build configuration list for PBXNativeTarget "Evergreen-iOS" */ = {
|
840D61A32029031E009BC708 /* Build configuration list for PBXNativeTarget "Evergreen-iOS" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="SafariExtensionViewController" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="view" destination="c22-O7-iKe" id="vwT-Xx-Aiz"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="c22-O7-iKe">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="330" height="119"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4Iy-aV-wGF">
|
||||||
|
<rect key="frame" x="103" y="82" width="124" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Subscribe to Feed" id="2Ec-kd-q2K">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Subscribe to Feed</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XPC!</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.Safari.extension</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).SafariExtensionHandler</string>
|
||||||
|
<key>SFSafariContentScript</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>Script</key>
|
||||||
|
<string>evergreen-subscribe-to-feed.js</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>SFSafariToolbarItem</key>
|
||||||
|
<dict>
|
||||||
|
<key>Action</key>
|
||||||
|
<string>Command</string>
|
||||||
|
<key>Identifier</key>
|
||||||
|
<string>Button</string>
|
||||||
|
<key>Image</key>
|
||||||
|
<string>ToolbarItemIcon.pdf</string>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>Subscribe to Feed</string>
|
||||||
|
</dict>
|
||||||
|
<key>SFSafariWebsiteAccess</key>
|
||||||
|
<dict>
|
||||||
|
<key>Allowed Domains</key>
|
||||||
|
<array>
|
||||||
|
<string>*.*</string>
|
||||||
|
</array>
|
||||||
|
<key>Level</key>
|
||||||
|
<string>All</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2018 Ranchero Software. All rights reserved.</string>
|
||||||
|
<key>NSHumanReadableDescription</key>
|
||||||
|
<string>This extension adds a Safari toolbar button for easily subscribing to the syndication feed for the current page.</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,116 @@
|
||||||
|
//
|
||||||
|
// SafariExtensionHandler.swift
|
||||||
|
// Subscribe to Feed
|
||||||
|
//
|
||||||
|
// Created by Daniel Jalkut on 6/11/18.
|
||||||
|
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SafariServices
|
||||||
|
|
||||||
|
class SafariExtensionHandler: SFSafariExtensionHandler {
|
||||||
|
|
||||||
|
// Safari App Extensions don't support any reasonable means of detecting whether a
|
||||||
|
// specific Safari page was loaded with the benefit of the extension's injected
|
||||||
|
// JavaScript. For this reason a condition can easily be reached where the toolbar
|
||||||
|
// icon is active for a page, but the expected supporting code is not loaded into
|
||||||
|
// the page. To detect this and disable our icon, we use a kind of "ping" trick
|
||||||
|
// to verify whether our code is installed.
|
||||||
|
|
||||||
|
// I tried to use a NSMapTable from String to the closure directly, but Swift
|
||||||
|
// complains that the object as to be a class type.
|
||||||
|
typealias ValidationHandler = (Bool, String) -> Void
|
||||||
|
class ValidationWrapper {
|
||||||
|
let validationHandler: ValidationHandler
|
||||||
|
|
||||||
|
init(validationHandler: @escaping ValidationHandler) {
|
||||||
|
self.validationHandler = validationHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps from UUID to a validation wrapper
|
||||||
|
static var gPingPongMap = Dictionary<String, ValidationWrapper>()
|
||||||
|
|
||||||
|
// Bottleneck for calling through to a validation handler we have saved, and removing it from the list.
|
||||||
|
static func callValidationHandler(forHandlerID handlerID: String, withShouldValidate shouldValidate: Bool) {
|
||||||
|
if let validationWrapper = gPingPongMap[handlerID] {
|
||||||
|
validationWrapper.validationHandler(shouldValidate, "")
|
||||||
|
gPingPongMap.removeValue(forKey: handlerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validationQueue = DispatchQueue(label: "Toolbar Validation")
|
||||||
|
|
||||||
|
override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String : Any]?) {
|
||||||
|
if (messageName == "subscribeToFeed") {
|
||||||
|
if let feedURLString = userInfo?["url"] as? String {
|
||||||
|
if let feedURL = URL(string: feedURLString) {
|
||||||
|
// We could do something more Evergreen-specific like invoke an app-specific scheme
|
||||||
|
// to subscribe in the app. For starters we just let NSWorkspace open the URL in the
|
||||||
|
// default "feed:" URL scheme handler.
|
||||||
|
NSWorkspace.shared.open(feedURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (messageName == "pong") {
|
||||||
|
if let validationIDString = userInfo?["validationID"] as? String {
|
||||||
|
// Should we validate the button?
|
||||||
|
let shouldValidate = userInfo?["shouldValidate"] as? Bool ?? false
|
||||||
|
SafariExtensionHandler.callValidationHandler(forHandlerID: validationIDString, withShouldValidate:shouldValidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func toolbarItemClicked(in window: SFSafariWindow) {
|
||||||
|
window.getActiveTab { (activeTab) in
|
||||||
|
activeTab?.getActivePage(completionHandler: { (activePage) in
|
||||||
|
activePage?.dispatchMessageToScript(withName: "toolbarButtonClicked", userInfo: nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
|
||||||
|
|
||||||
|
let uniqueValidationID = NSUUID().uuidString
|
||||||
|
|
||||||
|
// Save it right away to eliminate any doubt of whether the handler gets deallocated while
|
||||||
|
// we are waiting for a callback from the getActiveTab or getActivatePage methods below.
|
||||||
|
let validationWrapper = ValidationWrapper(validationHandler: validationHandler)
|
||||||
|
SafariExtensionHandler.gPingPongMap[uniqueValidationID] = validationWrapper
|
||||||
|
|
||||||
|
validationQueue.sync {
|
||||||
|
// See comments above where gPingPongMap is declared. Upon being asked to validate the
|
||||||
|
// toolbar icon for a specific page, we save the validationHandler and postpone calling
|
||||||
|
// it until we have either received a response from our installed JavaScript, or until
|
||||||
|
// a timeout period has elapsed
|
||||||
|
window.getActiveTab { (activeTab) in
|
||||||
|
guard let activeTab = activeTab else {
|
||||||
|
SafariExtensionHandler.callValidationHandler(forHandlerID: uniqueValidationID, withShouldValidate:false);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTab.getActivePage { (activePage) in
|
||||||
|
guard let activePage = activePage else {
|
||||||
|
SafariExtensionHandler.callValidationHandler(forHandlerID: uniqueValidationID, withShouldValidate:false);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
activePage.getPropertiesWithCompletionHandler { (pageProperties) in
|
||||||
|
if let isActive = pageProperties?.isActive {
|
||||||
|
if isActive {
|
||||||
|
// Capture the uniqueValidationID to ensure it doesn't change out from under us on a future call
|
||||||
|
activePage.dispatchMessageToScript(withName: "ping", userInfo: ["validationID": uniqueValidationID])
|
||||||
|
|
||||||
|
let pongTimeoutInNanoseconds = Int(NSEC_PER_SEC / UInt64(1))
|
||||||
|
let timeoutDeadline = DispatchTime.now() + DispatchTimeInterval.nanoseconds(pongTimeoutInNanoseconds)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: timeoutDeadline, execute: { [timedOutValidationID = uniqueValidationID] in
|
||||||
|
SafariExtensionHandler.callValidationHandler(forHandlerID: timedOutValidationID, withShouldValidate:false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// SafariExtensionViewController.swift
|
||||||
|
// Subscribe to Feed
|
||||||
|
//
|
||||||
|
// Created by Daniel Jalkut on 6/11/18.
|
||||||
|
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SafariServices
|
||||||
|
|
||||||
|
class SafariExtensionViewController: SFSafariExtensionViewController {
|
||||||
|
|
||||||
|
// This would be the place to handle a popover that could, for example, list the possibly multiple feeds offered by a site.
|
||||||
|
static let shared: SafariExtensionViewController = {
|
||||||
|
let shared = SafariExtensionViewController()
|
||||||
|
shared.preferredContentSize = NSSize(width:320, height:240)
|
||||||
|
return shared
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,124 @@
|
||||||
|
// Prevent injecting the JavaScript in IFRAMES, and from acting before Safari is ready...
|
||||||
|
if ((window.top === window) && (typeof safari != 'undefined') && (document.location != null)) {
|
||||||
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
|
if (window.top === window)
|
||||||
|
{
|
||||||
|
var thisPageLinkObjects = null;
|
||||||
|
|
||||||
|
// I convert the native "link" node into an object that I can pass out to the global page
|
||||||
|
function objectFromLink(theLink)
|
||||||
|
{
|
||||||
|
var linkObject = new Object();
|
||||||
|
|
||||||
|
linkObject.href = theLink.href;
|
||||||
|
linkObject.type = theLink.type;
|
||||||
|
linkObject.title = theLink.title;
|
||||||
|
|
||||||
|
return linkObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some sites will list feeds with inappropriate or at least less-than-ideal information
|
||||||
|
// in the MIME type attribute. We cover some edge cases here that allow to be passed through,
|
||||||
|
// where they will successfully open as "feed://" URLs in the browser.
|
||||||
|
function isValidFeedLink(theLink)
|
||||||
|
{
|
||||||
|
var isValid = false;
|
||||||
|
|
||||||
|
switch (theLink.type)
|
||||||
|
{
|
||||||
|
case "application/atom+xml":
|
||||||
|
case "application/x.atom+xml":
|
||||||
|
case "application/rss+xml":
|
||||||
|
// These types do not require other criteria.
|
||||||
|
isValid = (theLink.href != null);
|
||||||
|
|
||||||
|
case "text/xml":
|
||||||
|
case "application/rdf+xml":
|
||||||
|
// These types require a title that has "RSS" in it.
|
||||||
|
if (theLink.title && theLink.title.search(/RSS/i) != -1)
|
||||||
|
{
|
||||||
|
isValid = (theLink.href != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanForSyndicationFeeds()
|
||||||
|
{
|
||||||
|
// In case we don't find any, we establish that we have at least tried by setting the
|
||||||
|
// variables to empty instead of null.
|
||||||
|
thisPageLinkObjects = []
|
||||||
|
|
||||||
|
thisPageLinks = document.getElementsByTagName("link");
|
||||||
|
|
||||||
|
for (thisLinkIndex = 0; thisLinkIndex < thisPageLinks.length; thisLinkIndex++)
|
||||||
|
{
|
||||||
|
var thisLink = thisPageLinks[thisLinkIndex];
|
||||||
|
var thisLinkRel = thisLink.getAttribute("rel");
|
||||||
|
if (thisLinkRel == "alternate")
|
||||||
|
{
|
||||||
|
if (isValidFeedLink(thisLink))
|
||||||
|
{
|
||||||
|
thisPageLinkObjects.push(objectFromLink(thisLink));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribeToFeed(theFeed)
|
||||||
|
{
|
||||||
|
// Convert the URL to a feed:// scheme because Safari
|
||||||
|
// will refuse to load e.g. a feed that is listed merely
|
||||||
|
// as "text/xml". We do some preflighting of the link rel
|
||||||
|
// in the PageLoadEnd.js so we can be more confident it's a
|
||||||
|
// good feed: URL.
|
||||||
|
var feedURL = theFeed.href;
|
||||||
|
if (feedURL.match(/^http[s]?:\/\//))
|
||||||
|
{
|
||||||
|
feedURL = feedURL.replace(/^http[s]?:\/\//, "feed://");
|
||||||
|
}
|
||||||
|
else if (feedURL.match(/^feed:/) == false)
|
||||||
|
{
|
||||||
|
feedURL = "feed:" + feedURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
safari.extension.dispatchMessage("subscribeToFeed", { "url": feedURL });
|
||||||
|
}
|
||||||
|
|
||||||
|
safari.self.addEventListener("message", function(event)
|
||||||
|
{
|
||||||
|
if (event.name === "toolbarButtonClicked")
|
||||||
|
{
|
||||||
|
// Workaround Radar #31182842, in which residual copies of our
|
||||||
|
// app extension may remain loaded in context of pages in Safari,
|
||||||
|
// causing multiple responses to broadcast message about toolbar
|
||||||
|
// button being clicked. In the case of the "extra" injections,
|
||||||
|
// the document location is null, so we can avoid doing on anything.
|
||||||
|
if ((document.location != null) && (thisPageLinkObjects.length > 0))
|
||||||
|
{
|
||||||
|
feedToOpen = thisPageLinkObjects[0];
|
||||||
|
subscribeToFeed(feedToOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.name === "ping")
|
||||||
|
{
|
||||||
|
// Just a hack to get the toolbar icon validation to work as expected.
|
||||||
|
// If we don't pong back, the extension knows we are not loaded in a page.
|
||||||
|
|
||||||
|
// There is a bug in Safari where the messageHandler is apparently held on to by Safari
|
||||||
|
// even after an extension is disabled. So an effort to "ping" an extension's scripts will
|
||||||
|
// succeed even if its been disabled and the page reloaded. Checking for the existance of
|
||||||
|
// document.location seems to ensure we have enough of a handle still on the document that
|
||||||
|
// we can do something useful with it.
|
||||||
|
var shouldValidate = (document.location != null) && (thisPageLinkObjects.length > 0);
|
||||||
|
|
||||||
|
// Pass back the same validationID we were handed so they can look up the correlated validationHandler
|
||||||
|
safari.extension.dispatchMessage("pong", { "validationID": event.message.validationID, "shouldValidate": shouldValidate });
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
scanForSyndicationFeeds();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue