Repository: matryer/xbar Branch: main Commit: d62423905899 Files: 708 Total size: 4.6 MB Directory structure: gitextract_oblxmil8/ ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ └── deploy-xbarappcom.yaml ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE.txt ├── README.md ├── app/ │ ├── .gitignore │ ├── README.md │ ├── app.go │ ├── build.sh │ ├── categories_service.go │ ├── categories_service_test.go │ ├── command_service.go │ ├── frontend/ │ │ ├── .gitignore │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── public/ │ │ │ └── index.html │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── App.svelte │ │ │ ├── Homepage.svelte │ │ │ ├── InstalledPluginView.svelte │ │ │ ├── PersonView.svelte │ │ │ ├── PluginView.svelte │ │ │ ├── PluginsList.svelte │ │ │ ├── backend/ │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ ├── elements/ │ │ │ │ ├── A.svelte │ │ │ │ ├── Breadcrumbs.svelte │ │ │ │ ├── Button.svelte │ │ │ │ ├── Duration.svelte │ │ │ │ ├── Error.svelte │ │ │ │ ├── KeyboardShortcuts.svelte │ │ │ │ ├── PluginCollection.svelte │ │ │ │ ├── PluginDetails.svelte │ │ │ │ ├── PluginSourceBrowser.svelte │ │ │ │ ├── Spinner.svelte │ │ │ │ ├── Switch.svelte │ │ │ │ ├── VariableInput.svelte │ │ │ │ └── Variables.svelte │ │ │ ├── main.js │ │ │ ├── pagedata.svelte │ │ │ ├── rpc.svelte │ │ │ ├── signals.svelte │ │ │ ├── styles.css │ │ │ └── waiters.svelte │ │ └── tailwind.config.js │ ├── go.mod │ ├── go.sum │ ├── incoming_urls.go │ ├── incoming_urls_test.go │ ├── main.go │ ├── menu_parser.go │ ├── menu_parser_test.go │ ├── package.json │ ├── package.sh │ ├── person_service.go │ ├── plugins_service.go │ ├── settings.go │ ├── settings_test.go │ ├── test.sh │ └── wails.json ├── archive/ │ └── bitbar/ │ ├── .gitignore │ ├── .gitmodules │ ├── .travis.yml │ ├── App/ │ │ ├── BitBar/ │ │ │ ├── App.xib │ │ │ ├── AppDelegate.m │ │ │ ├── Base.lproj/ │ │ │ │ └── MainMenu.xib │ │ │ ├── BitBar-Info.plist │ │ │ ├── CHANGELOG.md │ │ │ ├── ExecutablePlugin.h │ │ │ ├── ExecutablePlugin.m │ │ │ ├── HTMLPlugin.h │ │ │ ├── HTMLPlugin.m │ │ │ ├── Images.xcassets/ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── NSColor+Hex.h │ │ │ ├── NSColor+Hex.m │ │ │ ├── NSString+ANSI.h │ │ │ ├── NSString+ANSI.m │ │ │ ├── NSUserDefaults+Settings.h │ │ │ ├── NSUserDefaults+Settings.m │ │ │ ├── Plugin.h │ │ │ ├── Plugin.m │ │ │ ├── PluginManager.h │ │ │ ├── PluginManager.m │ │ │ └── incoming-url-tests.html │ │ ├── BitBar.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace/ │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata/ │ │ │ │ ├── BitBar.xccheckout │ │ │ │ ├── BitBar.xcscmblueprint │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ ├── BitBar.xcscheme │ │ │ └── BitBarDistro.xcscheme │ │ ├── BitBarTests/ │ │ │ ├── BitBarTests-Info.plist │ │ │ ├── PluginManager+Test.h │ │ │ ├── PluginManager+Test.m │ │ │ ├── PluginManagerTest.m │ │ │ ├── PluginTest.m │ │ │ └── TestPlugins/ │ │ │ ├── one.10s.sh │ │ │ ├── three.7d.sh │ │ │ └── two.5m.sh │ │ └── Vendor/ │ │ ├── AHProxySettings/ │ │ │ ├── .gitignore │ │ │ ├── AHProxyExampe/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Base.lproj/ │ │ │ │ │ └── MainMenu.xib │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── main.m │ │ │ ├── AHProxySettings/ │ │ │ │ ├── AHProxy.h │ │ │ │ ├── AHProxy.m │ │ │ │ ├── AHProxySettings.h │ │ │ │ ├── AHProxySettings.m │ │ │ │ ├── NSTask+useSystemProxies.h │ │ │ │ └── NSTask+useSystemProxies.m │ │ │ ├── AHProxySettings.podspec │ │ │ ├── AHProxySettings.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ ├── project.xcworkspace/ │ │ │ │ │ └── contents.xcworkspacedata │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ └── AHProxySettings.xcscheme │ │ │ ├── AHProxySettingsTests/ │ │ │ │ ├── AHProxySettingsTest.m │ │ │ │ └── Info.plist │ │ │ ├── LICENSE │ │ │ └── README.md │ │ ├── DateTools/ │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CREDITS.md │ │ │ ├── DateTools/ │ │ │ │ ├── DTConstants.h │ │ │ │ ├── DTConstants.m │ │ │ │ ├── DTError.h │ │ │ │ ├── DTError.m │ │ │ │ ├── DTTimePeriod.h │ │ │ │ ├── DTTimePeriod.m │ │ │ │ ├── DTTimePeriodChain.h │ │ │ │ ├── DTTimePeriodChain.m │ │ │ │ ├── DTTimePeriodCollection.h │ │ │ │ ├── DTTimePeriodCollection.m │ │ │ │ ├── DTTimePeriodGroup.h │ │ │ │ ├── DTTimePeriodGroup.m │ │ │ │ ├── DateTools.bundle/ │ │ │ │ │ ├── ar.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── bg.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── ca.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── cs.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── cy.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── da.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── de.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── en.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── es.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── eu.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── fi.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── fr.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── gre.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── gu.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── he.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── hi.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── hr.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── hu.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── id.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── is.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── it.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── ja.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── ko.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── lv.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── ms.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── nb.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── nl.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── pl.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── pt-PT.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── pt.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── ro.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── ru.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── sl.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── sv.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── th.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── tr.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── uk.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── vi.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ ├── zh-Hans.lproj/ │ │ │ │ │ │ └── DateTools.strings │ │ │ │ │ └── zh-Hant.lproj/ │ │ │ │ │ └── DateTools.strings │ │ │ │ ├── DateTools.h │ │ │ │ ├── NSDate+DateTools.h │ │ │ │ └── NSDate+DateTools.m │ │ │ ├── DateTools.podspec │ │ │ ├── Examples/ │ │ │ │ └── DateToolsExample/ │ │ │ │ ├── DateTools/ │ │ │ │ │ └── Info.plist │ │ │ │ ├── DateToolsExample/ │ │ │ │ │ ├── AppDelegate.h │ │ │ │ │ ├── AppDelegate.m │ │ │ │ │ ├── Colours.h │ │ │ │ │ ├── Colours.m │ │ │ │ │ ├── DateToolsExample-Info.plist │ │ │ │ │ ├── DateToolsExample-Prefix.pch │ │ │ │ │ ├── DateToolsViewController.h │ │ │ │ │ ├── DateToolsViewController.m │ │ │ │ │ ├── DateToolsViewController.xib │ │ │ │ │ ├── ExampleNavigationController.h │ │ │ │ │ ├── ExampleNavigationController.m │ │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── LaunchImage.launchimage/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── TimePeriodsViewController.h │ │ │ │ │ ├── TimePeriodsViewController.m │ │ │ │ │ ├── TimePeriodsViewController.xib │ │ │ │ │ ├── en.lproj/ │ │ │ │ │ │ └── InfoPlist.strings │ │ │ │ │ └── main.m │ │ │ │ ├── DateToolsExample.xcodeproj/ │ │ │ │ │ ├── project.pbxproj │ │ │ │ │ ├── project.xcworkspace/ │ │ │ │ │ │ └── contents.xcworkspacedata │ │ │ │ │ └── xcshareddata/ │ │ │ │ │ └── xcschemes/ │ │ │ │ │ └── DateTools.xcscheme │ │ │ │ └── DateToolsExampleTests/ │ │ │ │ ├── DateToolsExampleTests-Info.plist │ │ │ │ └── en.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── Tests/ │ │ │ └── DateToolsTests/ │ │ │ ├── DateToolsTests/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Base.lproj/ │ │ │ │ │ └── Main.storyboard │ │ │ │ ├── DateToolsTests-Info.plist │ │ │ │ ├── DateToolsTests-Prefix.pch │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── LaunchImage.launchimage/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── ViewController.h │ │ │ │ ├── ViewController.m │ │ │ │ ├── en.lproj/ │ │ │ │ │ └── InfoPlist.strings │ │ │ │ ├── es.lproj/ │ │ │ │ │ ├── InfoPlist.strings │ │ │ │ │ └── Main.strings │ │ │ │ ├── ja.lproj/ │ │ │ │ │ ├── InfoPlist.strings │ │ │ │ │ └── Main.strings │ │ │ │ └── main.m │ │ │ ├── DateToolsTests.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ ├── project.xcworkspace/ │ │ │ │ │ └── contents.xcworkspacedata │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ ├── DateToolsTests.xcscheme │ │ │ │ └── DateToolsTestsTests.xcscheme │ │ │ └── DateToolsTestsTests/ │ │ │ ├── DTTimeAgoTests.m │ │ │ ├── DTTimePeriodChainTests.m │ │ │ ├── DTTimePeriodCollectionTests.m │ │ │ ├── DTTimePeriodGroupTests.m │ │ │ ├── DTTimePeriodTests.m │ │ │ ├── DateToolsTests.m │ │ │ ├── DateToolsTestsTests-Info.plist │ │ │ ├── en.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── es.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ └── ja.lproj/ │ │ │ └── InfoPlist.strings │ │ ├── LaunchAtLoginController/ │ │ │ ├── LaunchAtLoginController.h │ │ │ ├── LaunchAtLoginController.m │ │ │ └── README.md │ │ ├── NSStringEmojize/ │ │ │ ├── .gitignore │ │ │ ├── Example/ │ │ │ │ ├── NSStringEmojize/ │ │ │ │ │ ├── DIYAppDelegate.h │ │ │ │ │ ├── DIYAppDelegate.m │ │ │ │ │ ├── DIYViewController.h │ │ │ │ │ ├── DIYViewController.m │ │ │ │ │ ├── NSStringEmojize-Info.plist │ │ │ │ │ ├── NSStringEmojize-Prefix.pch │ │ │ │ │ ├── en.lproj/ │ │ │ │ │ │ └── InfoPlist.strings │ │ │ │ │ └── main.m │ │ │ │ ├── NSStringEmojize.xcodeproj/ │ │ │ │ │ ├── project.pbxproj │ │ │ │ │ └── project.xcworkspace/ │ │ │ │ │ └── contents.xcworkspacedata │ │ │ │ └── NSStringEmojizeTests/ │ │ │ │ ├── NSStringEmojizeTests-Info.plist │ │ │ │ ├── NSStringEmojizeTests.h │ │ │ │ ├── NSStringEmojizeTests.m │ │ │ │ └── en.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── LICENSE.md │ │ │ ├── NSStringEmojize/ │ │ │ │ ├── NSString+Emojize.h │ │ │ │ ├── NSString+Emojize.m │ │ │ │ └── emojis.h │ │ │ ├── NSStringEmojize.podspec │ │ │ └── README.md │ │ ├── STPrivilegedTask/ │ │ │ ├── .gitignore │ │ │ ├── LICENSE.txt │ │ │ ├── PrivilegedTaskExample/ │ │ │ │ ├── PrivilegedTaskExample/ │ │ │ │ │ ├── AppDelegate.h │ │ │ │ │ ├── AppDelegate.m │ │ │ │ │ ├── Base.lproj/ │ │ │ │ │ │ └── MainMenu.xib │ │ │ │ │ ├── Info.plist │ │ │ │ │ ├── lock-icon.icns │ │ │ │ │ └── main.m │ │ │ │ └── PrivilegedTaskExample.xcodeproj/ │ │ │ │ └── project.pbxproj │ │ │ ├── README.md │ │ │ ├── STPrivilegedTask.h │ │ │ ├── STPrivilegedTask.m │ │ │ └── STPrivilegedTask.podspec │ │ └── Sparkle/ │ │ ├── .clang-format │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CHANGELOG │ │ ├── Configurations/ │ │ │ ├── ConfigBinaryDelta.xcconfig │ │ │ ├── ConfigBinaryDeltaDebug.xcconfig │ │ │ ├── ConfigBinaryDeltaRelease.xcconfig │ │ │ ├── ConfigCommon.xcconfig │ │ │ ├── ConfigCommonCoverage.xcconfig │ │ │ ├── ConfigCommonDebug.xcconfig │ │ │ ├── ConfigCommonRelease.xcconfig │ │ │ ├── ConfigFileop.xcconfig │ │ │ ├── ConfigFramework.xcconfig │ │ │ ├── ConfigFrameworkDebug.xcconfig │ │ │ ├── ConfigFrameworkRelease.xcconfig │ │ │ ├── ConfigRelaunch.xcconfig │ │ │ ├── ConfigRelaunchDebug.xcconfig │ │ │ ├── ConfigRelaunchRelease.xcconfig │ │ │ ├── ConfigTestApp.xcconfig │ │ │ ├── ConfigTestAppDebug.xcconfig │ │ │ ├── ConfigTestAppRelease.xcconfig │ │ │ ├── ConfigUITest.xcconfig │ │ │ ├── ConfigUITestCoverage.xcconfig │ │ │ ├── ConfigUITestDebug.xcconfig │ │ │ ├── ConfigUITestRelease.xcconfig │ │ │ ├── ConfigUnitTest.xcconfig │ │ │ ├── ConfigUnitTestCoverage.xcconfig │ │ │ ├── ConfigUnitTestDebug.xcconfig │ │ │ ├── ConfigUnitTestRelease.xcconfig │ │ │ ├── make-release-package.sh │ │ │ └── set-git-version-info.sh │ │ ├── Documentation/ │ │ │ ├── .gitignore │ │ │ ├── Doxyfile │ │ │ └── build-docs.sh │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.markdown │ │ ├── Resources/ │ │ │ ├── Images.xcassets/ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── SUModelTranslation.plist │ │ │ ├── SampleAppcast.xml │ │ │ └── Sparkle-Icon.sketch │ │ ├── Sparkle/ │ │ │ ├── Autoupdate/ │ │ │ │ ├── Autoupdate-Info.plist │ │ │ │ └── Autoupdate.m │ │ │ ├── CheckLocalizations.swift │ │ │ ├── SUAppcast.h │ │ │ ├── SUAppcast.m │ │ │ ├── SUAppcastItem.h │ │ │ ├── SUAppcastItem.m │ │ │ ├── SUAutomaticUpdateAlert.h │ │ │ ├── SUAutomaticUpdateAlert.m │ │ │ ├── SUAutomaticUpdateDriver.h │ │ │ ├── SUAutomaticUpdateDriver.m │ │ │ ├── SUBasicUpdateDriver.h │ │ │ ├── SUBasicUpdateDriver.m │ │ │ ├── SUBinaryDeltaApply.h │ │ │ ├── SUBinaryDeltaApply.m │ │ │ ├── SUBinaryDeltaCommon.h │ │ │ ├── SUBinaryDeltaCommon.m │ │ │ ├── SUBinaryDeltaCreate.h │ │ │ ├── SUBinaryDeltaCreate.m │ │ │ ├── SUBinaryDeltaTool.m │ │ │ ├── SUBinaryDeltaUnarchiver.h │ │ │ ├── SUBinaryDeltaUnarchiver.m │ │ │ ├── SUCodeSigningVerifier.h │ │ │ ├── SUCodeSigningVerifier.m │ │ │ ├── SUConstants.h │ │ │ ├── SUConstants.m │ │ │ ├── SUDSAVerifier.h │ │ │ ├── SUDSAVerifier.m │ │ │ ├── SUDiskImageUnarchiver.h │ │ │ ├── SUDiskImageUnarchiver.m │ │ │ ├── SUErrors.h │ │ │ ├── SUExport.h │ │ │ ├── SUFileManager.h │ │ │ ├── SUFileManager.m │ │ │ ├── SUGuidedPackageInstaller.h │ │ │ ├── SUGuidedPackageInstaller.m │ │ │ ├── SUHost.h │ │ │ ├── SUHost.m │ │ │ ├── SUInstaller.h │ │ │ ├── SUInstaller.m │ │ │ ├── SULog.h │ │ │ ├── SULog.m │ │ │ ├── SUOperatingSystem.h │ │ │ ├── SUOperatingSystem.m │ │ │ ├── SUPackageInstaller.h │ │ │ ├── SUPackageInstaller.m │ │ │ ├── SUPipedUnarchiver.h │ │ │ ├── SUPipedUnarchiver.m │ │ │ ├── SUPlainInstaller.h │ │ │ ├── SUPlainInstaller.m │ │ │ ├── SUProbingUpdateDriver.h │ │ │ ├── SUProbingUpdateDriver.m │ │ │ ├── SUScheduledUpdateDriver.h │ │ │ ├── SUScheduledUpdateDriver.m │ │ │ ├── SUStandardVersionComparator.h │ │ │ ├── SUStandardVersionComparator.m │ │ │ ├── SUStatus.xib │ │ │ ├── SUStatusController.h │ │ │ ├── SUStatusController.m │ │ │ ├── SUSystemProfiler.h │ │ │ ├── SUSystemProfiler.m │ │ │ ├── SUUIBasedUpdateDriver.h │ │ │ ├── SUUIBasedUpdateDriver.m │ │ │ ├── SUUnarchiver.h │ │ │ ├── SUUnarchiver.m │ │ │ ├── SUUnarchiver_Private.h │ │ │ ├── SUUpdateAlert.h │ │ │ ├── SUUpdateAlert.m │ │ │ ├── SUUpdateDriver.h │ │ │ ├── SUUpdateDriver.m │ │ │ ├── SUUpdatePermissionPrompt.h │ │ │ ├── SUUpdatePermissionPrompt.m │ │ │ ├── SUUpdater.h │ │ │ ├── SUUpdater.m │ │ │ ├── SUUpdater_Private.h │ │ │ ├── SUUserInitiatedUpdateDriver.h │ │ │ ├── SUUserInitiatedUpdateDriver.m │ │ │ ├── SUVersionComparisonProtocol.h │ │ │ ├── SUVersionDisplayProtocol.h │ │ │ ├── SUWindowController.h │ │ │ ├── SUWindowController.m │ │ │ ├── Sparkle-Info.plist │ │ │ ├── Sparkle.h │ │ │ ├── Sparkle.pch │ │ │ ├── ar.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── ca.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ │ ├── SUUpdateAlert.strings │ │ │ │ └── Sparkle.strings │ │ │ ├── cs.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── da.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── de.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── el.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── en.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── es.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── fi.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ │ ├── SUUpdateAlert.strings │ │ │ │ └── Sparkle.strings │ │ │ ├── fr.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── he.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ │ ├── SUUpdateAlert.strings │ │ │ │ └── Sparkle.strings │ │ │ ├── is.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── it.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── ja.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── ko.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── nb.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── nl.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── no.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ │ ├── SUUpdateAlert.strings │ │ │ │ └── Sparkle.strings │ │ │ ├── pl.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── pt_BR.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── pt_PT.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── ro.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── ru.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── sk.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── sl.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── sv.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── th.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── tr.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── uk.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ ├── zh_CN.lproj/ │ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ │ ├── SUUpdateAlert.xib │ │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ │ └── Sparkle.strings │ │ │ └── zh_TW.lproj/ │ │ │ ├── SUAutomaticUpdateAlert.xib │ │ │ ├── SUUpdateAlert.xib │ │ │ ├── SUUpdatePermissionPrompt.xib │ │ │ └── Sparkle.strings │ │ ├── Sparkle.podspec │ │ ├── Sparkle.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace/ │ │ │ │ └── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ ├── Distribution.xcscheme │ │ │ ├── Sparkle.xcscheme │ │ │ └── UITests.xcscheme │ │ ├── TestApplication/ │ │ │ ├── English.lproj/ │ │ │ │ ├── InfoPlist.strings │ │ │ │ └── MainMenu.xib │ │ │ ├── SUTestApplicationDelegate.h │ │ │ ├── SUTestApplicationDelegate.m │ │ │ ├── SUTestWebServer.h │ │ │ ├── SUTestWebServer.m │ │ │ ├── SUUpdateSettingsWindowController.h │ │ │ ├── SUUpdateSettingsWindowController.m │ │ │ ├── SUUpdateSettingsWindowController.xib │ │ │ ├── TestApplication-Info.plist │ │ │ ├── ar.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── ca.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── cs.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── cy.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── da.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── de.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── el.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── es.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── fi.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── fr-CA.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── fr.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── he.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── hu.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── id.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── is.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── it.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── ja.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── ko.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── main.m │ │ │ ├── nb.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── nl.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── pl.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── pt-BR.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── pt-PT.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── pt.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── ro.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── ru.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── sk.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── sl.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── sparkletestcast.xml │ │ │ ├── sv.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── test_app_only_dsa_priv_dont_ever_do_this_for_real.pem │ │ │ ├── test_app_only_dsa_pub.pem │ │ │ ├── th.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── tr.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── uk.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ ├── zh-Hans.lproj/ │ │ │ │ └── InfoPlist.strings │ │ │ └── zh-Hant.lproj/ │ │ │ └── InfoPlist.strings │ │ ├── Tests/ │ │ │ ├── Resources/ │ │ │ │ ├── SparkleTestCodeSignApp.dmg │ │ │ │ ├── SparkleTestCodeSignApp.enc.dmg │ │ │ │ ├── SparkleTestCodeSignApp.tar.bz2 │ │ │ │ ├── SparkleTestCodeSignApp.tar.xz │ │ │ │ ├── signed-test-file.txt │ │ │ │ ├── test-pubkey.pem │ │ │ │ ├── test.sparkle_guided.pkg │ │ │ │ ├── testappcast.xml │ │ │ │ └── testnamespaces.xml │ │ │ ├── SUAppcastTest.swift │ │ │ ├── SUBinaryDeltaTest.m │ │ │ ├── SUCodeSigningVerifierTest.m │ │ │ ├── SUDSAVerifierTest.m │ │ │ ├── SUFileManagerTest.swift │ │ │ ├── SUInstallerTest.m │ │ │ ├── SUUnarchiverTest.swift │ │ │ ├── SUUpdaterTest.m │ │ │ ├── SUVersionComparisonTest.m │ │ │ ├── Sparkle Unit Tests-Bridging-Header.h │ │ │ └── SparkleTests-Info.plist │ │ ├── UITests/ │ │ │ ├── SUTestApplicationTest.swift │ │ │ └── UITests-Info.plist │ │ ├── Vendor/ │ │ │ ├── CocoatechCore/ │ │ │ │ ├── NTSynchronousTask.h │ │ │ │ └── NTSynchronousTask.m │ │ │ └── bsdiff/ │ │ │ ├── bscommon.c │ │ │ ├── bscommon.h │ │ │ ├── bsdiff.c │ │ │ ├── bspatch.c │ │ │ ├── bspatch.h │ │ │ ├── sais.c │ │ │ └── sais.h │ │ ├── bin/ │ │ │ ├── generate_keys │ │ │ └── sign_update │ │ └── fileop/ │ │ ├── SUFileOperationConstants.h │ │ ├── SUFileOperationConstants.m │ │ └── fileop.m │ ├── Docs/ │ │ ├── DistributingBitBar.md │ │ └── URLScheme.md │ ├── Makefile │ ├── README.md │ └── Scripts/ │ └── bitbar-bundler ├── pkg/ │ ├── metadata/ │ │ ├── categories_metadata.go │ │ ├── categories_metadata_test.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── plugin_metadata.go │ │ └── plugin_metadata_test.go │ ├── plugins/ │ │ ├── README.md │ │ ├── action.go │ │ ├── action_test.go │ │ ├── colors.go │ │ ├── emoji.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── install.go │ │ ├── install_test.go │ │ ├── installed_plugins.go │ │ ├── installed_plugins_test.go │ │ ├── item_params.go │ │ ├── item_params_test.go │ │ ├── parse.go │ │ ├── parse_test.go │ │ ├── plugin.go │ │ ├── plugin_darwin.go │ │ ├── plugin_linux.go │ │ ├── plugin_test.go │ │ ├── plugin_windows.go │ │ ├── refresh_interval.go │ │ ├── refresh_interval_test.go │ │ ├── testdata/ │ │ │ ├── broken-plugins/ │ │ │ │ ├── broken.1m.sh │ │ │ │ └── wont-quit.1m.sh │ │ │ ├── plugins/ │ │ │ │ ├── 001-multiple.1s.sh │ │ │ │ ├── 001-multiple.1s.sh.vars.json │ │ │ │ ├── 002-multiple.1s.sh │ │ │ │ ├── dirs-should-be-ignored/ │ │ │ │ │ └── .gitkeep │ │ │ │ ├── expanded.1m.sh │ │ │ │ ├── expanded.1m.sh.off │ │ │ │ ├── expanded.1s.sh │ │ │ │ ├── params.3s.sh │ │ │ │ ├── simple.1m.sh │ │ │ │ ├── simple.1m.sh.disabled │ │ │ │ └── simple.1s.sh │ │ │ ├── stub-api/ │ │ │ │ └── currency-tracker.1h.py.json │ │ │ ├── token-too-long/ │ │ │ │ ├── jma.1h.sh │ │ │ │ └── jma.1h.sh.output │ │ │ └── vars-test/ │ │ │ ├── plugin.sh │ │ │ └── plugin.sh.vars.json │ │ ├── variables.go │ │ └── variables_test.go │ └── update/ │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── update.go │ ├── update_test.go │ └── updatetest/ │ └── main.go ├── talk-overview.md ├── tools/ │ ├── sitegen/ │ │ ├── README.md │ │ ├── docs.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── images.go │ │ ├── main.go │ │ ├── repo.go │ │ ├── repo_test.go │ │ └── run.sh │ └── xbarmdcheck/ │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── testdata/ │ └── sample-plugin.sh └── xbarapp.com/ ├── .gcloudignore ├── README.md ├── app.yaml ├── articles/ │ └── 2021/ │ └── 03/ │ ├── 13/ │ │ └── Upgrade-legacy-BitBar-plugins.md │ └── 14/ │ └── Variables-in-xbar.md ├── build.sh ├── deploy.sh ├── download.go ├── gen.sh ├── go.mod ├── go.sum ├── main.go ├── main_test.go ├── package.json ├── postcss.config.js ├── public/ │ └── css/ │ └── xbar.css ├── run.sh ├── styles.css ├── tailwind.config.js └── templates/ ├── _layout.html ├── article.html ├── articles-index.html ├── category.html ├── contributor.html ├── contributors.html ├── index.html └── plugin.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ github: matryer ================================================ FILE: .github/workflows/deploy-xbarappcom.yaml ================================================ name: Build and Deploy on: push: branches: - main schedule: - cron: "0 0 * * 0" # every week jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 with: fetch-depth: 0 # Fetch all history for all tags and branches - name: Set up Go uses: actions/setup-go@v3 with: go-version: "1.22" - name: Run sitegen working-directory: tools/sitegen env: XBAR_GITHUB_ACCESS_TOKEN: ${{ secrets.XBAR_GITHUB_ACCESS_TOKEN }} run: ./run.sh -skipdata - name: Auth with Google Cloud uses: google-github-actions/auth@v2 with: credentials_json: ${{ secrets.GCP_CREDENTIALS }} - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v2 with: version: ">= 363.0.0" - name: Deploy to Google App Engine working-directory: xbarapp.com env: GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }} run: | VERSION=$(date +%Y%m%d%H%M%S) gcloud app deploy --project xbarapp --version $VERSION --quiet --verbosity=debug ================================================ FILE: .gitignore ================================================ .DS_Store .idea app/frontend/package.json.md5 xbarapp.com/public/docs/ tools/sitegen/sitegen app/node_modules app/build/bin pkg/update/testarea xbarapp.com/node_modules xbarapp.com/public/docs xbarapp.com/package-lock.json app/build/darwin/info.plist app/frontend/package-lock.json app/build/.DS_Store app/.DS_Store xbarapp.com/public/.DS_Store tools/sitegen/.version app/.version xbarapp.com/.version xbarapp.com/xbarappcom xbarapp.com/version.gen.go tools/bloggen/.version pkg/.DS_Store ================================================ FILE: .vscode/settings.json ================================================ { "files.exclude": { "archive": true } } ================================================ FILE: LICENSE.txt ================================================ The MIT License (MIT) Copyright (c) 2014-2023 Mat Ryer + contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![](xbarapp.com/public/img/xbar-menu-preview.png)](https://xbarapp.com/) # Welcome to xbar xbar (the BitBar reboot) lets you put the output from any script/program in your macOS menu bar. * **Complete rewrite from the ground up** - in Go by @matryer and @leaanthony - using [Wails.app (build cross-platform desktop apps using Go & HTML/CSS/JS)](https://wails.app) * Completely open source * [Download latest xbar release](https://github.com/matryer/xbar/releases/latest) - requires macOS Catalina or newer (>= 10.15) * [Visit the app homepage at https://xbarapp.com](https://xbarapp.com) * [Get started](#get-started) and [installing plugins](#installing-plugins) Digging deeper: * [Browse plugin repository](https://xbarapp.com/) * [Guide to writing your own plugins](https://github.com/matryer/xbar-plugins/blob/main/CONTRIBUTING.md) And finally... * [Read the story about how xbar unexpectedly got going](https://medium.com/@matryer/what-happens-when-your-old-open-source-project-unexpectedly-gets-to-the-top-of-hacker-news-31114c6c6efb#.fznvtgskb) * [Contributing](#contributing) and [special thanks](#thanks) ## Get started ### Install * [Download the latest release of xbar](https://github.com/matryer/xbar/releases). ## Installing plugins From an xbar menu, choose **Preferences > Plugins...** to use the xbar app to discover and manage plugins. You can [browse all the plugins](https://xbarapp.com/) online, or [write your own](https://github.com/matryer/xbar-plugins/blob/main/CONTRIBUTING.md). ### The Plugin Directory The plugin directory is folder on your Mac where the plugins live, located at `~/Library/Application Support/xbar/plugins`. * If you're transitioning from Bitbar, move your plugins into this new folder to install them ## Contributing If you'd like to contribute a plugin, head over to https://github.com/matryer/xbar-plugins to get started. Please do not send pull requests to this repo. Open an issue and start a conversation first. PRs will likely not be accepted. * Get started with our [Writing plugins guide](https://github.com/matryer/xbar-plugins/blob/main/CONTRIBUTING.md) ## Thanks * Special thanks to [@leaanthony at https://wails.app](https://wails.app) and [@ianfoo](https://github.com/ianfoo), [@gingerbeardman](https://github.com/gingerbeardman), [@iosdeveloper](https://github.com/iosdeveloper), [@muhqu](https://github.com/muhqu), [@m-cat](https://github.com/m-cat), [@mpicard](https://github.com/mpicard), [@tylerb](https://github.com/tylerb) for their help * Thanks to [Chris Ryer](http://www.chrisryer.co.uk/) for the app logo - and to [@mazondo](https://twitter.com/mazondo) for the original * Thanks for all our [plugin contributors](https://xbarapp.com/) who have come up with some pretty genius things ================================================ FILE: app/.gitignore ================================================ app/frontend/package.json.md5 app/build .idea ================================================ FILE: app/README.md ================================================ # xbar Put anything into your macOS menu bar (sequel to BitBar) ## Development To build xbar, you will need: * Go v1.15+ * npm v6.14.9 * Wails v2 cli - `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-alpha.72` ### Running If running for the first time first generate the `.version` file and install the npm dependencies by running: ```bash cd app && git describe --tags > .version ``` and ```bash cd app/frontend && npm install ``` To start the application run: ```bash cd app/frontend && npm run dev ``` and ```bash cd app && wails dev ``` ### Building In this directory run `./build.sh`. The binary will be generated in `./build/bin/`. ```bash ./build.sh && ./build/bin/xbar ``` ### Updates To use the latest version of the Wails library, ensure that go.mod is using the latest release tag. The Wails CLI may be updated by running `wails update -pre`. When v2 is released, then `wails update` will keep you on the stable channel. ### Packaging Tag the branch: ```bash git tag -a v0.1.0 -m "release tag." git push origin v0.1.0 ``` ```bash ./package.sh ``` * For code signing, xbar uses https://github.com/matryer/gon (fork of https://github.com/mitchellh/gon) ================================================ FILE: app/app.go ================================================ package main import ( "context" "fmt" "log" "net/http" "os" "path/filepath" "sync" "time" "github.com/pkg/errors" "github.com/wailsapp/wails/v2/pkg/mac" "github.com/gregjones/httpcache" "github.com/gregjones/httpcache/diskcache" "github.com/wailsapp/wails/v2/pkg/options/dialog" "github.com/matryer/xbar/pkg/plugins" "github.com/matryer/xbar/pkg/update" wails "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/menu" "github.com/wailsapp/wails/v2/pkg/menu/keys" ) var ( pluginDirectory = filepath.Join(os.Getenv("HOME"), "Library", "Application Support", "xbar", "plugins") cacheDirectory = filepath.Join(os.Getenv("HOME"), "Library", "Application Support", "xbar", "cache") configFilename = filepath.Join(os.Getenv("HOME"), "Library", "Application Support", "xbar", "xbar.config.json") // concurrentIncomingURLs is the number of concurrent incoming URLs to handle at // the same time. concurrentIncomingURLs int = 1 // apiRequestTimeout is the timeout for making API calls. apiRequestTimeout = 30 * time.Second ) type app struct { runtime *wails.Runtime settings *settings // appMenu isn't visible - it's used for key shortcuts. appMenu *menu.Menu contextMenus []*menu.ContextMenu defaultTrayMenu *menu.TrayMenu plugins plugins.Plugins pluginTrays map[string]*menu.TrayMenu menuParser *MenuParser startsAtLoginMenu *menu.MenuItem autoupdateMenu *menu.MenuItem appUpdatesMenu *menu.MenuItem // Verbose gets whether verbose output will be printed // or not. Verbose bool CategoriesService *CategoriesService PluginsService *PluginsService PersonService *PersonService CommandService *CommandService // incomingURLSemaphore is a buffered channel that keeps the // number of incoming URLs being parsed to one at a time. incomingURLSemaphore chan struct{} // lock protects menu items when RefreshAll // is called. // Also protects stopPluginsFunc, pluginsStoppedSignal, // menuIsOpen and isDarkMode. lock sync.Mutex stopPluginsFunc context.CancelFunc // menuIsOpen keeps track of whether menus are open or not. // If they're open, they will not be updated. menuIsOpen bool // pluginsStoppedSignal is closed when plugins have stopped running. pluginsStoppedSignal chan struct{} defaultTrayMenuActive bool // isDarkMode indicates whether the system is running // in dark mode or not. isDarkMode bool } // newApp makes a new app. func newApp() (*app, error) { settings, err := loadSettings(configFilename) if err != nil { return nil, errors.Wrap(err, "loadSettings") } app := &app{ settings: settings, Verbose: true, menuParser: NewMenuParser(), incomingURLSemaphore: make(chan struct{}, concurrentIncomingURLs), } app.appMenu = menu.NewMenuFromItems( menu.AppMenu(), menu.EditMenu(), menu.WindowMenu(), &menu.MenuItem{ Type: menu.SubmenuType, Label: "Browser", SubMenu: menu.NewMenuFromItems( menu.Text("Refresh", keys.CmdOrCtrl("r"), app.onBrowserRefreshMenuClicked), menu.Text("Clear cache and refresh", keys.Combo("r", keys.CmdOrCtrlKey, keys.ShiftKey), app.onBrowserHardRefreshMenuClicked), ), }, ) app.contextMenus = []*menu.ContextMenu{ menu.NewContextMenu("refreshContextMenu", menu.NewMenuFromItems( menu.Text("Refresh page data", nil, app.onBrowserHardRefreshMenuClicked), menu.Text("Refresh plugins", nil, app.onPluginsRefreshAllMenuClicked), menu.Text("Clear Cache", nil, app.onClearCacheMenuClicked), )), } app.appUpdatesMenu = &menu.MenuItem{ Type: menu.TextType, Label: "Check for Updates…", Click: app.onCheckForUpdatesMenuClick, } app.autoupdateMenu = &menu.MenuItem{ Label: "Update Automatically", Type: menu.CheckboxType, Checked: app.settings.AutoUpdate, Click: app.updateAutoupdate, } app.startsAtLoginMenu = &menu.MenuItem{ Label: "Open at Login", Type: menu.CheckboxType, Checked: false, Click: app.updateStartOnLogin, } startsAtLogin, err := mac.StartsAtLogin() if err != nil { if app.Verbose { log.Println("open at login:", err) } app.startsAtLoginMenu.Label = "Open at Login" app.startsAtLoginMenu.Disabled = true } else { app.startsAtLoginMenu.Checked = startsAtLogin } // client-side caching to cacheDirectory tp := httpcache.NewTransport(diskcache.New(cacheDirectory)) client := &http.Client{ Transport: tp, Timeout: apiRequestTimeout, } app.CategoriesService = NewCategoriesService(client) app.PersonService = NewPersonService(client) app.CommandService = NewCommandService(app.RefreshAll) app.PluginsService = NewPluginsService(client, "https://xbarapp.com/docs/plugins/") app.PluginsService.OnRefresh = app.RefreshAll app.defaultTrayMenu = &menu.TrayMenu{ Label: "xbar", Menu: app.newXbarMenu(nil, false), } return app, nil } func (app *app) Start(runtime *wails.Runtime) { app.setDarkMode(runtime.System.IsDarkMode()) runtime.Events.OnThemeChange(func(darkMode bool) { // keep track of dark mode changing, and refresh all // plugins if it does. app.setDarkMode(darkMode) app.RefreshAll() }) app.runtime = runtime app.PluginsService.runtime = runtime app.CommandService.runtime = runtime app.CommandService.clearCache = app.clearCache // ensure the plugin directory is there if err := os.MkdirAll(pluginDirectory, 0777); err != nil { log.Println("failed to create plugin directory:", err) } app.RefreshAll() go func() { // wait before checking for updates time.Sleep(10 * time.Second) for { app.checkForUpdates(true) // check again in twelve hours time.Sleep(12 * time.Hour) } }() } func (app *app) RefreshAll() { app.lock.Lock() defer app.lock.Unlock() if app.stopPluginsFunc != nil { // plugins are already running - let's stop them // and wait for them to stop app.stopPluginsFunc() <-app.pluginsStoppedSignal } // remove plugins if app.defaultTrayMenuActive { // only default menu - remove it app.runtime.Menu.DeleteTrayMenu(app.defaultTrayMenu) app.defaultTrayMenuActive = false } for _, plugin := range app.plugins { m, ok := app.pluginTrays[plugin.Command] if !ok { continue } app.runtime.Menu.DeleteTrayMenu(m) } var err error app.plugins, err = plugins.Dir(pluginDirectory) if err != nil { app.onErr(err.Error()) return } app.pluginTrays = make(map[string]*menu.TrayMenu) if len(app.plugins) == 0 { // no plugins - use default app.runtime.Menu.SetTrayMenu(app.defaultTrayMenu) app.defaultTrayMenuActive = true return } for _, plugin := range app.plugins { // Setup plugin plugin.AppleScriptTemplate = app.settings.Terminal.AppleScriptTemplate3 plugin.OnCycle = app.onCycle plugin.OnRefresh = app.onRefresh if app.Verbose { //plugin.Stdout = os.Stdout plugin.Stderr = os.Stderr plugin.Debugf = plugins.DebugfPrefix(plugin.CleanFilename(), plugins.DebugfLog) } app.pluginTrays[plugin.Command] = &menu.TrayMenu{ Label: " ", Menu: app.newXbarMenu(plugin, false), OnOpen: app.onMenuWillOpen, OnClose: app.onMenuDidClose, } app.runtime.Menu.SetTrayMenu(app.pluginTrays[plugin.Command]) } app.pluginsStoppedSignal = make(chan struct{}) var ctx context.Context ctx, app.stopPluginsFunc = context.WithCancel(context.Background()) go func() { // use stopPluginsFunc to allow the context to // be canceled - which will kill all running plugin subprocesses. app.plugins.Run(ctx) close(app.pluginsStoppedSignal) }() } // CheckForUpdates proactively checks for updates. func (app *app) CheckForUpdates() { app.checkForUpdates(false) } func (app *app) onMenuWillOpen() { app.lock.Lock() defer app.lock.Unlock() app.menuIsOpen = true } func (app *app) onMenuDidClose() { app.lock.Lock() defer app.lock.Unlock() app.menuIsOpen = false } func (app *app) updateStartOnLogin(data *menu.CallbackData) { err := mac.StartAtLogin(data.MenuItem.Checked) if err != nil { app.startsAtLoginMenu.Label = "Start at Login unavailable" app.startsAtLoginMenu.Disabled = true } // We need to refresh all as the menuitem is used in multiple places. // If we don't refresh, only the menuitem clicked will toggle in the UI. app.refreshMenus() } func (app *app) updateAutoupdate(data *menu.CallbackData) { if data.MenuItem.Checked && app.settings.AutoUpdate { // nothing to do return } if !data.MenuItem.Checked && !app.settings.AutoUpdate { // nothing to do return } app.settings.AutoUpdate = data.MenuItem.Checked if err := app.settings.save(); err != nil { log.Println("err: updateAutoupdate:", err) return } if app.settings.AutoUpdate { go app.checkForUpdates(true) } // We need to refresh all as the menuitem is used in multiple places. // If we don't refresh, only the menuitem clicked will toggle in the UI. app.refreshMenus() } // onErr adds a single menu showing the specified error // string. func (app *app) onErr(err string) { if app.defaultTrayMenuActive { app.runtime.Menu.DeleteTrayMenu(app.defaultTrayMenu) app.defaultTrayMenuActive = false } errorMenu := &menu.Menu{} errorMenu.Append(menu.Text(err, nil, nil)) errorMenu.Append(menu.Separator()) errorMenu.Merge(app.newXbarMenu(nil, false)) app.defaultTrayMenu = &menu.TrayMenu{ Label: "⚠️ xbar", Menu: errorMenu, } app.runtime.Menu.SetTrayMenu(app.defaultTrayMenu) app.defaultTrayMenuActive = true } // Shutdown shuts down the app. func (app *app) Shutdown() { if app.stopPluginsFunc != nil { // it's possible this gets called _before_ the stop // func is set. In which case, we'll just ignore it. app.stopPluginsFunc() } } func (app *app) newXbarMenu(plugin *plugins.Plugin, asSubmenu bool) *menu.Menu { var items []*menu.MenuItem if plugin != nil { items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: "Refresh", Accelerator: keys.CmdOrCtrl("r"), Click: func(ctx *menu.CallbackData) { app.onPluginsRefreshMenuClicked(ctx, plugin) }, }) } items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: "Refresh All", Accelerator: keys.Combo("r", keys.CmdOrCtrlKey, keys.ShiftKey), Click: app.onPluginsRefreshAllMenuClicked, }) if plugin != nil { items = append(items, menu.Text("Run in Terminal…", keys.CmdOrCtrl("t"), func(_ *menu.CallbackData) { err := plugin.RunInTerminal(app.settings.Terminal.AppleScriptTemplate3) if err != nil { _, err2 := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.ErrorDialog, Title: "Run in Terminal", Message: err.Error(), Buttons: []string{"OK"}, CancelButton: "OK", }) if err2 != nil { log.Println(err2) return } return } })) } items = append(items, menu.Separator()) if plugin != nil { items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: "Open Plugin…", Accelerator: keys.CmdOrCtrl("e"), Click: func(_ *menu.CallbackData) { app.runtime.Window.Show() rel, err := filepath.Rel(pluginDirectory, plugin.Command) if err != nil { log.Println(err) return } app.runtime.Events.Emit("xbar.browser.openInstalledPlugin", map[string]string{ "path": rel, }) }, }) } items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: "Plugin Browser…", Accelerator: keys.CmdOrCtrl("p"), Click: app.onPluginsMenuClicked, }) items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: "Open Plugin Folder…", Click: app.onOpenPluginsFolderClicked, }) items = append(items, menu.Separator()) items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: fmt.Sprintf("xbar (%s)", version), Disabled: true, }) items = append(items, app.appUpdatesMenu) items = append(items, app.autoupdateMenu) items = append(items, app.startsAtLoginMenu) items = append(items, menu.Separator()) items = append(items, &menu.MenuItem{ Type: menu.TextType, Label: "Quit xbar", Accelerator: keys.CmdOrCtrl("q"), Click: app.onQuitMenuClicked, }) if asSubmenu { m := menu.NewMenuFromItems( menu.SubMenu("xbar", &menu.Menu{ Items: items, }), ) return m } m := &menu.Menu{Items: items} return m } func (app *app) createDefaultMenus() { app.defaultTrayMenu = &menu.TrayMenu{ Label: "xbar", Menu: app.newXbarMenu(nil, false), } } func (app *app) onPluginsMenuClicked(_ *menu.CallbackData) { app.runtime.Window.Show() } func (app *app) onOpenPluginsFolderClicked(_ *menu.CallbackData) { _ = app.CommandService.OpenPath(pluginDirectory) } func (app *app) onQuitMenuClicked(_ *menu.CallbackData) { app.runtime.Quit() } func (app *app) onPluginsRefreshMenuClicked(_ *menu.CallbackData, p *plugins.Plugin) { p.TriggerRefresh() } func (app *app) onPluginsRefreshAllMenuClicked(_ *menu.CallbackData) { app.RefreshAll() } func (app *app) onBrowserRefreshMenuClicked(_ *menu.CallbackData) { app.runtime.Events.Emit("xbar.browser.refresh") } func (app *app) onBrowserHardRefreshMenuClicked(_ *menu.CallbackData) { app.clearCache(true) app.runtime.Events.Emit("xbar.browser.refresh") } func (app *app) onCheckForUpdatesMenuClick(_ *menu.CallbackData) { app.CheckForUpdates() } func (app *app) onClearCacheMenuClicked(_ *menu.CallbackData) { app.clearCache(false) } func (app *app) clearCache(passive bool) { // bit cheeky - but use the oslock in PluginsService to protect // against this from being run concurrently. app.PluginsService.osLock.Lock() defer app.PluginsService.osLock.Unlock() err := os.RemoveAll(cacheDirectory) if err != nil { if passive { return } _, err2 := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.ErrorDialog, Title: "Clear cache failed", Message: err.Error(), Buttons: []string{"OK"}, CancelButton: "OK", }) if err2 != nil { log.Println(err2) return } return } if passive { return } _, err2 := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.InfoDialog, Title: "Cache cleared", Message: "The local cache was successfully cleared.", Buttons: []string{"OK"}, CancelButton: "OK", }) if err2 != nil { log.Println(err2) return } } func (app *app) handleIncomingURL(url string) { // wait for a space app.incomingURLSemaphore <- struct{}{} defer func() { // free up this space <-app.incomingURLSemaphore }() log.Println("incoming URL: handleIncomingURL", url) incomingURL, err := parseIncomingURL(url) if err != nil { _, err2 := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.ErrorDialog, Title: "Invalid URL", Message: err.Error(), Buttons: []string{"OK"}, CancelButton: "OK", }) if err2 != nil { log.Println(err2) return } return } switch incomingURL.Action { case "openPlugin": app.runtime.Window.Show() app.runtime.Events.Emit("xbar.incomingURL.openPlugin", map[string]string{ "path": incomingURL.Params.Get("path"), }) case "refreshPlugin": for _, plugin := range app.plugins { rel, err := filepath.Rel(pluginDirectory, plugin.Command) if err != nil { log.Println("incoming URL: rel for this failed", err) continue } if rel == incomingURL.Params.Get("path") { plugin.TriggerRefresh() return } } case "refreshAllPlugins": app.RefreshAll() default: log.Printf("incoming URL: skipping, unknown action %q\n", incomingURL.Action) } } // onRefresh is fired when a plugin needs to refresh. func (app *app) onRefresh(ctx context.Context, p *plugins.Plugin, _ error) { app.lock.Lock() defer app.lock.Unlock() if app.menuIsOpen { // don't update while the menu is open // as this can cause a crash return } tray, ok := app.pluginTrays[p.Command] if !ok { log.Println("no item - probably refreshing", tray.Label) return } app.updateLabel(tray, p) pluginMenu := app.menuParser.ParseItems(ctx, p.Items.ExpandedItems) if pluginMenu == nil { pluginMenu = app.newXbarMenu(p, false) } else { pluginMenu.Append(menu.Separator()) pluginMenu.Merge(app.newXbarMenu(p, true)) } tray.Menu = pluginMenu app.runtime.Menu.SetTrayMenu(tray) } func (app *app) onCycle(_ context.Context, p *plugins.Plugin) { app.lock.Lock() defer app.lock.Unlock() if app.menuIsOpen { // don't update while the menu is open // as this can cause a crash return } tray, ok := app.pluginTrays[p.Command] if !ok { // no tray item - it's probably refreshing // so we'll just skip silently. return } if app.updateLabel(tray, p) { app.runtime.Menu.UpdateTrayMenuLabel(tray) } } // refreshMenus refreshes all tray menus to ensure they // are in sync with the data, EG: checkbox sync func (app *app) refreshMenus() { app.onMenuWillOpen() for _, tray := range app.pluginTrays { app.runtime.Menu.SetTrayMenu(tray) } app.onMenuDidClose() } func (app *app) updateLabel(tray *menu.TrayMenu, p *plugins.Plugin) bool { cycleItem := p.CurrentCycleItem() if cycleItem == nil { return false } tray.Label = cycleItem.DisplayText() tray.Image = cycleItem.Params.Image tray.FontName = cycleItem.Params.Font tray.FontSize = cycleItem.Params.Size tray.RGBA = cycleItem.Params.Color tray.Disabled = cycleItem.Params.Disabled if cycleItem.Params.TemplateImage != "" { tray.Image = cycleItem.Params.TemplateImage tray.MacTemplateImage = true } return true } // checkForUpdates looks to see if there's a newer version of xbar, // downloads it and installs it. // If passive is true, it won't complain if it fails. func (app *app) checkForUpdates(passive bool) { if app.Verbose { log.Printf("checking for updates... (current: %s)", version) log.Println("updates: passive", passive) log.Println("updates: AutoUpdate", app.settings.AutoUpdate) } u := update.Updater{ CurrentVersion: version, //LatestReleaseGitHubEndpoint: "https://api.github.com/repos/matryer/xbar/releases/latest", LatestReleaseGitHubEndpoint: "https://api.github.com/repos/matryer/xbar/releases/latest", Client: &http.Client{Timeout: 10 * time.Minute}, SelectAsset: func(release update.Release, asset update.Asset) bool { // look for the zip file return filepath.Ext(asset.Name) == ".zip" }, DownloadBytesLimit: 10_741_824, // 10MB } latest, hasUpdate, err := u.HasUpdate() if err != nil { if app.Verbose { log.Println("failed to check for updates:", err) } if !passive { _, err := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.ErrorDialog, Title: "Update check failed", Message: err.Error(), Buttons: []string{"OK"}, CancelButton: "OK", }) if err != nil { log.Println(err) return } } return } if !hasUpdate { // they are using the latest version if app.Verbose { log.Println("update: you have the latest version") } if !passive { _, err := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.InfoDialog, Title: "You're up to date", Message: fmt.Sprintf("%s is the latest version.", latest.TagName), Buttons: []string{"OK"}, CancelButton: "OK", }) if err != nil { log.Println(err) return } } return } if !app.settings.AutoUpdate { oneWeek := 168 * time.Hour // if this check is passive, and the release is only a few days // old - do a soft prompt. if passive && latest.CreatedAt.After(time.Now().Add(0-oneWeek)) { // Update menu text app.appUpdatesMenu.Label = "Install " + latest.TagName + "…" app.refreshMenus() return } response, err := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.QuestionDialog, Title: "Update xbar?", Message: fmt.Sprintf("xbar %s is now available (you have %s).\n\nWould you like to update?", latest.TagName, u.CurrentVersion), Buttons: []string{"Update", "Later"}, DefaultButton: "Update", CancelButton: "Later", }) if err != nil { log.Println(err) return } switch response { case "Update": // continue case "Later": return } } else { if app.Verbose { log.Println("autoupdating...") } } _, err = u.Update() if err != nil { if app.Verbose { log.Println("failed to update:", err) } if !passive { _, err := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.InfoDialog, Title: "Update successful", Message: "Please restart xbar for the changes to take effect.", Buttons: []string{"OK"}, CancelButton: "OK", }) if err != nil { log.Println(err) return } } return } err = u.Restart() if err != nil { if app.Verbose { log.Println("failed to restart:", err) } if !passive { _, err := app.runtime.Dialog.Message(&dialog.MessageDialog{ Type: dialog.InfoDialog, Title: "Update successful", Message: "Please restart xbar for the changes to take effect.", Buttons: []string{"OK"}, CancelButton: "OK", }) if err != nil { log.Println(err) return } } return } } // tickOS waits a beat after some os changes to give the system // time to reflect those changes. func tickOS() { time.Sleep(500 * time.Millisecond) } // setDarkMode sets the current dark mode state. // It updates app.isDarkMode and also sets the // appropriate environment variables. func (app *app) setDarkMode(darkmode bool) { app.lock.Lock() defer app.lock.Unlock() app.isDarkMode = darkmode var err error if darkmode { err = os.Setenv("BitBarDarkMode", "true") // backwards compatibility if err != nil { log.Println("os.Setenv", err) } err = os.Setenv("XBARDarkMode", "true") if err != nil { log.Println("os.Setenv", err) } } else { err = os.Setenv("BitBarDarkMode", "false") // backwards compatibility if err != nil { log.Println("os.Setenv", err) } err = os.Setenv("XBARDarkMode", "false") if err != nil { log.Println("os.Setenv", err) } } } ================================================ FILE: app/build.sh ================================================ #!/bin/bash set -e VERSION=`git describe --tags` echo "" echo " xbar ${VERSION}..." echo "" echo -n $VERSION > .version wails build -o xbar ================================================ FILE: app/categories_service.go ================================================ package main import ( "context" "encoding/json" "io/ioutil" "net/http" ) // Category represents a group of plugins. type Category struct { Path string `json:"path"` Text string `json:"text"` Children []Category `json:"children"` CategoryPathSegments []PathItem `json:"categoryPathSegments"` } // PathItem is a path segment. type PathItem struct { Path string `json:"path"` Text string `json:"text"` IsLast bool `json:"isLast"` } // CategoriesService access category information. type CategoriesService struct { baseURL string client *http.Client } // NewCategoriesService makes a new CategoriesService. func NewCategoriesService(client *http.Client) *CategoriesService { return &CategoriesService{ baseURL: "https://xbarapp.com/docs", client: client, } } // GetCategories gets the categories from the remote server. func (c *CategoriesService) GetCategories() ([]Category, error) { req, err := http.NewRequest("GET", c.baseURL+"/plugins/categories.json", nil) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(req.Context(), apiRequestTimeout) defer cancel() req = req.WithContext(ctx) res, err := c.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } var payload struct { Categories []Category `json:"categories"` } err = json.Unmarshal(body, &payload) if err != nil { return nil, err } return payload.Categories, nil } ================================================ FILE: app/categories_service_test.go ================================================ package main import ( "net/http" "testing" "time" "github.com/matryer/is" ) func TestCategoryRepositoryGetCategories(t *testing.T) { is := is.New(t) cr := NewCategoriesService(&http.Client{Timeout: 1 * time.Second}) cats, err := cr.GetCategories() is.NoErr(err) is.True(len(cats) > 0) cr.baseURL = "broken" cats, err = cr.GetCategories() is.True(err != nil) is.True(cats == nil) } ================================================ FILE: app/command_service.go ================================================ package main import ( "os" "os/exec" "path/filepath" "syscall" "github.com/pkg/errors" wails "github.com/wailsapp/wails/v2" ) // CommandService provides window service. type CommandService struct { runtime *wails.Runtime OnRefresh func() clearCache func(passive bool) } // NewCommandService makes a new CommandService. func NewCommandService(OnRefresh func()) *CommandService { return &CommandService{ OnRefresh: OnRefresh, } } // ClearCache clears the cache. func (c *CommandService) ClearCache() { c.clearCache(false) } // RefreshAllPlugins refreshes all plugins. func (c *CommandService) RefreshAllPlugins() { c.OnRefresh() } // WindowHide hides the window. func (c *CommandService) WindowHide() { c.runtime.Window.Hide() } // WindowMinimise minimises the window. func (c *CommandService) WindowMinimise() { c.runtime.Window.Minimise() } // OpenPath opens a window. func (c CommandService) OpenPath(path string) error { err := c.runCommand("open", path) if err != nil { return errors.Wrapf(err, "unable to open path %q", path) } return nil } // OpenURL opens a window. func (c CommandService) OpenURL(url string) error { err := c.runCommand("open", url) if err != nil { return errors.Wrapf(err, "unable to open URL %s", url) } return nil } // OpenFile opens a file for editing. func (c CommandService) OpenFile(path string) error { err := c.runCommand("open", filepath.Join(pluginDirectory, path)) if err != nil { return errors.Wrapf(err, "failed to open %q", path) } return nil } // runCommand runs the command, wiring up stdout and stderr, and // inheriting the environment. func (CommandService) runCommand(name string, args ...string) error { cmd := exec.Command(name, args...) cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, } cmd.Env = os.Environ() return cmd.Run() } ================================================ FILE: app/frontend/.gitignore ================================================ /node_modules/ /public/build/ .DS_Store /public/bundle.js.map /public/bundle.js /public/bundle.css ================================================ FILE: app/frontend/package.json ================================================ { "name": "xbar", "version": "1.0.0", "scripts": { "build": "rollup -c", "dev": "rollup -c -w", "start": "sirv public", "start:dev": "sirv public --single --host 0.0.0.0 --dev" }, "devDependencies": { "@rollup/plugin-commonjs": "^11.0.0", "@rollup/plugin-image": "^2.0.6", "@rollup/plugin-node-resolve": "^7.0.0", "@rollup/plugin-url": "^5.0.1", "@wails/runtime": "^1.3.10", "autoprefixer": "^10.4.14", "focus-visible": "^5.2.0", "highlight.js": ">=10.4.1", "postcss": "^8.4.21", "postcss-import": "^15.1.0", "rollup": "^2.35.1", "rollup-plugin-livereload": "^1.0.0", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "rollup-plugin-svelte": "~6.1.1", "rollup-plugin-terser": "^5.1.2", "sirv-cli": "^0.4.4", "svelte": "^3.32.2", "svelte-highlight": "^0.6.2", "svelte-preprocess": "^4.6.1", "tailwindcss": "^3.3.1" }, "dependencies": { "svelte-hash-router": "^1.0.1" } } ================================================ FILE: app/frontend/postcss.config.js ================================================ module.exports = { plugins: [ require('postcss-import'), require('tailwindcss'), require('autoprefixer'), ], } ================================================ FILE: app/frontend/public/index.html ================================================ xbar ================================================ FILE: app/frontend/rollup.config.js ================================================ import svelte from 'rollup-plugin-svelte' import resolve from '@rollup/plugin-node-resolve' import commonjs from '@rollup/plugin-commonjs' import livereload from 'rollup-plugin-livereload' import { terser } from 'rollup-plugin-terser' import postcss from 'rollup-plugin-postcss' import autoPreprocess from 'svelte-preprocess' import image from '@rollup/plugin-image' import { string } from 'rollup-plugin-string' import url from '@rollup/plugin-url' const production = !process.env.ROLLUP_WATCH; export default { input: 'src/main.js', output: { sourcemap: true, format: 'iife', name: 'app', file: 'public/bundle.js' }, onwarn: handleRollupWarning, plugins: [ image(), // Embed binary files url({ include: ['**/*.woff', '**/*.woff2'], limit: Infinity, }), // Embed text files string({ include: ["**/*.jsx", "**/*.go", "**/*.txt"], }), svelte({ preprocess: autoPreprocess(), // enable run-time checks when not in production dev: !production, // we'll extract any component CSS out into // a separate file - better for performance // css: css => { // css.write('bundle.css'); // }, emitCss: true, }), // If you have external dependencies installed from // npm, you'll most likely need these plugins. In // some cases you'll need additional configuration - // consult the documentation for details: // https://github.com/rollup/plugins/tree/master/packages/commonjs resolve({ browser: true, dedupe: ['svelte'] }), commonjs(), // PostCSS preprocessing postcss({ extensions: ['.css', '.scss'], extract: true, minimize: false, use: [ ['sass', { includePaths: [ './src/theme', './node_modules' ] }] ], }), // In dev mode, call `npm run start` once // the bundle has been generated !production && serve(), // Watch the `public` directory and refresh the // browser on changes when not in production !production && livereload('public'), // If we're building for production (npm run build // instead of npm run dev), minify production && terser() ], watch: { clearScreen: false } }; function handleRollupWarning(warning) { console.error('ERROR: ' + warning.toString()); } function serve() { let started = false; return { writeBundle() { if (!started) { started = true; require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { stdio: ['ignore', 'inherit', 'inherit'], shell: true }); } } }; } ================================================ FILE: app/frontend/src/App.svelte ================================================
================================================ FILE: app/frontend/src/Homepage.svelte ================================================

Featured plugins

================================================ FILE: app/frontend/src/InstalledPluginView.svelte ================================================ {#if !err} {#if installedPlugin} {installedPlugin.title || installedPlugin.filename} {/if}
{#if refreshInterval}
Refresh every:
{/if}
{#if installedPlugin && installedPlugin.imageURL}
Screenshot of {installedPlugin.title}
{/if}
{#if installedPlugin}
{/if}
{#if installedPlugin && installedPlugin.vars && installedPlugin.enabled}
{/if} {#if installedPlugin}
{/if}
{/if} ================================================ FILE: app/frontend/src/PersonView.svelte ================================================ Contributors @{personDetails ? personDetails.person.githubUsername : ''} {#if personDetails}
Profile pic for {personDetails.person.name}

{#if personDetails.person.githubUsername != personDetails.person.name} {personDetails.person.name} {/if} @{personDetails.person.githubUsername}

{#if personDetails.person.bio}

{personDetails.person.bio}

{/if}

{/if} ================================================ FILE: app/frontend/src/PluginView.svelte ================================================ {plugin ? plugin.filename : ''} {#if plugin}

{#if plugin.imageURL}
Screenshot of {plugin.title}
{/if}
{/if} ================================================ FILE: app/frontend/src/PluginsList.svelte ================================================ {category ? category.text : ''}
================================================ FILE: app/frontend/src/backend/index.js ================================================ // @ts-check // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT const backend = { "main": { "CategoriesService": { /** * GetCategories * @returns {Promise|Error>} - Go Type: []main.Category */ "GetCategories": () => { return window.backend.main.CategoriesService.GetCategories(); }, } "CommandService": { /** * ClearCache * @returns {Promise} */ "ClearCache": () => { return window.backend.main.CommandService.ClearCache(); }, /** * OpenFile * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: error */ "OpenFile": (arg1) => { return window.backend.main.CommandService.OpenFile(arg1); }, /** * OpenPath * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: error */ "OpenPath": (arg1) => { return window.backend.main.CommandService.OpenPath(arg1); }, /** * OpenURL * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: error */ "OpenURL": (arg1) => { return window.backend.main.CommandService.OpenURL(arg1); }, /** * RefreshAllPlugins * @returns {Promise} */ "RefreshAllPlugins": () => { return window.backend.main.CommandService.RefreshAllPlugins(); }, /** * WindowHide * @returns {Promise} */ "WindowHide": () => { return window.backend.main.CommandService.WindowHide(); }, /** * WindowMinimise * @returns {Promise} */ "WindowMinimise": () => { return window.backend.main.CommandService.WindowMinimise(); }, } "PersonService": { /** * GetPersonDetails * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: *main.PersonDetails */ "GetPersonDetails": (arg1) => { return window.backend.main.PersonService.GetPersonDetails(arg1); }, } "PluginsService": { /** * GetFeaturedPlugins * @returns {Promise|Error>} - Go Type: []metadata.Plugin */ "GetFeaturedPlugins": () => { return window.backend.main.PluginsService.GetFeaturedPlugins(); }, /** * GetInstalledPluginMetadata * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: *main.InstalledPluginMetadata */ "GetInstalledPluginMetadata": (arg1) => { return window.backend.main.PluginsService.GetInstalledPluginMetadata(arg1); }, /** * GetInstalledPlugins * @returns {Promise|Error>} - Go Type: []plugins.InstalledPlugin */ "GetInstalledPlugins": () => { return window.backend.main.PluginsService.GetInstalledPlugins(); }, /** * GetPlugin * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: *metadata.Plugin */ "GetPlugin": (arg1) => { return window.backend.main.PluginsService.GetPlugin(arg1); }, /** * GetPlugins * @param {string} arg1 - Go Type: string * @returns {Promise|Error>} - Go Type: []metadata.Plugin */ "GetPlugins": (arg1) => { return window.backend.main.PluginsService.GetPlugins(arg1); }, /** * InstallPlugin * @param {any} arg1 - Go Type: metadata.Plugin * @returns {Promise} - Go Type: string */ "InstallPlugin": (arg1) => { return window.backend.main.PluginsService.InstallPlugin(arg1); }, /** * LoadVariableValues * @param {string} arg1 - Go Type: string * @returns {Promise} - Go Type: map[string]interface {} */ "LoadVariableValues": (arg1) => { return window.backend.main.PluginsService.LoadVariableValues(arg1); }, /** * SaveVariableValues * @param {string} arg1 - Go Type: string * @param {any} arg2 - Go Type: map[string]interface {} * @returns {Promise} - Go Type: error */ "SaveVariableValues": (arg1, arg2) => { return window.backend.main.PluginsService.SaveVariableValues(arg1, arg2); }, /** * SetEnabled * @param {string} arg1 - Go Type: string * @param {boolean} arg2 - Go Type: bool * @returns {Promise} - Go Type: string */ "SetEnabled": (arg1, arg2) => { return window.backend.main.PluginsService.SetEnabled(arg1, arg2); }, /** * SetRefreshInterval * @param {string} arg1 - Go Type: string * @param {any} arg2 - Go Type: plugins.RefreshInterval * @returns {Promise} - Go Type: *main.SetRefreshIntervalResult */ "SetRefreshInterval": (arg1, arg2) => { return window.backend.main.PluginsService.SetRefreshInterval(arg1, arg2); }, /** * UninstallPlugin * @param {any} arg1 - Go Type: main.UninstallPluginRequest * @returns {Promise} - Go Type: bool */ "UninstallPlugin": (arg1) => { return window.backend.main.PluginsService.UninstallPlugin(arg1); }, } } }; export default backend; ================================================ FILE: app/frontend/src/backend/package.json ================================================ { "name": "backend", "version": "1.0.0", "description": "Package to wrap backend method calls", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ================================================ FILE: app/frontend/src/elements/A.svelte ================================================ ================================================ FILE: app/frontend/src/elements/Breadcrumbs.svelte ================================================
================================================ FILE: app/frontend/src/elements/Button.svelte ================================================ ================================================ FILE: app/frontend/src/elements/Duration.svelte ================================================ {#if value.unit == 'milliseconds'} ms {:else} {/if} ================================================ FILE: app/frontend/src/elements/Error.svelte ================================================ {#if err} {#if isNotFoundError(err)}
404 Not found

← Use the navigation to find your way back.

{:else}
Something went wrong, this: {err} Open issue
×
{/if} {/if} ================================================ FILE: app/frontend/src/elements/KeyboardShortcuts.svelte ================================================ ================================================ FILE: app/frontend/src/elements/PluginCollection.svelte ================================================ {#if plugins}
{#each plugins as plugin}

{plugin.title}

{#if plugin.imageURL} Photo for {plugin.title} {/if}

{plugin.desc}

{#if plugin.authors && plugin.authors.length}
Profile picture for {plugin.authors[0].name}

@{plugin.authors[0].githubUsername}

{/if}
{/each}
{/if} ================================================ FILE: app/frontend/src/elements/PluginDetails.svelte ================================================ {#if plugin}
{#if $$slots.action}

{plugin.title || plugin.filename}

{:else}

{plugin.title || plugin.filename}

{/if}

by {#if plugin.authors} {#each plugin.authors as author} {author.name || author.githubUsername} {#if author.githubUsername} {/if} {/each} {:else} Anon. {/if}

{plugin.desc}

{#if $$slots.footer} {/if}
{/if} ================================================ FILE: app/frontend/src/elements/PluginSourceBrowser.svelte ================================================ {#if files && files.length > 0} {#each files as file}

Source code

{#if showEditButton}
{/if}
{file.content}
{/each} {/if} ================================================ FILE: app/frontend/src/elements/Spinner.svelte ================================================ ================================================ FILE: app/frontend/src/elements/Switch.svelte ================================================ ================================================ FILE: app/frontend/src/elements/VariableInput.svelte ================================================ {#if variable} {#if variable.type === 'string'} {:else if variable.type === 'boolean'} {:else if variable.type === 'number'} {:else if variable.type === 'select'} {/if} {/if} ================================================ FILE: app/frontend/src/elements/Variables.svelte ================================================ {#if variables && variables.length && values}
{#each variables as variable} {/each}
{variable.desc}

💡 You must refresh the plugin for changes to take effect.

{/if} ================================================ FILE: app/frontend/src/main.js ================================================ import App from './App.svelte' import { ready } from '@wails/runtime' import { routes } from 'svelte-hash-router' import Homepage from './Homepage.svelte' import PluginsList from './PluginsList.svelte' import PluginView from './PluginView.svelte' import PeopleView from './PersonView.svelte' import InstalledPluginView from './InstalledPluginView.svelte' let app; routes.set({ '/': Homepage, '/plugins/*': PluginsList, '/installed-plugins/*': InstalledPluginView, '/plugin-details/*': PluginView, '/people/:username': PeopleView, }) ready(() => { app = new App({ target: document.body, }); }); export default app; ================================================ FILE: app/frontend/src/pagedata.svelte ================================================ ================================================ FILE: app/frontend/src/rpc.svelte ================================================ ================================================ FILE: app/frontend/src/signals.svelte ================================================ ================================================ FILE: app/frontend/src/styles.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; * { cursor: default !important; } html { font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; -webkit-font-smoothing: subpixel-antialiased; font-size: 13px; } @media (prefers-color-scheme: dark) { html { background-color: rgb(24,24,28,0.5); } } html, body { height: 100%; width: 100%; position: fixed; overflow: hidden; overscroll-behavior-y: none; } /* from https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ */ .nice-wrapping { /* These are technically the same, but use both */ overflow-wrap: break-word; word-wrap: break-word; -ms-word-break: break-all; /* This is the dangerous one in WebKit, as it breaks things wherever */ word-break: break-all; /* Instead use this non-standard one: */ word-break: break-word; /* Adds a hyphen where the word breaks, if supported (No Blink) */ -ms-hyphens: auto; -moz-hyphens: auto; -webkit-hyphens: auto; hyphens: auto; } ::-webkit-scrollbar-track { background: transparent; border-radius: 2px; padding: 2px; padding-right: 10px; } ::-webkit-scrollbar { width: 12px; width: 6px; background: transparent; margin-right: 50px; } ::-webkit-scrollbar-thumb { border-radius: 2px; width: 6px; background: rgba(160,160,160,0.5); } /* Credit: https://stackoverflow.com/a/4407335 */ .noselect { cursor: default; -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */ -khtml-user-select: none; /* Konqueror HTML */ -moz-user-select: none; /* Old versions of Firefox */ -ms-user-select: none; /* Internet Explorer/Edge */ user-select: none; /* Non-prefixed version, currently supported by Chrome, Edge, Opera and Firefox */ } .allow-select { -webkit-touch-callout: initial; /* iOS Safari */ -webkit-user-select: initial; /* Safari */ -khtml-user-select: initial; /* Konqueror HTML */ -moz-user-select: initial; /* Old versions of Firefox */ -ms-user-select: initial; /* Internet Explorer/Edge */ user-select: initial; /* Non-prefixed version, currently supported by Chrome, Edge, Opera and Firefox */ } ================================================ FILE: app/frontend/src/waiters.svelte ================================================ ================================================ FILE: app/frontend/tailwind.config.js ================================================ const colors = require('tailwindcss/colors') module.exports = { purge: [ './src/**/*.html', './src/**/*.js', './src/**/*.svelte', ], theme: { colors: { transparent: 'transparent', gray: colors.neutral, blue: colors.blue, black: colors.black, white: colors.white, yellow: colors.yellow, }, }, plugins: [], } ================================================ FILE: app/go.mod ================================================ module github.com/matryer/xbar/app go 1.16 require ( github.com/go-ole/go-ole v1.2.5 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/uuid v1.2.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/klauspost/compress v1.13.1 // indirect github.com/leaanthony/go-ansi-parser v1.3.0 github.com/matryer/is v1.4.0 github.com/matryer/xbar/pkg/metadata v0.0.0-20210701102621-61a690f92a94 github.com/matryer/xbar/pkg/plugins v0.0.0-20210701102621-61a690f92a94 github.com/matryer/xbar/pkg/update v0.0.0-20210701102621-61a690f92a94 github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/errors v0.9.1 github.com/wailsapp/wails/v2 v2.0.0-alpha.74 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect nhooyr.io/websocket v1.8.7 // indirect ) replace github.com/matryer/xbar/pkg/plugins => ../pkg/plugins replace github.com/matryer/xbar/pkg/metadata => ../pkg/metadata replace github.com/matryer/xbar/pkg/update => ../pkg/update //replace github.com/wailsapp/wails/v2 => ../../wails/v2 ================================================ FILE: app/go.sum ================================================ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU= github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= github.com/leaanthony/debme v1.1.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= github.com/leaanthony/go-ansi-parser v1.2.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/leaanthony/go-ansi-parser v1.3.0 h1:8CRGdyxf2/cCiBZmYqpFkeTGcsFswDcVeB2SZKYUnbo= github.com/leaanthony/go-ansi-parser v1.3.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= github.com/leaanthony/gosod v1.0.1/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU= github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw= github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c= github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk= github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/wailsapp/wails/v2 v2.0.0-alpha.74 h1:h7K/gM2uR8HJQwb+VZYSTsbMqPz1O2Dx6jn9FFBA20o= github.com/wailsapp/wails/v2 v2.0.0-alpha.74/go.mod h1:BFBtJpsnU9bMYfqhotj5wYn5EIGBXXJl5x+2ZT9UrKw= github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28= github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/ztrue/tracerr v0.3.0 h1:lDi6EgEYhPYPnKcjsYzmWw4EkFEoA/gfe+I9Y5f+h6Y= github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= ================================================ FILE: app/incoming_urls.go ================================================ package main import ( "net/url" "strings" "github.com/pkg/errors" ) type incomingURL struct { // Action is the action to take. Action string // Params are the parameters for the action. Params url.Values } // parseIncomingURL parses an incoming xbar:// URL. func parseIncomingURL(urlStr string) (incomingURL, error) { var inURL incomingURL u, err := url.Parse(urlStr) if err != nil { return inURL, err } if u.Scheme != "xbar" && u.Host != "app.xbarapp.com" { return inURL, errors.New("not an xbar:// url") } inURL.Action = strings.Trim(u.Path, "/") inURL.Params = u.Query() switch inURL.Action { case "openPlugin": case "refreshPlugin": case "refreshAllPlugins": default: // not ok return inURL, errors.Errorf("unsupported action %q", inURL.Action) } return inURL, nil } ================================================ FILE: app/incoming_urls_test.go ================================================ package main import ( "testing" "github.com/matryer/is" ) func TestParseIncomingURL(t *testing.T) { is := is.New(t) result, err := parseIncomingURL(`xbar://app.xbarapp.com/openPlugin?path=IoT/homebridge.10s.py`) is.NoErr(err) is.Equal(result.Action, "openPlugin") is.Equal(result.Params.Get("path"), "IoT/homebridge.10s.py") result, err = parseIncomingURL(`xbar://app.xbarapp.com/refreshPlugin?path=cycle_text_and_detail`) is.NoErr(err) is.Equal(result.Action, "refreshPlugin") is.Equal(result.Params.Get("path"), "cycle_text_and_detail") result, err = parseIncomingURL(`xbar://app.xbarapp.com/refreshAllPlugins`) is.NoErr(err) is.Equal(result.Action, "refreshAllPlugins") result, err = parseIncomingURL(`xbar://app.xbarapp.com/nope?path=cycle_text_and_detail`) is.True(err != nil) } ================================================ FILE: app/main.go ================================================ package main import ( _ "embed" "fmt" "os" "github.com/pkg/errors" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/logger" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/mac" ) //go:embed .version var version string func main() { println("xbar", version) if err := run(); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } println("xbar exited") } func run() error { app, err := newApp() if err != nil { return errors.Wrap(err, "newApp") } wailsLogLevel := logger.ERROR app.Verbose = true if app.Verbose { wailsLogLevel = logger.DEBUG } err = wails.Run(&options.App{ Title: "xbar", Width: 1080, Height: 700, MinWidth: 800, MinHeight: 600, StartHidden: true, HideWindowOnClose: true, Mac: &mac.Options{ WebviewIsTransparent: true, WindowBackgroundIsTranslucent: true, TitleBar: mac.TitleBarHiddenInset(), Menu: app.appMenu, ActivationPolicy: mac.NSApplicationActivationPolicyAccessory, URLHandlers: map[string]func(string){ // xbar://... "xbar": app.handleIncomingURL, }, }, ContextMenus: app.contextMenus, LogLevel: wailsLogLevel, Startup: app.Start, Shutdown: app.Shutdown, Bind: []interface{}{ app.PersonService, app.CategoriesService, app.PluginsService, app.CommandService, }, }) if err != nil { return err } return nil } ================================================ FILE: app/menu_parser.go ================================================ package main import ( "context" "fmt" "strconv" "github.com/leaanthony/go-ansi-parser" "github.com/matryer/xbar/pkg/plugins" "github.com/wailsapp/wails/v2/pkg/menu" "github.com/wailsapp/wails/v2/pkg/menu/keys" ) // MenuParser translates xbar items into Wails menu items. type MenuParser struct{} // NewMenuParser makes a new MenuParser. func NewMenuParser() *MenuParser { return &MenuParser{} } // ParseItems parses the items, returning the new menu. func (m MenuParser) ParseItems(ctx context.Context, items []*plugins.Item) *menu.Menu { if len(items) == 0 { return nil } theMenu := menu.NewMenu() for _, item := range items { if item.Params.Separator { theMenu.Append(menu.Separator()) continue } menuItem := m.ParseMenuItem(ctx, item) theMenu.Append(menuItem) if item.Alternate != nil { menuItem = m.ParseMenuItem(ctx, item.Alternate) theMenu.Append(menuItem) } } return theMenu } // ParseMenuItem parses a single item, returning the new menu. func (m MenuParser) ParseMenuItem(ctx context.Context, item *plugins.Item) *menu.MenuItem { var err error displayText := item.DisplayText() itemAction := item.Action() menuItem := menu.Text(displayText, nil, func(_ *menu.CallbackData) { if itemAction == nil { return } itemAction(ctx) }) if item.Text != displayText { tooltip := item.Text if item.Params.ANSI { tooltip, err = strconv.Unquote(`"` + tooltip + `"`) if err != nil { tooltip = item.Text } } if ansi.HasEscapeCodes(tooltip) { menuItem.Tooltip, err = ansi.Cleanse(tooltip) if err != nil { menuItem.Tooltip = err.Error() } } else { menuItem.Tooltip = tooltip } } if item.Params.Key != "" { acc, err := keys.Parse(item.Params.Key) if err != nil { // show the error in the menu menuItem.Label = fmt.Sprintf("error: %s", err) } menuItem.Accelerator = acc } menuItem.Image = item.Params.Image menuItem.FontName = item.Params.Font menuItem.FontSize = item.Params.Size menuItem.RGBA = item.Params.Color // Check for template image if item.Params.TemplateImage != "" { menuItem.Image = item.Params.TemplateImage // get template images working on all macOS versions menuItem.MacTemplateImage = true } menuItem.MacAlternate = item.Params.Alternate if item.Params.Dropdown == false { menuItem.Hidden = true } if len(item.Items) > 0 { // subitems menuItem.SubMenu = m.ParseItems(ctx, item.Items) } if itemAction == nil && menuItem.SubMenu == nil { // no action and no submenu, disable it. menuItem.Disabled = true } if item.Params.Disabled { // explicitly disabled menuItem.Disabled = true } return menuItem } ================================================ FILE: app/menu_parser_test.go ================================================ package main import ( "context" "encoding/json" "testing" "time" "github.com/matryer/is" "github.com/matryer/xbar/pkg/plugins" "github.com/wailsapp/wails/v2/pkg/menu" ) func TestMenuParser(t *testing.T) { is := is.New(t) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() plugin := &plugins.Plugin{} items := []*plugins.Item{ { Plugin: plugin, Text: "one", Items: []*plugins.Item{ {Text: "sub1"}, {Text: "sub2"}, {Text: "sub3"}, }, }, { Plugin: plugin, Text: "two", Params: plugins.ItemParams{ Font: "Courier New", Size: 26, Key: "CmdOrCtrl+Shift+K", }, }, { Plugin: plugin, Text: "three", }, { Plugin: plugin, Text: "four", Params: plugins.ItemParams{ Href: "https://xbarapp.com", }, }, { Plugin: plugin, Text: "five", Params: plugins.ItemParams{ Shell: "echo", ShellParams: []string{"hi"}, }, }, { Plugin: plugin, Text: "six", Params: plugins.ItemParams{ Refresh: true, }, }, { Plugin: plugin, Text: "seven but this will be truncated", Params: plugins.ItemParams{ Refresh: true, Length: 5, }, }, { Plugin: plugin, Text: "Template Image", Params: plugins.ItemParams{ TemplateImage: "base64stuff", }, }, { Plugin: plugin, Text: "Non Alternate", Alternate: &plugins.Item{ Text: "Alternate", Params: plugins.ItemParams{ Alternate: true, }, }, }, { Text: "\033[0;38;2;255;0;0mA\033[0;38;2;255;127;0mN\033[0;38;2;255;255;0mS\033[0;38;2;0;255;0mI\u001B[0m", Params: plugins.ItemParams{ Refresh: true, }, }, } menuitems := NewMenuParser().ParseItems(ctx, items) is.Equal(len(menuitems.Items), 11) // len(menuitems.Items) is.Equal(menuitems.Items[0].Label, "one") is.Equal(menuitems.Items[0].Disabled, false) is.True(menuitems.Items[0].SubMenu != nil) is.Equal(len(menuitems.Items[0].SubMenu.Items), 3) is.Equal(menuitems.Items[1].Label, "two") is.Equal(menuitems.Items[1].Disabled, true) is.Equal(menuitems.Items[1].FontName, "Courier New") is.Equal(menuitems.Items[1].FontSize, 26) is.True(menuitems.Items[1].Accelerator != nil) is.Equal(menuitems.Items[1].Accelerator.Key, "k") is.Equal(len(menuitems.Items[1].Accelerator.Modifiers), 2) // len(modifiers) is.Equal(menuitems.Items[2].Label, "three") is.Equal(menuitems.Items[2].Disabled, true) is.Equal(menuitems.Items[3].Label, "four") is.Equal(menuitems.Items[3].Disabled, false) is.Equal(menuitems.Items[4].Label, "five") is.Equal(menuitems.Items[4].Disabled, false) is.Equal(menuitems.Items[5].Label, "six") is.Equal(menuitems.Items[5].Disabled, false) is.Equal(menuitems.Items[6].Label, "seve…") is.Equal(menuitems.Items[6].Tooltip, "seven but this will be truncated") is.Equal(menuitems.Items[7].Label, "Template Image") is.Equal(menuitems.Items[7].Image, "base64stuff") //is.Equal(menuitems.Items[7].MacTemplateImage, true) is.Equal(menuitems.Items[8].Label, "Non Alternate") is.Equal(menuitems.Items[9].Label, "Alternate") is.Equal(menuitems.Items[9].MacAlternate, true) is.Equal(menuitems.Items[10].Tooltip, "ANSI") } func JSON(menu *menu.Menu, is *is.I) string { data, err := json.Marshal(menu) is.NoErr(err) return string(data) } ================================================ FILE: app/package.json ================================================ {} ================================================ FILE: app/package.sh ================================================ #!/bin/bash set -e VERSION=`git describe --tags` # usage: # git tag -a v0.1.0 -m "release tag." # git push origin v0.1.0 # ./build.sh # functions requeststatus() { # $1: requestUUID requestUUID=${1?:"need a request UUID"} req_status=$(xcrun altool --notarization-info "$requestUUID" \ --username "${AC_USERNAME}" \ --password "${AC_PASSWORD}" 2>&1 \ | awk -F ': ' '/Status:/ { print $2; }' ) echo "$req_status" } notarizefile() { # $1: path to file to notarize, $2: identifier filepath=${1:?"need a filepath"} identifier=${2:?"need an identifier"} # upload file echo "## uploading $filepath for notarization" requestUUID=$(xcrun altool --notarize-app \ --primary-bundle-id "$identifier" \ --username "${AC_USERNAME}" \ --password "${AC_PASSWORD}" \ --asc-provider "${AC_PROVIDER}" \ --file "$filepath" 2>&1 \ | awk '/RequestUUID/ { print $NF; }') echo "Notarization RequestUUID: $requestUUID" if [[ $requestUUID == "" ]]; then echo "could not upload for notarization" exit 1 fi # wait for status to be not "in progress" any more request_status="in progress" while [[ "$request_status" == "in progress" ]]; do echo -n "waiting... " sleep 10 request_status=$(requeststatus "$requestUUID") echo "$request_status" done # print status information xcrun altool --notarization-info "$requestUUID" \ --username "${AC_USERNAME}" \ --password "${AC_PASSWORD}" echo if [[ $request_status != "success" ]]; then echo "## could not notarize $filepath" exit 1 fi } echo "" echo " xbar ${VERSION}..." echo "" echo -n $VERSION > .version # run all tests ./test.sh rm -rf ./build/bin sed "s/0.0.0/${VERSION}/" ./build/darwin/Info.plist.src > ./build/darwin/Info.plist #CGO_LDFLAGS=-mmacosx-version-min=10.13 wails build -package -production -platform darwin/amd64 -o xbar #CGO_LDFLAGS=-mmacosx-version-min=10.13 wails build -package -production -platform darwin/arm64 -o xbar CGO_LDFLAGS=-mmacosx-version-min=10.13 wails build -package -production -platform darwin/universal -o xbar cd ./build/bin/ echo "Signing the binary..." codesign -s "${XBAR_SIGNING_IDENTITY}" -o runtime -v "./xbar.app/Contents/MacOS/xbar" echo "Creating DMG..." create-dmg ./xbar.app --overwrite --identity="${XBAR_SIGNING_IDENTITY}" --dmg-title "Install xbar" mv xbar*.dmg "xbar.${VERSION}.dmg" #echo "TARing..." #tar -czvf xbar.${VERSION}.tar.gz ./xbar.app echo "Zipping..." zip -r xbar.zip ./xbar.app mv xbar.zip "xbar.${VERSION}.zip" #xcrun notarytool submit "xbar.${VERSION}.zip" --keychain-profile "${AC_PASSWORD}" --wait echo "Notorizing..." notarizefile "xbar.${VERSION}.zip" "com.xbarapp.app" notarizefile "xbar.${VERSION}.dmg" "com.xbarapp.app" xcrun stapler staple "xbar.${VERSION}.dmg" rm -rf ./build/bin/xbar.app open . ================================================ FILE: app/person_service.go ================================================ package main import ( "context" "encoding/json" "io/ioutil" "net/http" "github.com/matryer/xbar/pkg/metadata" ) // PersonService provides people related functionality. type PersonService struct { baseURL string Client *http.Client } // NewPersonService makes a new PersonService. func NewPersonService(client *http.Client) *PersonService { return &PersonService{ Client: client, baseURL: "https://xbarapp.com/docs", } } // PersonDetails are details relating to a human. type PersonDetails struct { Person *metadata.Person `json:"person"` Plugins []metadata.Plugin `json:"plugins"` } // GetPersonDetails gets details about a human by their GitHub username. func (p *PersonService) GetPersonDetails(githubUsername string) (*PersonDetails, error) { req, err := http.NewRequest("GET", p.baseURL+"/contributors/"+githubUsername+".json", nil) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(req.Context(), apiRequestTimeout) defer cancel() req = req.WithContext(ctx) res, err := p.Client.Do(req) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } var payload *PersonDetails err = json.Unmarshal(body, &payload) if err != nil { return nil, err } return payload, nil } ================================================ FILE: app/plugins_service.go ================================================ package main import ( "context" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "os" "path/filepath" "sync" "time" "github.com/matryer/xbar/pkg/metadata" "github.com/matryer/xbar/pkg/plugins" "github.com/pkg/errors" wails "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options/dialog" ) // PluginsService access remote plugin information. type PluginsService struct { runtime *wails.Runtime baseURL string client *http.Client // osLock is used whenever there are operating system changes, // like renaming files. This prevents overlap and potentially strange // state. // todo: move this to a better place? osLock sync.Mutex // OnRefresh is called whenever the menus should // be updated. OnRefresh func() } // NewPluginsService makes a new PluginsService. func NewPluginsService(client *http.Client, baseURL string) *PluginsService { return &PluginsService{ baseURL: baseURL, client: client, } } // GetPlugins gets the plugins for the specified category. func (p *PluginsService) GetPlugins(categoryPath string) ([]metadata.Plugin, error) { req, err := http.NewRequest("GET", p.baseURL+categoryPath+"/plugins.json", nil) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(req.Context(), apiRequestTimeout) defer cancel() req = req.WithContext(ctx) res, err := p.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } var payload struct { Plugins []metadata.Plugin } err = json.Unmarshal(body, &payload) if err != nil { return nil, err } return payload.Plugins, nil } // GetPlugin gets the plugin metadata for a plugin. func (p *PluginsService) GetPlugin(pluginPath string) (*metadata.Plugin, error) { req, err := http.NewRequest("GET", p.baseURL+pluginPath+".json", nil) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(req.Context(), apiRequestTimeout) defer cancel() req = req.WithContext(ctx) res, err := p.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } var payload struct { Plugin *metadata.Plugin } err = json.Unmarshal(body, &payload) if err != nil { return nil, err } return payload.Plugin, nil } // GetFeaturedPlugins gets the featured plugins. func (p *PluginsService) GetFeaturedPlugins() ([]metadata.Plugin, error) { req, err := http.NewRequest("GET", p.baseURL+"featured-plugins.json", nil) if err != nil { return nil, err } ctx, cancel := context.WithTimeout(req.Context(), apiRequestTimeout) defer cancel() req = req.WithContext(ctx) res, err := p.client.Do(req) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } var payload struct { Plugins []metadata.Plugin } err = json.Unmarshal(body, &payload) if err != nil { return nil, err } return payload.Plugins, nil } // GetInstalledPlugins gets the installed plugins. func (p *PluginsService) GetInstalledPlugins() ([]plugins.InstalledPlugin, error) { p.osLock.Lock() defer p.osLock.Unlock() return plugins.GetInstalledPlugins(pluginDirectory) } // InstallPlugin installs the plugin described by the provided metadata. func (p *PluginsService) InstallPlugin(plugin metadata.Plugin) (string, error) { defer p.OnRefresh() p.osLock.Lock() defer p.osLock.Unlock() if p.runtime != nil { response, err := p.runtime.Dialog.Message(&dialog.MessageDialog{ Type: "Question", Title: "Install plugin", Message: fmt.Sprintf("Are you sure you want to install %s?", plugin.Title), Buttons: []string{"Install", "Cancel"}, DefaultButton: "Install", CancelButton: "Cancel", }) if err != nil { return "", err } switch response { case "Install": // continue case "Cancel": return "", nil } } installer := &plugins.Installer{ Client: &http.Client{ Timeout: 1 * time.Minute, }, PluginDir: pluginDirectory, } pluginPath := "https://xbarapp.com/docs/plugins/" + plugin.Path + ".json" pluginPathURL, err := url.Parse(pluginPath) if err != nil { return "", errors.Wrapf(err, "parse URL: %s", pluginPath) } installedPluginPath, err := installer.Install(pluginPathURL) if err != nil { return "", errors.Wrap(err, "Install") } tickOS() // wait a beat return installedPluginPath, nil } // UninstallPluginRequest is the object to send when uninstalling an // installed plugin. type UninstallPluginRequest struct { Path string Title string } // UninstallPlugin removes a plugin. func (p *PluginsService) UninstallPlugin(installedPluginInfo UninstallPluginRequest) (bool, error) { defer p.OnRefresh() p.osLock.Lock() defer p.osLock.Unlock() if p.runtime != nil { response, err := p.runtime.Dialog.Message(&dialog.MessageDialog{ Type: "Question", Title: "Uninstall plugin", Message: fmt.Sprintf("Are you sure you want to remove %s?\n\nThis cannot be undone.", installedPluginInfo.Title), Buttons: []string{"Uninstall", "Cancel"}, DefaultButton: "Uninstall", CancelButton: "Cancel", }) if err != nil { return false, err } switch response { case "Uninstall": // continue case "Cancel": return false, nil } } installer := &plugins.Installer{ PluginDir: pluginDirectory, } err := installer.Uninstall(installedPluginInfo.Path) if err != nil { return false, errors.Wrap(err, "uninstall") } tickOS() // wait a beat return true, nil } // InstalledPluginMetadata is the metadata extracted from an installed // plugin. type InstalledPluginMetadata struct { Plugin metadata.Plugin `json:"plugin"` Enabled bool `json:"enabled"` RefreshInterval plugins.RefreshInterval `json:"refreshInterval"` Error string `json:"error,omitempty"` } // GetInstalledPluginMetadata loads the plugin metadata from a plugin file. func (p *PluginsService) GetInstalledPluginMetadata(installedPluginPath string) (*InstalledPluginMetadata, error) { p.osLock.Lock() defer p.osLock.Unlock() filename := filepath.Base(installedPluginPath) b, err := os.ReadFile(filepath.Join(pluginDirectory, installedPluginPath)) if err != nil { return nil, err } md, err := metadata.Parse(metadata.DebugfNoop, filename, string(b)) if err != nil { return nil, err } md.Path = installedPluginPath response := &InstalledPluginMetadata{ Plugin: md, Enabled: plugins.IsPluginEnabled(installedPluginPath), } response.RefreshInterval, err = plugins.ParseFilenameInterval(filename) if err != nil { response.Error = err.Error() } return response, nil } // LoadVariableValues loads the values for an installed plugin. func (p *PluginsService) LoadVariableValues(installedPluginPath string) (map[string]interface{}, error) { p.osLock.Lock() defer p.osLock.Unlock() defer tickOS() // wait a beat return plugins.LoadVariableValues(pluginDirectory, installedPluginPath) } // SaveVariableValues saves the values for an installed plugin. func (p *PluginsService) SaveVariableValues(installedPluginPath string, values map[string]interface{}) error { p.osLock.Lock() defer p.osLock.Unlock() defer tickOS() // wait a beat return plugins.SaveVariableValues(pluginDirectory, installedPluginPath, values) } // SetEnabled sets a plugin to enabled or disabled state, depending on the value of // the enabled parameter. func (p *PluginsService) SetEnabled(installedPluginPath string, enabled bool) (string, error) { defer p.OnRefresh() p.osLock.Lock() defer p.osLock.Unlock() newPath, err := plugins.SetEnabled(pluginDirectory, installedPluginPath, enabled) if err != nil { return "", err } tickOS() // wait a beat return newPath, err } // SetRefreshIntervalResult is the refresh interval result returned from SetRefreshInterval. type SetRefreshIntervalResult struct { InstalledPluginPath string `json:"installedPluginPath"` RefreshInterval plugins.RefreshInterval `json:"refreshInterval"` } // SetRefreshInterval updates the refresh interval for a plugin. func (p *PluginsService) SetRefreshInterval(installedPluginPath string, refreshInterval plugins.RefreshInterval) (*SetRefreshIntervalResult, error) { defer p.OnRefresh() p.osLock.Lock() defer p.osLock.Unlock() newPath, newInterval, err := plugins.SetRefreshInterval(pluginDirectory, installedPluginPath, refreshInterval) if err != nil { return nil, err } result := &SetRefreshIntervalResult{ InstalledPluginPath: newPath, RefreshInterval: newInterval, } tickOS() // wait a beat return result, nil } ================================================ FILE: app/settings.go ================================================ package main import ( "encoding/json" "io/ioutil" "os" "path/filepath" "sync" "github.com/pkg/errors" ) type settings struct { sync.Mutex path string `json:"-"` // AutoUpdate indicates that xbar should automatically // update itself. AutoUpdate bool `json:"autoupdate"` Terminal struct { AppleScriptTemplate3 string `json:"appleScriptTemplate3"` } `json:"terminal"` } func (s *settings) setDefaults() { if s.Terminal.AppleScriptTemplate3 == "" { s.Terminal.AppleScriptTemplate3 = ` set quotedScriptName to quoted form of "{{ .Command }}" {{ if .Params }} set commandLine to {{ .Vars }} & " " & quotedScriptName & " " & {{ .Params }} {{ else }} set commandLine to {{ .Vars }} & " " & quotedScriptName {{ end }} if application "Terminal" is running then tell application "Terminal" do script commandLine activate end tell else tell application "Terminal" do script commandLine in window 1 activate end tell end if ` } } func loadSettings(path string) (*settings, error) { s := &settings{ path: path, } b, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { // file not found - it's ok, just use defaults s.setDefaults() return s, nil } return nil, errors.Wrap(err, "ReadFile") } err = json.Unmarshal(b, s) if err != nil { return nil, errors.Wrap(err, "Unmarshal") } s.setDefaults() return s, nil } func (s *settings) save() error { s.Lock() defer s.Unlock() s.setDefaults() b, err := json.MarshalIndent(s, "", "\t") if err != nil { return errors.Wrap(err, "MarshalIndent") } err = os.MkdirAll(filepath.Dir(s.path), 0777) if err != nil { return errors.Wrap(err, "MkdirAll") } err = ioutil.WriteFile(s.path, b, 0777) if err != nil { return errors.Wrap(err, "WriteFile") } return nil } ================================================ FILE: app/settings_test.go ================================================ package main import ( "os" "path/filepath" "testing" "github.com/matryer/is" ) func TestSettings(t *testing.T) { is := is.New(t) t.Cleanup(func() { os.RemoveAll(filepath.Join("testdata", "settings.json")) }) s, err := loadSettings(filepath.Join("testdata", "settings.json")) is.NoErr(err) s.AutoUpdate = true err = s.save() is.NoErr(err) s, err = loadSettings(filepath.Join("testdata", "settings.json")) is.NoErr(err) is.Equal(s.AutoUpdate, true) } ================================================ FILE: app/test.sh ================================================ #!/bin/bash set -e VERSION=`git describe --tags` echo -n $VERSION > .version go test cd ../pkg/metadata go test cd ../../app cd ../pkg/plugins go test cd ../../app cd ../pkg/update go test cd ../../app # cd ../tools/sitegen # echo -n $VERSION > .version # go test -short # cd ../../app ================================================ FILE: app/wails.json ================================================ { "name": "xbar", "outputfilename": "xbar", "html": "frontend/public/index.html", "js": "frontend/public/bundle.js", "css": "frontend/public/bundle.css", "frontend:build": "npm run build", "frontend:install": "npm install" } ================================================ FILE: archive/bitbar/.gitignore ================================================ # xcode noise build/* *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata profile *.moved-aside # osx noise .DS_Store profile build ================================================ FILE: archive/bitbar/.gitmodules ================================================ [submodule "App/Vendor/STPrivilegedTask"] path = App/Vendor/STPrivilegedTask url = https://github.com/sveinbjornt/STPrivilegedTask.git [submodule "App/Vendor/DateTools"] path = App/Vendor/DateTools url = https://github.com/MatthewYork/DateTools.git [submodule "App/Vendor/LaunchAtLoginController"] path = App/Vendor/LaunchAtLoginController url = https://github.com/ksuther/LaunchAtLoginController--with-ARC-.git [submodule "App/Vendor/AHProxySettings"] path = App/Vendor/AHProxySettings url = https://github.com/eahrold/AHProxySettings.git [submodule "App/Vendor/NSStringEmojize"] path = App/Vendor/NSStringEmojize url = https://github.com/diy/NSStringEmojize.git [submodule "App/Vendor/Sparkle"] path = App/Vendor/Sparkle url = https://github.com/sparkle-project/Sparkle.git ================================================ FILE: archive/bitbar/.travis.yml ================================================ language: objective-c osx_image: xcode11.2 xcode_project: App/BitBar.xcodeproj xcode_scheme: BitBar xcode_sdk: macosx10.11 #before_install: #- brew update #- brew outdated xctool || brew upgrade xctool script: - CERT_P12=Certificate.p12 - echo "$CERT_BASE64" | base64 --decode > $CERT_P12 - KEYCHAIN=build.keychain - security create-keychain -p travis $KEYCHAIN - security default-keychain -s $KEYCHAIN - security unlock-keychain -p travis $KEYCHAIN - security set-keychain-settings -t 3600 -u $KEYCHAIN - security import $CERT_P12 -k $KEYCHAIN -P "$CERT_PW" -T /usr/bin/codesign - 'IDENTITY="Developer ID Application: Code and That Ltd (B3T8QSC4HG)"' - if [ -n "$TRAVIS_TAG" ]; then xctool -project $TRAVIS_XCODE_PROJECT -scheme $TRAVIS_XCODE_SCHEME -sdk $TRAVIS_XCODE_SDK -configuration Release OBJROOT=$PWD/build SYMROOT=$PWD/build ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="$IDENTITY" build analyze; else xctool -project $TRAVIS_XCODE_PROJECT -scheme $TRAVIS_XCODE_SCHEME -sdk $TRAVIS_XCODE_SDK -configuration Release OBJROOT=$PWD/build SYMROOT=$PWD/build ONLY_ACTIVE_ARCH=NO build analyze -failOnWarnings; fi - security delete-keychain $KEYCHAIN before_deploy: - OUTPUTDIR="$PWD/build/Release" - cd $OUTPUTDIR - ditto -c -k --sequesterRsrc --keepParent "BitBar.app" "BitBar-$TRAVIS_TAG.zip" - ditto -c -k --sequesterRsrc --keepParent "BitBarDistro.app" "BitBarDistro-$TRAVIS_TAG.zip" deploy: provider: releases api_key: secure: VB7wqPRAmwRxX1ugTss4lWdcCjMO4+9yYuvkSKIhRz5PcKFTdgIE5Ol29wssYSlEnk1D5ZqeCJBe3t2qowrxOKHWKJRxH5r4fbgYAYnbk9/nWsMLgWDn1mo4nYa0sD4GyMUDY9JqqmtBY3nZ2pYcJ0L1LmxUU+EHViwcBQz6G4Y= file: - $OUTPUTDIR/BitBar-$TRAVIS_TAG.zip - $OUTPUTDIR/BitBarDistro-$TRAVIS_TAG.zip skip_cleanup: true on: repo: matryer/bitbar tags: true after_deploy: # Rebuild the Sparkle feed - curl -s -X POST -H "Authorization:token $GH_TOKEN" -H Accept:application/vnd.github.mister-fantastic-preview https://api.github.com/repos/matryer/bitbar/pages/builds # Update the Sparkle feed cache - brew install jq - PRERELEASE=$(curl -s -H "Authorization:token $GH_TOKEN" "https://api.github.com/repos/matryer/bitbar/releases/tags/$TRAVIS_TAG" | jq .prerelease) - echo "$PRERELEASE" - if [ "$PRERELEASE" = true ]; then FEED=(beta distro-beta); else FEED=(bitbar distro); fi - while :; do STATUS=$(curl -s -H "Authorization:token $GH_TOKEN" https://api.github.com/repos/matryer/bitbar/pages | jq .status); if [ "$STATUS" != '"queued"' ] && [ "$STATUS" != '"building"' ]; then echo "$STATUS"; break; fi; sleep 1; done - curl -s -H "X-RELOAD-KEY:$SPARKLE_UPDATE_KEY" -D - -o /dev/null "https://bitbarapp.com/feeds/${FEED[0]}/reload" - curl -s -H "X-RELOAD-KEY:$SPARKLE_UPDATE_KEY" -D - -o /dev/null "https://bitbarapp.com/feeds/${FEED[1]}/reload" notifications: slack: secure: TpJVJf/NWxDvHxPjaQJnVg4vlW6JeLQ7eWadzFo7sUNSdB7Tui703AvYjG8tZTlk2lJ/4bV4jKgz8+rSElkeGsfPLTgDU33IDmG3V9o6MrAeV/ZQL37793bj+zJFxNuOkoIBaBQt1aNDTuIDUB97MNv02Vklb1M7Yd44NRVXdHk= ================================================ FILE: archive/bitbar/App/BitBar/App.xib ================================================ ================================================ FILE: archive/bitbar/App/BitBar/AppDelegate.m ================================================ // // AppDelegate.m // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // #import "NSUserDefaults+Settings.h" #import "LaunchAtLoginController.h" #import "PluginManager.h" #import "Plugin.h" #import #import @interface AppDelegate : NSObject @property (assign) IBOutlet NSWindow *window; @property PluginManager *pluginManager; // plugin download @property NSURLDownload *download; @property NSString *destinationPath; @property NSString *suggestedDestinationPath; @end @implementation AppDelegate - (NSArray*) otherCopies { return [NSRunningApplication runningApplicationsWithBundleIdentifier:NSBundle.mainBundle.bundleIdentifier]; } - (void)applicationWillFinishLaunching:(NSNotification *)n { NSString *feedURLString; #ifdef DISTRO feedURLString = @"https://bitbarapp.com/feeds/distro"; #else feedURLString = @"https://bitbarapp.com/feeds/bitbar"; #endif SUUpdater *updater = [SUUpdater sharedUpdater]; updater.delegate = self; updater.automaticallyChecksForUpdates = YES; updater.feedURL = [NSURL URLWithString:feedURLString]; updater.sendsSystemProfile = YES; // register custom url scheme handler [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; if (self.otherCopies.count <= 1) return; NSModalResponse runm = [[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.", NSBundle.mainBundle.infoDictionary[(NSString *)kCFBundleNameKey]] defaultButton:@"Quit" alternateButton:@"Kill others" otherButton:nil informativeTextWithFormat:@"Quit, or kill the other copy(ies)?"] runModal]; runm == 1 ? [NSApp terminate:nil] : ({ for ( NSRunningApplication *app in self.otherCopies) if (app.processIdentifier != NSProcessInfo.processInfo.processIdentifier) [app terminate]; }); } - (void) applicationDidFinishLaunching:(NSNotification*)n { // enable usage of Safari's WebInspector to debug HTML Plugins [NSUserDefaults.standardUserDefaults setBool:YES forKey:@"WebKitDeveloperExtras"]; [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: @selector(receiveWakeNote:) name: NSWorkspaceDidWakeNotification object: NULL]; if (DEFS.isFirstTimeAppRun) { LaunchAtLoginController *launcher = LaunchAtLoginController.new; if (!launcher.launchAtLogin) [launcher setLaunchAtLogin:YES]; DEFS.isFirstTimeAppRun = NO; } NSString* pluginsDirectory = DEFS.pluginsDirectory; // Test if we are running unit tests! NSString* testBundlePath = [NSProcessInfo processInfo].environment[@"XCInjectBundle"]; if (testBundlePath && [testBundlePath hasSuffix:@".xctest"]) { pluginsDirectory = [[[NSBundle bundleWithPath:testBundlePath] resourcePath] stringByAppendingPathComponent:@"TestPlugins"]; } // make a plugin manager [_pluginManager = [PluginManager.alloc initWithPluginPath:pluginsDirectory] setupAllPlugins]; } - (void) receiveWakeNote: (NSNotification*) note { [[self pluginManager] reset]; } - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { // check if plugins directory is set if (!DEFS.pluginsDirectory) return; // extract the url from the event and handle it NSString *URLString = [event paramDescriptorForKeyword:keyDirectObject].stringValue; URLString = [URLString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSString *prefix = @"bitbar://refreshPlugin?name="; if ([URLString hasPrefix:prefix]) { URLString = [URLString substringFromIndex:prefix.length]; NSArray *plugins = [self.pluginManager.plugins filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"name LIKE %@", URLString]]; [plugins makeObjectsPerformSelector:@selector(performRefreshNow) withObject:nil]; return; } // don't open plugins if user configuration is disabled if (DEFS.userConfigDisabled) return; prefix = @"bitbar://openPlugin?"; // skip urls that don't begin with our prefix if (![URLString hasPrefix:prefix]) return; URLString = [URLString substringFromIndex:prefix.length]; prefix = @"title="; NSString *title = nil; if ([URLString hasPrefix:prefix]) { URLString = [URLString substringFromIndex:prefix.length]; NSArray *components = [URLString componentsSeparatedByString:@"&"]; if (components.count < 2) return; title = components.firstObject; URLString = [[components subarrayWithRange:NSMakeRange(1, components.count - 1)] componentsJoinedByString:@"&"]; } prefix = @"src="; if (![URLString hasPrefix:prefix]) return; URLString = [URLString substringFromIndex:prefix.length]; BOOL trusted = NO; // if the plugin is at our repository, only display the filename if ([URLString hasPrefix:@"https://github.com/matryer/xbar-plugins/raw/master/"]) { trusted = YES; } NSAlert *alert = [[NSAlert alloc] init]; [alert addButtonWithTitle:@"Install"]; [alert addButtonWithTitle:@"Cancel"]; alert.messageText = [NSString stringWithFormat:@"Download and install the plugin %@?", trusted ? (title.length > 0 ? title : URLString.lastPathComponent) : [NSString stringWithFormat:@"at %@", URLString]]; if (trusted) { alert.informativeText = @"Only install plugins from trusted sources."; } else { alert.informativeText = @"CAUTION: This plugin is not from the official BitBar repository. We recommend that you only install plugins from trusted sources."; } if ([alert runModal] != NSAlertFirstButtonReturn) { // cancel clicked return; } [self.download cancel]; self.destinationPath = nil; self.suggestedDestinationPath = nil; // NSURLSession is not available below 10.9 :( self.download = [[NSURLDownload alloc] initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:URLString]] delegate:self]; } #pragma mark - NSURLDownload delegate - (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename { self.suggestedDestinationPath = [DEFS.pluginsDirectory stringByAppendingPathComponent:filename]; [download setDestination:self.suggestedDestinationPath allowOverwrite:NO]; } - (void)download:(NSURLDownload *)download didCreateDestination:(NSString *)path { self.destinationPath = path; } - (void)downloadDidFinish:(NSURLDownload *)download { if (self.destinationPath) { if (self.suggestedDestinationPath && ![self.suggestedDestinationPath isEqualToString:self.destinationPath]) { // overwrite file at suggested destination path [[NSFileManager defaultManager] removeItemAtPath:self.suggestedDestinationPath error:nil]; if ([[NSFileManager defaultManager] moveItemAtPath:self.destinationPath toPath:self.suggestedDestinationPath error:nil]) self.destinationPath = self.suggestedDestinationPath; } // ensure plugin is executable // `chmod +x plugin.sh` struct stat st; stat(self.destinationPath.UTF8String, &st); chmod(self.destinationPath.UTF8String, (st.st_mode & ALLPERMS) | S_IXUSR | S_IXGRP | S_IXOTH); } // refresh [self.pluginManager reset]; self.download = nil; self.destinationPath = nil; self.suggestedDestinationPath = nil; } - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error { NSAlert *alert = [[NSAlert alloc] init]; [alert addButtonWithTitle:@"OK"]; alert.messageText = @"Download failed"; alert.informativeText = error.localizedDescription; [alert runModal]; self.download = nil; self.destinationPath = nil; self.suggestedDestinationPath = nil; } #pragma mark - SUUpdater delegate // hack to disable the update prompt if user configuration is disabled - (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater { id driver; if (DEFS.userConfigDisabled || ((driver = [updater valueForKey:@"driver"]) && !driver)) return nil; SEL sel = NSSelectorFromString(@"bestItemFromAppcastItems:getDeltaItem:withHostVersion:comparator:"); NSArray *items = appcast.items; void *deltaUpdateItem = nil; id version = [[driver valueForKey:@"host"] valueForKey:@"version"]; id comparator = [driver valueForKey:@"versionComparator"]; void *item = nil; NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[driver class] methodSignatureForSelector:sel]]; inv.selector = sel; inv.target = [driver class]; if (items) [inv setArgument:&items atIndex:2]; [inv setArgument:&deltaUpdateItem atIndex:3]; if (version) [inv setArgument:&version atIndex:4]; if (comparator) [inv setArgument:&comparator atIndex:5]; [inv invoke]; if (deltaUpdateItem) { item = deltaUpdateItem; } else { [inv getReturnValue:&item]; } return (__bridge SUAppcastItem *)item; } @end int main(int argc, const char * argv[]) { return NSApplicationMain(argc, argv); } ================================================ FILE: archive/bitbar/App/BitBar/Base.lproj/MainMenu.xib ================================================ ================================================ FILE: archive/bitbar/App/BitBar/BitBar-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleSignature ???? CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLSchemes bitbar CFBundleVersion $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType public.app-category.productivity LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} LSUIElement NSHumanReadableCopyright Copyright ©2013-2016 Mat Ryer. All rights reserved. NSMainNibFile App NSPrincipalClass NSApplication ================================================ FILE: archive/bitbar/App/BitBar/CHANGELOG.md ================================================ # BitBar Changes ## v1.9.2 * Added Sparkle updater ## v1.9.1 * Bug fixes and performance enhancements ## v1.9 * Restored distributable BitBar * BitBar now requires OS X 10.7 to fix a crash on refresh and wake from sleep * Added support for single click action from the status bar (`href`, `bash` and `refresh`) * Added support for separators in sub menus * Added update check (Preferences menu will show you when there's a newer version available) * Various other bug fixes ## v1.8 * Added `emojize` parameter * Added sub menus * Added ANSI color code support and `ansi=false` option to turn it off * Significant bug fixes and performance enhancements ## v1.7 * Added `image` and `templateImage` parameters (Base64 encoded images) * Memory leak fixes and bug fixes ## v1.6 * Added [distributable BitBar](https://github.com/matryer/bitbar/blob/master/Docs/DistributingBitBar.md) to allow you to bundle a BitBar app with plugins, and distribute a tamper free version. * Added ability to refresh plugins remotely via the [URL scheme](https://github.com/matryer/bitbar/blob/master/Docs/URLScheme.md) - #149 and #216 * Integration with Slack - please join us: [![Slack Status](https://getbitbar.herokuapp.com/badge.svg)](https://getbitbar.herokuapp.com/) * Added ability to [hide Preferences menu](https://github.com/matryer/bitbar/blob/master/Docs/DistributingBitBar.md#settings). * Small UI improvements based on feedback from lovely users just like you * Updated dependencies to fix crash and memory leak * See a [complete list of all changes in this release](https://github.com/matryer/bitbar/compare/v1.5.1...master) ## v1.5 Features: * Added `trim=false` option to give plugin authors control of whitespace - #182 * Added `alternate=true` option to allow option key menu items - #218 * Tasks now run in background (preventing menu bar items from locking) - #181 * Plugins will reset on wake from sleep - #184 * Added URL scheme for opening and downloading plugins - #224 Other: * Work to address duplicate items - #21 * Updated "Browse plugins..." link to getbitbar.com * Improved docs * "Open at login" was getting reset every time - now it's remembered - #169 * Warnings and static analyzer errors are resolved ## v1.4 Features: * Made `$BitBarDarkMode` environment variable available to plugins - #155 * Ability to hide items from the dropdown - #102 * Selecting a Plugin folder (rather than having to navigate inside it) is enough - better UI - #120 * `Reset` renamed to `Refresh` to make it clearer - #82 * `refresh` parameter indicates that an item should issue the refresh of a plugin - #48 * Added support for `length` parameter - #131 Other: * Started tracking changes * Fixed vertical alignment - #153 * Numbers are now monospace - #148 * Removed unnecessary separators from BitBar menu - #143 * General bug fixes and improvements * Support spaces in paths * Fonts will be checked, and a default will be used if they're not available * Plugins now live in their own repo - #78 ================================================ FILE: archive/bitbar/App/BitBar/ExecutablePlugin.h ================================================ // // ExecutablePlugin.h // BitBar // // Created by Mathias Leppich on 22/01/14. // Copyright (c) 2014 Bit Bar. All rights reserved. // #import "Plugin.h" @interface ExecutablePlugin : Plugin @property (nonatomic, strong) NSTimer *lineCycleTimer; @property (nonatomic, strong) NSTimer *refreshTimer; - (BOOL) refreshContentByExecutingCommand; @end ================================================ FILE: archive/bitbar/App/BitBar/ExecutablePlugin.m ================================================ // // ExecutablePlugin.m // BitBar // // Created by Mathias Leppich on 22/01/14. // Copyright (c) 2014 Bit Bar. All rights reserved. // #import "ExecutablePlugin.h" #import "PluginManager.h" #import "NSTask+useSystemProxies.h" #import "NSUserDefaults+Settings.h" @implementation ExecutablePlugin - (BOOL) refreshContentByExecutingCommand { if (![[NSFileManager defaultManager] fileExistsAtPath:self.path]) { return NO; } NSTask *task = NSTask.new; [task setEnvironment:self.manager.environment]; [task setLaunchPath:self.path]; [task useSystemProxies]; NSPipe *stdoutPipe = [NSPipe pipe]; [task setStandardOutput:stdoutPipe]; NSPipe *stderrPipe = [NSPipe pipe]; [task setStandardError:stderrPipe]; @try { [task launch]; } @catch (NSException *e) { NSLog(@"Error when running %@: %@", self.name, e); self.lastCommandWasError = YES; self.content = @""; self.errorContent = e.reason; return NO; } NSData *stdoutData = [[stdoutPipe fileHandleForReading] readDataToEndOfFile]; NSData *stderrData = [[stderrPipe fileHandleForReading] readDataToEndOfFile]; [task waitUntilExit]; self.content = [NSString.alloc initWithData:stdoutData encoding:NSUTF8StringEncoding]; self.errorContent = [NSString.alloc initWithData:stderrData encoding:NSUTF8StringEncoding]; // failure if ([task terminationStatus] != 0) { self.lastCommandWasError = YES; return NO; } // success self.lastCommandWasError = NO; return YES; } - (void)performRefreshNow { self.content = @"Updating ..."; self.errorContent = @""; [self rebuildMenuForStatusItem:self.statusItem]; self.currentLine = -1; [self cycleLines]; [self.manager pluginDidUdpdateItself:self]; [self refresh]; } -(BOOL)refresh { __weak ExecutablePlugin *weakSelf = self; [self.lineCycleTimer invalidate]; self.lineCycleTimer = nil; [self.refreshTimer invalidate]; self.refreshTimer = nil; // execute command dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ [weakSelf refreshContentByExecutingCommand]; dispatch_sync(dispatch_get_main_queue(), ^{ if (weakSelf) { __strong ExecutablePlugin* strongSelf = weakSelf; strongSelf.lastUpdated = NSDate.new; [strongSelf rebuildMenuForStatusItem:strongSelf.statusItem]; // reset the current line strongSelf.currentLine = -1; // update the status item [strongSelf cycleLines]; // sort out multi-line cycler if (strongSelf.isMultiline) { // start the timer to keep cycling lines strongSelf.lineCycleTimer = [NSTimer scheduledTimerWithTimeInterval:strongSelf.cycleLinesIntervalSeconds target:strongSelf selector:@selector(cycleLines) userInfo:nil repeats:YES]; } // tell the manager this plugin has updated [strongSelf.manager pluginDidUdpdateItself:strongSelf]; // strongSelf next refresh strongSelf.refreshTimer = [NSTimer scheduledTimerWithTimeInterval:[strongSelf.refreshIntervalSeconds doubleValue] target:strongSelf selector:@selector(refresh) userInfo:nil repeats:NO]; } }); }); return YES; } - (void) close { [self.lineCycleTimer invalidate]; self.lineCycleTimer = nil; [self.refreshTimer invalidate]; self.refreshTimer = nil; } - (void) copyOutput { NSString *valueToCopy = [self.allContentLines objectAtIndex:self.currentLine]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; [pasteboard clearContents]; [pasteboard writeObjects:[NSArray arrayWithObject:valueToCopy]]; } - (void) copyAllOutput { NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; [pasteboard clearContents]; [pasteboard writeObjects:[NSArray arrayWithObject:self.allContent]]; } - (void) runPluginExternally { NSString* script = @"tell application \"Terminal\" \n\ do script \"%@\" \n\ activate \n\ end tell"; NSString *s = [NSString stringWithFormat: script, [self.path stringByReplacingOccurrencesOfString:@" " withString:@"\\\\ "]]; NSAppleScript *as = [NSAppleScript.alloc initWithSource:s]; [as executeAndReturnError:nil]; } - (void) addAdditionalMenuItems:(NSMenu *)menu { if (!DEFS.userConfigDisabled) { NSMenuItem *runItem = [NSMenuItem.alloc initWithTitle:@"Run in Terminal…" action:@selector(runPluginExternally) keyEquivalent:@"o"]; [runItem setTarget:self]; [menu addItem:runItem]; } } @end ================================================ FILE: archive/bitbar/App/BitBar/HTMLPlugin.h ================================================ // // HTMLPlugin.h // BitBar // // Created by Mathias Leppich on 22/01/14. // Copyright (c) 2014 Bit Bar. All rights reserved. // #import "Plugin.h" @class WebView; @interface HTMLPlugin : Plugin @property (nonatomic, strong) NSTimer *autoReloadTimer; @property (nonatomic, strong) WebView *webView; @property (nonatomic, readonly) NSString * reloadInterval; @property (nonatomic, strong) NSMenu * menu; // callable from JavaScript - (void) resizeToFit; - (void) resetMenu; - (void) addMenuItem:(NSObject*)titleOrParamsDict; - (void) addMenuItems:(NSObject*)titleOrParamsDict; - (void) addMenuSeperatorItem; - (void) showMenu; - (void) showWebInspector; @end ================================================ FILE: archive/bitbar/App/BitBar/HTMLPlugin.m ================================================ // // HTMLPlugin.m // BitBar // // Created by Mathias Leppich on 22/01/14. // Copyright (c) 2014 Bit Bar. All rights reserved. // #import "HTMLPlugin.h" #import "PluginManager.h" #import @interface WebInspector : NSObject { WebView *_webView; } - (id)initWithWebView:(WebView *)webView; - (void)detach: (id)sender; - (void)show: (id)sender; - (void)showConsole:(id)sender; @end @implementation HTMLPlugin -(BOOL)refresh { if (![[NSFileManager defaultManager] fileExistsAtPath:self.path]) { return NO; } NSLog(@" HTML File: %@", self.path); self.content = @"HTML File"; self.currentLine = -1; [self cycleLines]; [self.manager pluginDidUdpdateItself:self]; [self reinitAutoReloadTimer]; return YES; } -(void)reinitAutoReloadTimer { [self.autoReloadTimer invalidate]; self.autoReloadTimer = [NSTimer scheduledTimerWithTimeInterval:[self.refreshIntervalSeconds doubleValue] target:self selector:@selector(reloadWebView) userInfo:nil repeats:YES]; } -(void)reloadWebView { NSLog(@"soft reload"); [self.webView reload:nil]; } -(void)rebuildMenuForStatusItem:(NSStatusItem *)statusItem { WebView * webview = [WebView.alloc initWithFrame:NSMakeRect(0, 0, 15, 15)]; self.webView = webview; webview.frameLoadDelegate = (id)self; webview.resourceLoadDelegate = (id)self; webview.UIDelegate = (id)self; webview.drawsBackground = NO; webview.mainFrame.frameView.allowsScrolling = NO; webview.shouldUpdateWhileOffscreen = YES; webview.autoresizingMask = NSViewWidthSizable; NSURL * url = [NSURL fileURLWithPath:self.path]; NSURLRequest * req = [NSURLRequest.alloc initWithURL:url]; [webview.mainFrame loadRequest:req]; statusItem.view = webview; [self resetMenu]; } - (void)resizeWebViewToFitContents { WebView * webView = (WebView *)self.statusItem.view; WebFrame * webFrame = [webView mainFrame]; //get the rect for the rendered frame NSRect webFrameRect = [[[webFrame frameView] documentView] frame]; //get the rect of the current webview NSRect webViewRect = [webView frame]; //calculate the new frame NSRect newWebViewRect = NSMakeRect(webViewRect.origin.x, webViewRect.origin.y, webFrameRect.size.width, webViewRect.size.height); //set the frame [webView setFrame:newWebViewRect]; NSLog(@"The dimensions of the page are: %@",NSStringFromRect(webFrameRect)); } # pragma mark Delegate methods - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)webFrame { [self resizeWebViewToFitContents]; } - (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)windowObject forFrame:(WebFrame *)frame { // WebInspector * inspector = [WebInspector.alloc initWithWebView:sender]; // [inspector detach:sender]; // [inspector showConsole:sender]; NSLog(@"didClearWindowObject: windowObject: %@", windowObject); WebScriptObject * script = [sender windowScriptObject]; NSLog(@"didClearWindowObject: script: %@", script); [script setValue:self forKey:@"BitBar"]; [script evaluateWebScript:@"Object.isArray = function(a){ return a instanceof Array; }"]; } - (void)webView:(WebView *)webView addMessageToConsole:(NSDictionary *)dictionary { NSLog(@"Error from webkit: %@", dictionary); } -(NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems { // disable right-click menu in webview return nil; } -(BOOL)webView:(WebView *)sender shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag { // disable text selection return NO; } + (BOOL)isSelectorExcludedFromWebScript:(SEL)selector { SEL allowed[] = { @selector(log:), @selector(setReloadInterval:), @selector(resizeToFit), @selector(showWebInspector), @selector(resetMenu), @selector(addMenuItem:), @selector(addMenuItems:), @selector(addMenuSeperatorItem), @selector(showMenu) }; for (int i=0; i<(sizeof allowed)/(sizeof allowed[0]); i++) { if (allowed[i] == selector) { NSLog(@"allow Selector: %@", NSStringFromSelector(selector)); return NO; } } //NSLog(@"isSelectorExcludedFromWebScript: %@", NSStringFromSelector(selector)); return YES; } +(NSString *)webScriptNameForSelector:(SEL)selector { return [NSStringFromSelector(selector) stringByReplacingOccurrencesOfString:@":" withString:@""]; } +(BOOL)isKeyExcludedFromWebScript:(const char *)name { NSArray * allowed = @[ @"reloadInterval" ]; if ([allowed containsObject:[NSString stringWithUTF8String:name]]) { NSLog(@"isKeyExcludedFromWebScript: %@", [NSString stringWithUTF8String:name]); return NO; } return YES; } #pragma mark - WebScriptObject Utils - (NSArray*) arrayOfKeysFromWebScriptObject:(WebScriptObject *)obj { WebScriptObject* bridge = [obj evaluateWebScript:@"Object"]; WebScriptObject* keysObj = [bridge callWebScriptMethod:@"keys" withArguments:@[obj]]; return [self arrayFromWebScriptObject:keysObj]; } - (NSDictionary*) dictionaryFromWebScriptObject:(WebScriptObject *)obj { NSArray * keys = [self arrayOfKeysFromWebScriptObject:obj]; NSMutableDictionary * dict = [NSMutableDictionary.alloc initWithCapacity:keys.count]; for (NSString * key in keys) { NSObject * value = [obj valueForKey:key]; if ([[value class] isSubclassOfClass:[NSString class]] || [[value class] isSubclassOfClass:[NSNumber class]]) { [dict setObject:value forKey:key]; } } return dict; } - (BOOL) isWebScriptObjectInstanceOfArray:(WebScriptObject *)obj { WebScriptObject* bridge = [obj evaluateWebScript:@"Object"]; NSNumber * result = [bridge callWebScriptMethod:@"isArray" withArguments:@[obj]]; return [result boolValue]; } - (NSArray*) arrayFromWebScriptObject:(WebScriptObject *)obj { NSMutableArray * values = NSMutableArray.new; id elem = nil; int i = 0; WebUndefined *undefined = [WebUndefined undefined]; while ((elem = [obj webScriptValueAtIndex:i++]) != undefined) { [values addObject:elem]; } return values; } #pragma mark - Called from JavaScript -(void)log:(NSString*) str { NSLog(@"JAVASCRIPT LOG: %@", str); } -(NSNumber *)reloadInterval { NSLog(@"reloadInterval:"); return self.refreshIntervalSeconds; } -(void)setReloadInterval:(NSNumber* )arg{ if (![arg isKindOfClass:[NSNumber class]]) { return; } NSLog(@"setReloadInterval: %@", arg); self.refreshIntervalSeconds = arg; [self reinitAutoReloadTimer]; } - (void) resizeToFit { [self resizeWebViewToFitContents]; } - (void) resetMenu { NSLog(@"resetMenu"); _menu = NSMenu.new; } - (void) addMenuItem:(NSObject*)titleOrParamsDict { [self addMenuItems:titleOrParamsDict]; } - (void) addMenuItems:(NSObject*)titleOrParamsDict { NSDictionary * params = nil; if ([[titleOrParamsDict class] isSubclassOfClass:[NSString class]]) { NSString * title = (NSString *)titleOrParamsDict; if ([title hasPrefix:@"---"]) { [self addMenuSeperatorItem]; return; } params = @{@"title": title}; } else if ([[titleOrParamsDict class] isSubclassOfClass:[NSArray class]]) { NSArray * values = (NSArray *)titleOrParamsDict; for (NSObject * value in values) { [self addMenuItem:value]; } return; } else if ([[titleOrParamsDict class] isSubclassOfClass:[NSDictionary class]]) { params = (NSDictionary *)titleOrParamsDict; } else if ([[titleOrParamsDict class] isSubclassOfClass:[WebScriptObject class]]) { WebScriptObject * obj = (WebScriptObject*) titleOrParamsDict; if ([self isWebScriptObjectInstanceOfArray:obj]) { [self addMenuItems:[self arrayFromWebScriptObject:obj]]; return; } else { params = [self dictionaryFromWebScriptObject:obj]; } } else { NSLog(@"addMenuItem: ERROR: unhandled class: %@", [titleOrParamsDict class]); } if (params != nil) { NSLog(@"addMenuItem: %@", params); NSMenuItem * item = [self buildMenuItemWithParams:params]; [_menu addItem:item]; } } - (void) addMenuSeperatorItem { NSLog(@"addMenuSeperatorItem"); [_menu addItem:[NSMenuItem separatorItem]]; } - (void) showMenu { NSLog(@"showMenu"); _menu.delegate = self; [self addDefaultMenuItems:_menu]; self.statusItem.menu = _menu; [self.statusItem popUpStatusItemMenu:self.statusItem.menu]; } - (void) showWebInspector { WebView * webview = (WebView*) self.statusItem.view; WebInspector * inspector = [WebInspector.alloc initWithWebView:webview]; // [inspector detach:sender]; [inspector showConsole:webview]; } @end ================================================ FILE: archive/bitbar/App/BitBar/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "16x16", "idiom" : "mac", "filename" : "bitbar-16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", "filename" : "bitbar-32.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "bitbar-33.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "bitbar-64.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "bitbar-128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "bitbar-256.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "bitbar-257.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "bitbar-512.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "bitbar-513.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "bitbar-1024.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/BitBar/NSColor+Hex.h ================================================ // // NSColor+Hex.h // BitBar // // Created by Mathias Leppich on 03/02/14. // Copyright (c) 2014 Bit Bar. All rights reserved. // #import @interface NSColor (Hex) + (NSColor*) colorWithWebColorString:(NSString*)color; + (NSColor*) colorWithHexColorString:(NSString*)hex; @end ================================================ FILE: archive/bitbar/App/BitBar/NSColor+Hex.m ================================================ // // NSColor+Hex.m // BitBar // // Created by Mathias Leppich on 03/02/14. // Copyright (c) 2014 Bit Bar. All rights reserved. // #import "NSColor+Hex.h" @implementation NSColor (Hex) + (NSDictionary *)cssColors { static NSDictionary *cssDictionary = nil; return cssDictionary = cssDictionary ?: @{@"lightseagreen":@"20b2aa", @"floralwhite":@"fffaf0", @"lightgray":@"d3d3d3", @"darkgoldenrod":@"b8860b", @"paleturquoise":@"afeeee", @"goldenrod":@"daa520", @"skyblue":@"87ceeb", @"indianred":@"cd5c5c", @"darkgray":@"a9a9a9", @"khaki":@"f0e68c", @"blue":@"0000ff", @"darkred":@"8b0000", @"lightyellow":@"ffffe0", @"midnightblue":@"191970", @"chartreuse":@"7fff00", @"lightsteelblue":@"b0c4de", @"slateblue":@"6a5acd", @"firebrick":@"b22222", @"moccasin":@"ffe4b5", @"salmon":@"fa8072", @"sienna":@"a0522d", @"slategray":@"708090", @"teal":@"008080", @"lightsalmon":@"ffa07a", @"pink":@"ffc0cb", @"burlywood":@"deb887", @"gold":@"ffd700", @"springgreen":@"00ff7f", @"lightcoral":@"f08080", @"black":@"000000", @"blueviolet":@"8a2be2", @"chocolate":@"d2691e", @"aqua":@"00ffff", @"darkviolet":@"9400d3", @"indigo":@"4b0082", @"darkcyan":@"008b8b", @"orange":@"ffa500", @"antiquewhite":@"faebd7", @"peru":@"cd853f", @"silver":@"c0c0c0", @"purple":@"800080", @"saddlebrown":@"8b4513", @"lawngreen":@"7cfc00", @"dodgerblue":@"1e90ff", @"lime":@"00ff00", @"linen":@"faf0e6", @"lightblue":@"add8e6", @"darkslategray":@"2f4f4f", @"lightskyblue":@"87cefa", @"mintcream":@"f5fffa", @"olive":@"808000", @"hotpink":@"ff69b4", @"papayawhip":@"ffefd5", @"mediumseagreen":@"3cb371", @"mediumspringgreen":@"00fa9a", @"cornflowerblue":@"6495ed", @"plum":@"dda0dd", @"seagreen":@"2e8b57", @"palevioletred":@"db7093", @"bisque":@"ffe4c4", @"beige":@"f5f5dc", @"darkorchid":@"9932cc", @"royalblue":@"4169e1", @"darkolivegreen":@"556b2f", @"darkmagenta":@"8b008b", @"orange red":@"ff4500", @"lavender":@"e6e6fa", @"fuchsia":@"ff00ff", @"darkseagreen":@"8fbc8f", @"lavenderblush":@"fff0f5", @"wheat":@"f5deb3", @"steelblue":@"4682b4", @"lightgoldenrodyellow":@"fafad2", @"lightcyan":@"e0ffff", @"mediumaquamarine":@"66cdaa", @"turquoise":@"40e0d0", @"dark blue":@"00008b", @"darkorange":@"ff8c00", @"brown":@"a52a2a", @"dimgray":@"696969", @"deeppink":@"ff1493", @"powderblue":@"b0e0e6", @"red":@"ff0000", @"darkgreen":@"006400", @"ghostwhite":@"f8f8ff", @"white":@"ffffff", @"navajowhite":@"ffdead", @"navy":@"000080", @"ivory":@"fffff0", @"palegreen":@"98fb98", @"whitesmoke":@"f5f5f5", @"gainsboro":@"dcdcdc", @"mediumslateblue":@"7b68ee", @"olivedrab":@"6b8e23", @"mediumpurple":@"9370db", @"darkslateblue":@"483d8b", @"blanchedalmond":@"ffebcd", @"darkkhaki":@"bdb76b", @"green":@"008000", @"limegreen":@"32cd32", @"snow":@"fffafa", @"tomato":@"ff6347", @"darkturquoise":@"00ced1", @"orchid":@"da70d6", @"yellow":@"ffff00", @"green yellow":@"adff2f", @"azure":@"f0ffff", @"mistyrose":@"ffe4e1", @"cadetblue":@"5f9ea0", @"oldlace":@"fdf5e6", @"gray":@"808080", @"honeydew":@"f0fff0", @"peachpuff":@"ffdab9", @"tan":@"d2b48c", @"thistle":@"d8bfd8", @"palegoldenrod":@"eee8aa", @"mediumorchid":@"ba55d3", @"rosybrown":@"bc8f8f", @"mediumturquoise":@"48d1cc", @"lemonchiffon":@"fffacd", @"maroon":@"800000", @"mediumvioletred":@"c71585", @"violet":@"ee82ee", @"yellow green":@"9acd32", @"coral":@"ff7f50", @"lightgreen":@"90ee90", @"cornsilk":@"fff8dc", @"mediumblue":@"0000cd", @"aliceblue":@"f0f8ff", @"forestgreen":@"228b22", @"aquamarine":@"7fffd4", @"deepskyblue":@"00bfff", @"lightslategray":@"778899", @"darksalmon":@"e9967a", @"crimson":@"dc143c", @"sandybrown":@"f4a460", @"lightpink":@"ffb6c1", @"seashell":@"fff5ee"}; } + (NSColor*)colorWithWebColorString:(NSString*)colorString { NSString * hexString = [colorString hasPrefix:@"#"] ? [colorString substringWithRange:NSMakeRange(1,colorString.length - 1)] : self.cssColors[colorString.lowercaseString]; return [self colorWithHexColorString:hexString]; } + (NSColor*)colorWithHexColorString:(NSString*)inColorString { NSColor* result = nil; unsigned colorCode = 0; unsigned char redByte, greenByte, blueByte; if (nil != inColorString) { NSScanner* scanner = [NSScanner scannerWithString:inColorString]; (void) [scanner scanHexInt:&colorCode]; // ignore error } redByte = (unsigned char)(colorCode >> 16); greenByte = (unsigned char)(colorCode >> 8); blueByte = (unsigned char)(colorCode); // masks off high bits result = [NSColor colorWithCalibratedRed:(CGFloat)redByte / 0xff green:(CGFloat)greenByte / 0xff blue:(CGFloat)blueByte / 0xff alpha:1.0]; return result; } @end ================================================ FILE: archive/bitbar/App/BitBar/NSString+ANSI.h ================================================ // // NSString+ANSI.h // BitBar // // Created by Kent Karlsson on 3/11/16. // Copyright © 2016 Bit Bar. All rights reserved. // #import @interface NSString (ANSI) - (BOOL)containsANSICodes; - (NSMutableAttributedString*)attributedStringParsingANSICodes; @end ================================================ FILE: archive/bitbar/App/BitBar/NSString+ANSI.m ================================================ // // NSString+ANSI.m // BitBar // // Created by Kent Karlsson on 3/11/16. // Copyright © 2016 Bit Bar. All rights reserved. // #import "Cocoa/Cocoa.h" #import "NSString+ANSI.h" #import "NSColor+Hex.h" @implementation NSMutableDictionary (ANSI) - (NSMutableDictionary*)modifyAttributesForANSICodes:(NSString*)codes { BOOL bold = NO; NSFont* font = self[NSFontAttributeName]; NSArray* codeArray = [codes componentsSeparatedByString:@";"]; for (NSString* codeString in codeArray) { int code = codeString.intValue; switch (code) { case 0: [self removeAllObjects]; // remove italic and bold from font here if (font) self[NSFontAttributeName] = font; break; case 1: case 22: bold = (code == 1); break; // case 3: italic // case 23: italic off // case 4: underlined // case 24: underlined off case 30: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"7f7f7f" : @"000000"]; break; case 31: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"cd0000" : @"ff0000"]; break; case 32: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"00cd00" : @"00ff00"]; break; case 33: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"cdcd00" : @"ffff00"]; break; case 34: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"0000ee" : @"5c5cff"]; break; case 35: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"cd00cd" : @"ff00ff"]; break; case 36: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"00cdcd" : @"00ffff"]; break; case 37: self[NSForegroundColorAttributeName] = [NSColor colorWithHexColorString:bold ? @"e5e5e5" : @"ffffff"]; break; case 39: [self removeObjectForKey:NSForegroundColorAttributeName]; break; case 40: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"7f7f7f"]; break; case 41: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"cd0000"]; break; case 42: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"00cd00"]; break; case 43: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"cdcd00"]; break; case 44: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"0000ee"]; break; case 45: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"cd00cd"]; break; case 46: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"00cdcd"]; break; case 47: self[NSBackgroundColorAttributeName] = [NSColor colorWithHexColorString:@"e5e5e5"]; break; case 49: [self removeObjectForKey:NSBackgroundColorAttributeName]; break; default: break; } } return self; } @end @implementation NSString (ANSI) - (BOOL)containsANSICodes { return [self rangeOfString:@"\033["].location != NSNotFound; } - (NSMutableAttributedString*)attributedStringParsingANSICodes { NSMutableAttributedString* result = [[NSMutableAttributedString alloc] init]; NSMutableDictionary* attributes = [NSMutableDictionary.alloc init]; NSArray* parts = [self componentsSeparatedByString:@"\033["]; [result appendAttributedString:[NSAttributedString.alloc initWithString:parts.firstObject attributes:nil]]; for (NSString* part in [parts subarrayWithRange:NSMakeRange(1, parts.count - 1)]) { if (part.length == 0) continue; NSArray* sequence = [part componentsSeparatedByString:@"m"]; NSString* text = sequence.lastObject; if (sequence.count < 2) { [result appendAttributedString:[NSAttributedString.alloc initWithString:text attributes:attributes]]; } else if (sequence.count >= 2) { text = [[sequence subarrayWithRange:NSMakeRange(1, sequence.count - 1)] componentsJoinedByString:@"m"]; [attributes modifyAttributesForANSICodes:sequence[0]]; [result appendAttributedString:[NSAttributedString.alloc initWithString:text attributes:attributes]]; } } return result; } @end ================================================ FILE: archive/bitbar/App/BitBar/NSUserDefaults+Settings.h ================================================ // // Settings.h // BitBar // // Created by Mat Ryer on 11/13/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // @import Foundation; @interface NSUserDefaults (Settings) @property NSString* pluginsDirectory; @property BOOL isFirstTimeAppRun; @property BOOL userConfigDisabled; @end #define DEFS NSUserDefaults.standardUserDefaults ================================================ FILE: archive/bitbar/App/BitBar/NSUserDefaults+Settings.m ================================================ // // Settings.m // BitBar // // Created by Mat Ryer on 11/13/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // #import "NSUserDefaults+Settings.h" @implementation NSUserDefaults (Settings) - (NSString *)pluginsDirectory { #ifdef DISTRO return [self stringForKey:@"pluginsDirectory"] ?: [NSBundle mainBundle].executablePath.stringByDeletingLastPathComponent; #else return [self stringForKey:@"pluginsDirectory"]; #endif } - (void) setPluginsDirectory:(NSString*)value { [self setObject:value forKey:@"pluginsDirectory"]; } - (BOOL) isFirstTimeAppRun { return ![self boolForKey:@"appHasRun"]; } - (void) setIsFirstTimeAppRun:(BOOL)firstTime { [self setBool:!firstTime forKey:@"appHasRun"]; } - (BOOL)userConfigDisabled { #ifdef DISTRO id disabled = [self objectForKey:@"userConfigDisabled"]; return disabled ? [disabled boolValue] : YES; #else return [self boolForKey:@"userConfigDisabled"]; #endif } - (void)setUserConfigDisabled:(BOOL)disabled { [self setBool:disabled forKey:@"userConfigDisabled"]; } @end ================================================ FILE: archive/bitbar/App/BitBar/Plugin.h ================================================ // // Plugin.h // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // @import AppKit; @class PluginManager; @interface Plugin : NSObject @property (nonatomic) NSInteger currentLine, cycleLinesIntervalSeconds; @property (nonatomic) BOOL lastCommandWasError, pluginIsVisible, menuIsOpen; @property (readonly) BOOL isMultiline; @property (readonly) NSString *lastUpdatedString; @property (nonatomic, copy) NSString *path, *name, *content, *allContent, *errorContent; @property (nonatomic) NSArray *allContentLines; @property (nonatomic) NSArray *titleLines; @property (nonatomic) NSNumber *refreshIntervalSeconds; @property (nonatomic) NSMenuItem *lastUpdatedMenuItem; @property (nonatomic) NSDate *lastUpdated; @property (weak, readonly) PluginManager *manager; // UI @property (nonatomic) NSStatusItem *statusItem; - initWithManager:(PluginManager*)manager; - (void) close; - (NSMenuItem*) buildMenuItemForLine:(NSString *)line; - (NSMenuItem*) buildMenuItemWithParams:(NSDictionary *)params; - (NSDictionary *)dictionaryForLine:(NSString *)line; - (void) rebuildMenuForStatusItem:(NSStatusItem*)statusItem; - (void) addAdditionalMenuItems:(NSMenu *)menu; - (void) addDefaultMenuItems:(NSMenu *)menu; - (void)performRefreshNow; - (BOOL) refresh; - (void) cycleLines; - (void) contentHasChanged; - (BOOL) isFontValid:(NSString *)fontName; // actions - (void)changePluginsDirectorySelected:(id)sender; @end ================================================ FILE: archive/bitbar/App/BitBar/Plugin.m ================================================ // // Plugin.m // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // #import "Plugin.h" #import "PluginManager.h" #import "STPrivilegedTask.h" #import "NSDate+DateTools.h" #import "NSColor+Hex.h" #import "NSString+Emojize.h" #import "NSString+ANSI.h" #define DEFAULT_TIME_INTERVAL_SECONDS ((double)60.) @implementation Plugin - init { return (self = super.init) ? _currentLine = -1, _cycleLinesIntervalSeconds = 5, self : nil; } - initWithManager:(PluginManager*)manager { return (self = self.init) ? _manager = manager, self : nil; } - (NSStatusItem *)statusItem { return _statusItem = _statusItem ?: ({ // make the status item _statusItem = [self.manager.statusBar statusItemWithLength:NSVariableStatusItemLength]; // build the menu [self rebuildMenuForStatusItem:_statusItem]; _statusItem; }); } - (NSImage*) createImageFromBase64:(NSString*)string isTemplate:(BOOL)template{ NSData * imageData; if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) { imageData = [[NSData alloc] initWithBase64EncodedString:string options:0]; }else { imageData = [[NSData alloc] initWithBase64Encoding:string]; } NSImage * image = [[NSImage alloc] initWithData:imageData]; if (template) { [image setTemplate:true]; } return image; } - (NSMenuItem*) buildMenuItemWithParams:(NSDictionary *)params { if ([[params[@"dropdown"] lowercaseString] isEqualToString:@"false"]) { return nil; } NSString * fullTitle = params[@"title"]; if (![[params[@"emojize"] lowercaseString] isEqualToString:@"false"]) { fullTitle = [fullTitle emojizedString]; } if (![[params[@"trim"] lowercaseString] isEqualToString:@"false"]) { fullTitle = [fullTitle stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; } CGFloat titleLength = [fullTitle length]; CGFloat lengthParam = params[@"length"] ? [params[@"length"] floatValue] : titleLength; CGFloat truncLength = lengthParam >= titleLength ? titleLength : lengthParam; NSString * title = truncLength < titleLength ? [[fullTitle substringToIndex:truncLength] stringByAppendingString:@"…"] : fullTitle; SEL sel = params[@"href"] ? @selector(performMenuItemHREFAction:) : params[@"bash"] ? @selector(performMenuItemOpenTerminalAction:) : params[@"refresh"] ? @selector(performRefreshNow): nil; NSMenuItem * item = [NSMenuItem.alloc initWithTitle:title action:sel keyEquivalent:@""]; if (truncLength < titleLength) [item setToolTip:fullTitle]; item.representedObject = params; if (sel) { [item setTarget:self]; } BOOL parseANSI = [fullTitle containsANSICodes] && ![[params[@"ansi"] lowercaseString] isEqualToString:@"false"]; if (params[@"font"] || params[@"size"] || params[@"color"] || parseANSI) item.attributedTitle = [self attributedTitleWithParams:params]; if (params[@"alternate"]) { item.alternate = YES; item.keyEquivalentModifierMask = NSAlternateKeyMask; } if (params[@"templateImage"]) { item.image = [self createImageFromBase64:params[@"templateImage"] isTemplate:true]; }else if (params[@"image"]) { item.image = [self createImageFromBase64:params[@"image"] isTemplate:false]; } return item; } - (NSAttributedString*) attributedTitleWithParams:(NSDictionary *)params { NSString * fullTitle = params[@"title"]; if (![[params[@"emojize"] lowercaseString] isEqualToString:@"false"]) { fullTitle = [fullTitle emojizedString]; } if (![[params[@"trim"] lowercaseString] isEqualToString:@"false"]) { fullTitle = [fullTitle stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; } CGFloat titleLength = [fullTitle length]; CGFloat lengthParam = params[@"length"] ? [params[@"length"] floatValue] : titleLength; CGFloat truncLength = lengthParam >= titleLength ? titleLength : lengthParam; NSString * title = truncLength < titleLength ? [[fullTitle substringToIndex:truncLength] stringByAppendingString:@"…"] : fullTitle; CGFloat size = params[@"size"] ? [params[@"size"] floatValue] : 14; NSFont * font; if ([NSFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) { font = [self isFontValid:params[@"font"]] ? [NSFont fontWithName:params[@"font"] size:size] : [NSFont monospacedDigitSystemFontOfSize:size weight:NSFontWeightRegular] ?: [NSFont monospacedDigitSystemFontOfSize:size weight:NSFontWeightRegular]; } else { font = [self isFontValid:params[@"font"]] ? [NSFont fontWithName:params[@"font"] size:size] : [NSFont menuFontOfSize:size] ?: [NSFont menuFontOfSize:size]; } NSNumber * offset; if (@available(macOS 11.0.1, *)) { offset = @-1; } else { offset = @0; } NSDictionary* attributes = @{NSFontAttributeName: font, NSBaselineOffsetAttributeName : offset}; BOOL parseANSI = [fullTitle containsANSICodes] && ![[params[@"ansi"] lowercaseString] isEqualToString:@"false"]; if (parseANSI) { NSMutableAttributedString * attributedTitle = [title attributedStringParsingANSICodes]; [attributedTitle addAttributes:attributes range:NSMakeRange(0, attributedTitle.length)]; return attributedTitle; } else { NSColor * fgColor; NSMutableAttributedString * attributedTitle = [NSMutableAttributedString.alloc initWithString:title attributes:attributes]; if (!params[@"color"]) return attributedTitle; if ((fgColor = [NSColor colorWithWebColorString:[params objectForKey:@"color"]])) [attributedTitle addAttribute:NSForegroundColorAttributeName value:fgColor range:NSMakeRange(0, title.length)]; return attributedTitle; } } - (NSMenuItem*) buildMenuItemForLine:(NSString *)line { return [self buildMenuItemWithParams:[self dictionaryForLine:line]]; } - (NSDictionary*) dictionaryForLine:(NSString *)line { // Find the title NSRange found = [line rangeOfString:@"|"]; if (found.location == NSNotFound) return @{ @"title": line }; NSString * title = [line substringToIndex:found.location]; NSMutableDictionary * params = @{@"title":title}.mutableCopy; // Find the parameters NSString * paramStr = [line substringFromIndex:found.location + found.length]; NSScanner* scanner = [NSScanner scannerWithString:paramStr]; NSMutableCharacterSet* keyValueSeparator = [NSMutableCharacterSet characterSetWithCharactersInString:@"=:"]; NSMutableCharacterSet* quoteSeparator = [NSMutableCharacterSet characterSetWithCharactersInString:@"\"'"]; while (![scanner isAtEnd]) { NSString *key = @""; NSString* value = @""; [scanner scanUpToCharactersFromSet:keyValueSeparator intoString:&key]; [scanner scanCharactersFromSet:keyValueSeparator intoString:NULL]; if ([scanner scanCharactersFromSet:quoteSeparator intoString:NULL]) { [scanner scanUpToCharactersFromSet:quoteSeparator intoString:&value]; [scanner scanCharactersFromSet:quoteSeparator intoString:NULL]; } else { [scanner scanUpToString:@" " intoString:&value]; } // Remove extraneous spaces from key and value key = [key stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; params[key] = value; if([key isEqualToString:@"args"]){ params[key] = [value componentsSeparatedByString:@"__"]; } } return params; } - (void)performRefreshNow { NSLog(@"Nothing to refresh in this plugin"); } - (void)performHREFAction:(NSDictionary *)params { [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:params[@"href"]]]; } - (void) performMenuItemHREFAction:(NSMenuItem *)menuItem { [self performHREFAction:menuItem.representedObject]; } - (void) startTask:(NSMutableDictionary*)params { id task = [params[@"root"] isEqualToString:@"true"] ? STPrivilegedTask.new : NSTask.new; [(NSTask*)task setLaunchPath:params[@"bash"]]; [(NSTask*)task setArguments:params[@"args"]]; ((NSTask*)task).terminationHandler = ^(NSTask *task) { if (params[@"refresh"]) { [self performSelectorOnMainThread:@selector(performRefreshNow) withObject:NULL waitUntilDone:false]; } }; @try { [(NSTask*)task launch]; } @catch (NSException *e) { NSLog(@"Error launching command for %@:\n\tCMD: %@\n\tARGS: %@\n%@", self.name, params[@"bash"], params[@"args"], e); } [(NSTask*)task waitUntilExit]; } - (void)performOpenTerminalAction:(NSMutableDictionary *)params { NSString *bash = [params[@"bash"] stringByStandardizingPath].stringByResolvingSymlinksInPath, *param1 = params[@"param1"] ?: @"", *param2 = params[@"param2"] ?: @"", *param3 = params[@"param3"] ?: @"", *param4 = params[@"param4"] ?: @"", *param5 = params[@"param5"] ?: @"", *terminal = params[@"terminal"] ?: [NSString stringWithFormat:@"%s", "true"]; NSArray *args = params[@"args"] ?: ({ NSMutableArray *argArray = @[].mutableCopy; for (int i = 1; i < 6; i ++) { id x = params[[NSString stringWithFormat:@"param%i", i]]; if (x) [argArray addObject:x]; } argArray.copy; }); if([terminal isEqual: @"false"]){ NSLog(@"Args: %@", args); [params setObject:bash forKey:@"bash"]; [params setObject:args forKey:@"args"]; [self performSelectorInBackground:@selector(startTask:) withObject:params]; } else { NSString *full_link = [NSString stringWithFormat:@"'%@' %@ %@ %@ %@ %@", bash, param1, param2, param3, param4, param5]; NSString *s = [NSString stringWithFormat:@"tell application \"Terminal\" \n\ do script \"%@\" \n\ activate \n\ end tell", full_link]; NSAppleScript *as = [NSAppleScript.alloc initWithSource: s]; [as executeAndReturnError:nil]; } } - (void)performMenuItemOpenTerminalAction:(NSMenuItem *)menuItem { [self performOpenTerminalAction:menuItem.representedObject]; } - (void) rebuildMenuForStatusItem:(NSStatusItem*)statusItem { // build the menu NSMenu *menu = NSMenu.new; [menu setDelegate:self]; if (self.isMultiline) { // put all content as an item NSString *line; if ([self.titleLines count] > 1) { for (line in self.titleLines) { NSMenuItem * item = [self buildMenuItemForLine:line]; if (item) { [menu addItem:item]; } } // add the seperator [menu addItem:[NSMenuItem separatorItem]]; } // are there any allContentLines? if (self.allContentLines.count > 0) { // put all content as an item NSString *line; for (line in self.allContentLines) { if ([line isEqualToString:@"---"]) { [menu addItem:[NSMenuItem separatorItem]]; } else { NSMenu *submenu = menu; // traverse submenus up to the menu to add the item to while ([line hasPrefix:@"--"]) { line = [line substringFromIndex:2]; NSMenuItem *lastItem = submenu.itemArray.lastObject; if (!lastItem.submenu) { lastItem.submenu = [[NSMenu alloc] init]; lastItem.submenu.delegate = self; } submenu = lastItem.submenu; if ([line isEqualToString:@"---"]) { break; } } if ([line isEqualToString:@"---"]) { [submenu addItem:[NSMenuItem separatorItem]]; } else { NSMenuItem * item = [self buildMenuItemForLine:line]; if(item) [submenu addItem:item]; } } } // add the seperator [menu addItem:[NSMenuItem separatorItem]]; } } if (self.lastUpdated != nil) { self.lastUpdatedMenuItem = [NSMenuItem.alloc initWithTitle:@"Updated just now" action:nil keyEquivalent:@""]; [menu addItem:self.lastUpdatedMenuItem]; } [self addAdditionalMenuItems:menu]; [self addDefaultMenuItems:menu]; // set the menu statusItem.menu = menu; } - (void) addDefaultMenuItems:(NSMenu *)menu { [self.manager addHelperItemsToMenu:menu asSubMenu:(menu.itemArray.count>0)]; } - (void) addAdditionalMenuItems:(NSMenu *)menu { } - (void) changePluginsDirectorySelected:_ { _manager.path = nil; [_manager reset]; } - (NSNumber*) refreshIntervalSeconds { if (_refreshIntervalSeconds == nil) { NSArray *segments = [self.name componentsSeparatedByString:@"."]; if (segments.count < 3) return _refreshIntervalSeconds = @(DEFAULT_TIME_INTERVAL_SECONDS); NSString *timeStr = [segments[1] lowercaseString]; if ([timeStr length] < 2) { _refreshIntervalSeconds = @(DEFAULT_TIME_INTERVAL_SECONDS); return _refreshIntervalSeconds; } NSString *numberPart = [timeStr substringToIndex:[timeStr length]-1]; double numericalValue = numberPart.doubleValue ?: DEFAULT_TIME_INTERVAL_SECONDS; if ([timeStr hasSuffix:@"s"]) { // this is ok - but nothing to do } else if ([timeStr hasSuffix:@"m"]) numericalValue *= 60; else if ([timeStr hasSuffix:@"h"]) numericalValue *= 60*60; else if ([timeStr hasSuffix:@"d"]) numericalValue *= 60*60*24; else return _refreshIntervalSeconds = @(DEFAULT_TIME_INTERVAL_SECONDS); _refreshIntervalSeconds = @(numericalValue); } return _refreshIntervalSeconds; } - (BOOL) refresh { return YES; } - (void) close { } - (NSString*) lastUpdatedString { return [self.lastUpdated timeAgoSinceNow].lowercaseString; } - (void) cycleLines { // do nothing if the menu is open if (self.menuIsOpen) { return; }; // update the status item self.currentLine++; // if we've gone too far - wrap around if ((NSUInteger)self.currentLine >= self.titleLines.count) { self.currentLine = 0; } if (self.titleLines.count > 0) { NSDictionary * params = [self dictionaryForLine:self.titleLines[self.currentLine]]; // skip alternate line if (params[@"alternate"]) { [self cycleLines]; return; } if (params[@"href"] || params[@"bash"] || params[@"refresh"]) { self.statusItem.menu = nil; self.statusItem.action = @selector(statusItemClicked); self.statusItem.target = self; } else if (!self.statusItem.menu) { self.statusItem.action = NULL; self.statusItem.target = nil; [self rebuildMenuForStatusItem:self.statusItem]; } // Add image if present if (params[@"templateImage"]) { self.statusItem.image = [self createImageFromBase64:params[@"templateImage"] isTemplate:true]; }else if (params[@"image"]) { self.statusItem.image = [self createImageFromBase64:params[@"image"] isTemplate:false]; } else { self.statusItem.image = nil; } self.statusItem.attributedTitle = [self attributedTitleWithParams:params]; self.pluginIsVisible = YES; } else { self.statusItem = nil; self.pluginIsVisible = NO; } } - (void) contentHasChanged { _allContent = nil; _titleLines = nil; _allContentLines = nil; } - (BOOL) isFontValid:(NSString *)fontName { if (fontName == nil) { return NO; } NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:@{NSFontNameAttribute:fontName}]; NSArray *matches = [fontDescriptor matchingFontDescriptorsWithMandatoryKeys: nil]; return ([matches count] > 0); } - (void) setContent:(NSString *)content { _content = content; [self contentHasChanged]; } - (void) setErrorContent:(NSString *)errorContent { _errorContent = errorContent; [self contentHasChanged]; } - (NSString *)allContent { if (!_allContent) { _allContent = self.content; if (self.errorContent.length > 0) { _allContent = [@"⚠️" stringByAppendingString:_allContent]; _allContent = [_allContent stringByAppendingString:@"\n---\n"]; _allContent = [_allContent stringByAppendingString:self.errorContent]; } } return _allContent; } - (NSArray *)titleLines { return _titleLines = _titleLines ?: ({ NSMutableArray *cleanLines = @[].mutableCopy; for (NSString *lineEval in [self.allContent componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet]) { // strip whitespace NSString *line = [self cleanLine:lineEval]; // add the line if we have something in it if (line.length) { if ([line isEqualToString:@"---"]) break; [cleanLines addObject:line]; } } cleanLines.copy; }); } - (NSString*) cleanLine:(NSString*)line { NSArray *splitLine = [line componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; if ([[splitLine componentsJoinedByString:@""] isEqualToString:@""]) return @""; return line; //[splitLine componentsJoinedByString:@" "]; //return [regex stringByReplacingMatchesInString:line options:0 range:NSMakeRange(0, line.length) withTemplate:@" "]; } - (NSArray*) allContentLines { if (_allContentLines == nil) { NSArray *lines = [self.allContent componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSMutableArray *cleanLines = [NSMutableArray.alloc initWithCapacity:lines.count]; NSString *line; BOOL firstBreakFound = NO; for (line in lines) { // strip whitespace line = [self cleanLine:line]; // add the line if we have something in it if (line.length > 0) { if ([line isEqualToString:@"---"]) { if (firstBreakFound) { [cleanLines addObject:line]; } firstBreakFound = YES; } else { if (firstBreakFound) { [cleanLines addObject:line]; } } } } _allContentLines = [NSArray arrayWithArray:cleanLines]; } return _allContentLines; } - (BOOL) isMultiline { return [self.titleLines count] > 1 || [self.allContentLines count]>0; } - (void)statusItemClicked { NSDictionary *params = [self dictionaryForLine:self.titleLines[self.currentLine]]; if (params[@"href"]) { [self performHREFAction:params]; } else if (params[@"bash"]) { [self performOpenTerminalAction:[NSMutableDictionary dictionaryWithDictionary:params]]; } else if (params[@"refresh"]) { [self performRefreshNow]; } } #pragma mark - NSMenuDelegate - (void)menuWillOpen:(NSMenu *)menu { if (menu.supermenu) { return; } self.menuIsOpen = YES; if (self.currentLine >= 0 && self.currentLine < self.titleLines.count) { NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryForLine:self.titleLines[self.currentLine]]]; if (params[@"color"]) { [params removeObjectForKey:@"color"]; self.statusItem.attributedTitle = [self attributedTitleWithParams:params]; } } [self.statusItem setHighlightMode:YES]; [self.lastUpdatedMenuItem setTitle:self.lastUpdated ? [NSString stringWithFormat:@"Updated %@", self.lastUpdatedString] : @"Refreshing…"]; } - (void)menuDidClose:(NSMenu *)menu { if (menu.supermenu) { return; } self.menuIsOpen = NO; [self.statusItem setHighlightMode:NO]; if (self.currentLine >= 0 && self.currentLine < self.titleLines.count) { NSDictionary *params = [self dictionaryForLine:self.titleLines[self.currentLine]]; if (params[@"color"]) { self.statusItem.attributedTitle = [self attributedTitleWithParams:params]; } } } - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item { // restore about to be unhighlighted item if (menu.highlightedItem.representedObject && menu.highlightedItem.attributedTitle) { NSDictionary *params = menu.highlightedItem.representedObject; if (params[@"color"]) { menu.highlightedItem.attributedTitle = [self attributedTitleWithParams:params]; } } // remove about to be highlighted item color if (item.representedObject && item.attributedTitle) { NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:item.representedObject]; if (params[@"color"]) { [params removeObjectForKey:@"color"]; item.attributedTitle = [self attributedTitleWithParams:params]; } } } @end ================================================ FILE: archive/bitbar/App/BitBar/PluginManager.h ================================================ // // PluginManager.h // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // @import AppKit; @class Plugin; @interface PluginManager : NSObject @property (nonatomic, copy) NSString *path; @property (nonatomic) NSArray *plugins; @property (nonatomic) NSStatusBar *statusBar; @property (nonatomic) NSStatusItem *defaultStatusItem; @property (nonatomic, readonly) NSDictionary *environment; - initWithPluginPath:(NSString *)path; - (NSArray*) pluginFilesWithAsking:(BOOL)shouldAsk; - (void) reset; - (void) setupAllPlugins; - (void) clearPathAndReset; - (void) showSystemStatusItemWithMessage:(NSString*)message; - (void) addHelperItemsToMenu:(NSMenu*)menu asSubMenu:(BOOL)submenu; - (void) pluginDidUdpdateItself:(Plugin*)plugin; - (void) updateEnvironment; @end ================================================ FILE: archive/bitbar/App/BitBar/PluginManager.m ================================================ // // PluginManager.m // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // #import "PluginManager.h" #import "Plugin.h" #import "ExecutablePlugin.h" #import "HTMLPlugin.h" #import "NSUserDefaults+Settings.h" #import "LaunchAtLoginController.h" #import NSString *const AppleInterfaceThemeChangedNotification = @"AppleInterfaceThemeChangedNotification"; @interface PluginManager () { LaunchAtLoginController *_launchAtLoginController; } @property (nonatomic, readwrite) NSDictionary *environment; @end @implementation PluginManager - initWithPluginPath:(NSString*)path { if (self = [super init]) { _path = [path stringByStandardizingPath]; _launchAtLoginController = [[LaunchAtLoginController alloc] init]; [self updateEnvironment]; [self setupObserver]; } return self; } - (void)dealloc { [NSDistributedNotificationCenter.defaultCenter removeObserver:self name:AppleInterfaceThemeChangedNotification object:nil]; } - (void)setupObserver { [NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(updateEnvironment) name:AppleInterfaceThemeChangedNotification object:nil]; } - (void) showSystemStatusItemWithMessage:(NSString*)message { [self.statusBar removeStatusItem:self.defaultStatusItem]; // make default menu item self.defaultStatusItem = [self.statusBar statusItemWithLength:NSVariableStatusItemLength]; [self.defaultStatusItem setTitle:NSProcessInfo.processInfo.processName]; [(self.defaultStatusItem.menu = NSMenu.new) setDelegate:self]; if (message.length) { NSMenuItem *messageMenuItem = [NSMenuItem.alloc initWithTitle:message action:nil keyEquivalent:@""]; [self.defaultStatusItem.menu addItem:messageMenuItem]; [self.defaultStatusItem.menu addItem:NSMenuItem.separatorItem]; } [self addHelperItemsToMenu:self.defaultStatusItem.menu asSubMenu:NO]; } #define ADD_MENU(TITLE,SELECTOR,SHORTCUT,TARGET) ({ \ NSMenuItem *item = [NSMenuItem.alloc initWithTitle:TITLE action:NSStringFromSelector(@selector(SELECTOR)) ? @selector(SELECTOR) : NULL keyEquivalent:SHORTCUT?:@""];\ if (TARGET) [item setTarget:TARGET]; \ [targetMenu addItem:item]; item; }) - (void) addHelperItemsToMenu:(NSMenu*)menu asSubMenu:(BOOL)submenu { NSMenu *targetMenu; NSMenuItem *moreItem = nil; if (submenu) { NSMenu *moreMenu = [NSMenu.alloc initWithTitle:@"Preferences"]; moreItem = [NSMenuItem.alloc initWithTitle:@"Preferences" action:nil keyEquivalent:@""]; moreItem.submenu = moreMenu; [menu addItem:moreItem]; targetMenu = moreMenu; } else targetMenu = menu; // add reset, aka refreshMenuItem ADD_MENU(@"Refresh all", reset, @"r", self); [targetMenu addItem:NSMenuItem.separatorItem]; NSString *versionString = nil; versionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; if (!versionString) { versionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; } NSMenuItem *versionMenuitem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"v%@", versionString] action:nil keyEquivalent:@""]; if (!DEFS.userConfigDisabled) { versionMenuitem.alternate = YES; versionMenuitem.keyEquivalentModifierMask = NSAlternateKeyMask; // add edit action, aka prefsMenuItem ADD_MENU(@"Change Plugin Folder…", changePluginDirectory,@"",self); // add edit action, aka openPluginFolderMenuItem ADD_MENU(@"Open Plugin Folder…",openPluginFolder, nil, self); // add browser item, aka openPluginBrowserMenuItem ADD_MENU(@"Get Plugins…", openPluginsBrowser, nil, self); [targetMenu addItem:NSMenuItem.separatorItem]; // open at login, aka openAtLoginMenuItem [ADD_MENU(@"Open at Login", toggleOpenAtLogin:, nil, self) setState:_launchAtLoginController.launchAtLogin]; [targetMenu addItem:NSMenuItem.separatorItem]; //ADD_MENU(@"Check for Updates…", checkForUpdates:, nil, [SUUpdater sharedUpdater]); } [targetMenu addItem:versionMenuitem]; // // // add troubleshooting item // ADD_MENU(@"User Guide…", openTroubleshootingPage,@"g",self); // // // add troubleshooting item // ADD_MENU(@"Report an Issue…",openHomepage,@"i",self); // // quit menu ADD_MENU(@"Quit",quit, @"q",self); } - (void) quit { [NSApp terminate:[NSApplication sharedApplication]]; } #define WSPACE NSWorkspace.sharedWorkspace - (void) openReportIssuesPage { [WSPACE openURL:[NSURL URLWithString:@"https://github.com/matryer/bitbar/issues"]]; } - (void) openPluginsBrowser { [WSPACE openURL:[NSURL URLWithString:@"https://getbitbar.com/"]]; } - (void) openHomepage { [WSPACE openURL:[NSURL URLWithString:@"https://github.com/matryer/bitbar"]]; } - (void) openPluginFolder { [WSPACE openURL:[NSURL fileURLWithPath:self.path]]; } - (void) toggleOpenAtLogin:(id)sender { [_launchAtLoginController setLaunchAtLogin:!_launchAtLoginController.launchAtLogin]; } - (NSArray*) pluginFilesWithAsking:(BOOL)shouldAsk { BOOL dirIsOK = !!self.path; if (dirIsOK) { BOOL isDir; if (![NSFileManager.defaultManager fileExistsAtPath:self.path isDirectory:&isDir]) dirIsOK = NO; if (!isDir) dirIsOK = NO; } if (dirIsOK) { // get the listing NSError *error; NSArray *dirFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:self.path error:&error]; // handle error if there is one if (error != nil) { dirIsOK = NO; } else { // filter dot files dirFiles = [dirFiles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT self BEGINSWITH '.'"]]; // filter markdown files dirFiles = [dirFiles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT self ENDSWITH '.md'"]]; // filter application executable // filter subdirectories dirFiles = [dirFiles filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id name, NSDictionary *bindings) { BOOL isDir; NSString * path = [self.path stringByAppendingPathComponent:name]; return ![path isEqualToString:[NSBundle mainBundle].executablePath] && [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] && !isDir; }]]; return dirFiles; } } return (!dirIsOK && shouldAsk) && self.beginSelectingPluginsDir ? [self pluginFilesWithAsking:NO] : nil; } - (BOOL) beginSelectingPluginsDir { NSOpenPanel* openDlg = [NSOpenPanel openPanel]; [openDlg setCanChooseDirectories:YES]; [openDlg setCanChooseFiles:NO]; [openDlg setCanCreateDirectories:YES]; [openDlg setPrompt:@"Use as Plugins Directory"]; [openDlg setTitle:@"Select BitBar Plugins Directory"]; if (openDlg.runModal == NSOKButton) { if (!openDlg.URL.isFileURL) { // TODO: error popup return NO; } BOOL isDir = NO; if ([[NSFileManager defaultManager] fileExistsAtPath: openDlg.URL.path isDirectory: &isDir] && isDir) { // symlink bundled plugins in selected directory self.path = [NSBundle mainBundle].executablePath.stringByDeletingLastPathComponent; NSArray *pluginFiles = [self pluginFilesWithAsking:NO]; for (NSString *file in pluginFiles) [[NSFileManager defaultManager] createSymbolicLinkAtPath:[openDlg.URL.path stringByAppendingPathComponent:file] withDestinationPath:[self.path stringByAppendingPathComponent:file] error:nil]; self.path = [openDlg.URL path]; [DEFS setPluginsDirectory:self.path]; return YES; } // TODO: error popup return NO; } else self.path = DEFS.pluginsDirectory; return NO; } - (void) reset { // remove all status items for (Plugin *plugin in _plugins) { [self.statusBar removeStatusItem:plugin.statusItem]; [plugin close]; } _plugins = nil; [self.statusBar removeStatusItem:self.defaultStatusItem]; [self setupAllPlugins]; } - (void) clearPathAndReset { self.path = nil; [self reset]; } - (void)changePluginDirectory { if ([self beginSelectingPluginsDir] == YES) { [self reset]; } } - (NSArray *)plugins { return _plugins = _plugins ?: ({ NSArray *pluginFiles = [self pluginFilesWithAsking:YES]; NSMutableArray *plugins = [NSMutableArray.alloc initWithCapacity:[pluginFiles count]]; NSString *file; for (file in pluginFiles) { // setup this plugin Plugin *plugin; if ([@[@"html",@"htm"] containsObject:file.pathExtension.lowercaseString]) { plugin = [HTMLPlugin.alloc initWithManager:self]; } else { plugin = [ExecutablePlugin.alloc initWithManager:self]; } [plugin setPath:[self.path stringByAppendingPathComponent:file]]; [plugin setName:file]; [plugin.statusItem setTitle:@"…"]; [plugins addObject:plugin]; } plugins.copy; }); } - (void)updateEnvironment { NSMutableDictionary *env = NSProcessInfo.processInfo.environment.mutableCopy; env[@"BitBar"] = [@YES stringValue]; // Determine if Mac is in Dark Mode NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; if ([osxMode isEqualToString:@"Dark"]) { env[@"BitBarDarkMode"] = [@YES stringValue]; } self.environment = env; } - (void) pluginDidUdpdateItself:(Plugin*)plugin { [self checkForNoPlugins]; } - (void)checkForNoPlugins { Plugin *plugin; NSInteger visiblePlugins = 0; for (plugin in self.plugins) { if (plugin.pluginIsVisible) visiblePlugins++; } visiblePlugins != 0 ? [self.statusBar removeStatusItem:self.defaultStatusItem] : [self showSystemStatusItemWithMessage:@"No plugins found"]; } - (NSStatusBar *)statusBar { return _statusBar = _statusBar ?: NSStatusBar.systemStatusBar; } - (void) setupAllPlugins { NSArray *plugins = self.plugins; if (!plugins.count) [self checkForNoPlugins]; else { for (Plugin *plugin in plugins) [plugin refresh]; } } - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { if ([menuItem action] == @selector(toggleOpenAtLogin:)) { [menuItem setState:_launchAtLoginController.launchAtLogin ? NSOnState : NSOffState]; } return YES; } #pragma mark - NSMenuDelegate - (void)menuWillOpen:(NSMenu *)menu { [self.defaultStatusItem setHighlightMode:YES]; } - (void)menuDidClose:(NSMenu *)menu { [self.defaultStatusItem setHighlightMode:NO]; } @end ================================================ FILE: archive/bitbar/App/BitBar/incoming-url-tests.html ================================================ Incoming URL tests

BitBar - Incoming URL tests

Link Expectation
Normal Should open and install 'Cycle Text and Detail' plugin
Missing title Should open and install 'Cycle Text and Detail' plugin but use cycle_text_and_detail.sh as title
Empty title Should open and install 'Cycle Text and Detail' plugin but use cycle_text_and_detail.sh as title
Unofficial repo Should open and install test plugin but have additional warnings - and use full URL (not title)
================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 0531DCD91844070A007F0A96 /* App.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0531DCD81844070A007F0A96 /* App.xib */; }; 05322A49183406E2004D9AFE /* NSUserDefaults+Settings.m in Sources */ = {isa = PBXBuildFile; fileRef = 05322A48183406E2004D9AFE /* NSUserDefaults+Settings.m */; }; 055819781834172E00B44B00 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 055819771834172E00B44B00 /* LaunchAtLoginController.m */; }; 059971961833394500EA6D8D /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 059971951833394500EA6D8D /* Plugin.m */; }; 0599719818333DB300EA6D8D /* PluginTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0599719718333DB300EA6D8D /* PluginTest.m */; }; 05DAE007183323DD00409786 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE006183323DD00409786 /* AppDelegate.m */; }; 05DAE02D1833274100409786 /* PluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE02C1833274100409786 /* PluginManager.m */; }; 05DAE02F1833276C00409786 /* PluginManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE02E1833276C00409786 /* PluginManagerTest.m */; }; 05DB995C1C3D4B80008B5159 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 05DB995B1C3D4B80008B5159 /* Images.xcassets */; }; 36372DCC1C9424DB0005EB32 /* PluginTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0599719718333DB300EA6D8D /* PluginTest.m */; }; 36372DCE1C9424DB0005EB32 /* PluginManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE02E1833276C00409786 /* PluginManagerTest.m */; }; 36372DCF1C9424DB0005EB32 /* PluginManager+Test.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303DF1C93848400AF5499 /* PluginManager+Test.m */; }; 36372DD21C9424DB0005EB32 /* TestPlugins in Resources */ = {isa = PBXBuildFile; fileRef = AE7303DC1C93721600AF5499 /* TestPlugins */; }; 36D0CE0A1D0AF012001AFB77 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36D0CDFF1D0AF009001AFB77 /* Sparkle.framework */; }; 36D0CE0B1D0AF012001AFB77 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 36D0CDFF1D0AF009001AFB77 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36D0CE101D0AF018001AFB77 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36D0CDFF1D0AF009001AFB77 /* Sparkle.framework */; }; 36D0CE111D0AF019001AFB77 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 36D0CDFF1D0AF009001AFB77 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36DCD8BE1C76027C004DE286 /* AHProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB21C3D95E600A64968 /* AHProxy.m */; }; 36DCD8BF1C76027C004DE286 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE006183323DD00409786 /* AppDelegate.m */; }; 36DCD8C01C76027C004DE286 /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21581B9F0F10002539F7 /* DTConstants.m */; }; 36DCD8C11C76027C004DE286 /* HTMLPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB71890670800C380A4 /* HTMLPlugin.m */; }; 36DCD8C21C76027C004DE286 /* STPrivilegedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21501B9F0BE6002539F7 /* STPrivilegedTask.m */; }; 36DCD8C31C76027C004DE286 /* ExecutablePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB4189066DE00C380A4 /* ExecutablePlugin.m */; }; 36DCD8C41C76027C004DE286 /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21621B9F0F10002539F7 /* DTTimePeriodGroup.m */; }; 36DCD8C51C76027C004DE286 /* NSTask+useSystemProxies.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB61C3D95E600A64968 /* NSTask+useSystemProxies.m */; }; 36DCD8C61C76027C004DE286 /* PluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE02C1833274100409786 /* PluginManager.m */; }; 36DCD8C71C76027C004DE286 /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215A1B9F0F10002539F7 /* DTError.m */; }; 36DCD8C81C76027C004DE286 /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21601B9F0F10002539F7 /* DTTimePeriodCollection.m */; }; 36DCD8C91C76027C004DE286 /* NSUserDefaults+Settings.m in Sources */ = {isa = PBXBuildFile; fileRef = 05322A48183406E2004D9AFE /* NSUserDefaults+Settings.m */; }; 36DCD8CA1C76027C004DE286 /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21641B9F0F10002539F7 /* NSDate+DateTools.m */; }; 36DCD8CB1C76027C004DE286 /* NSColor+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = A22AF328189F823E0011DFCD /* NSColor+Hex.m */; }; 36DCD8CC1C76027C004DE286 /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 059971951833394500EA6D8D /* Plugin.m */; }; 36DCD8CD1C76027C004DE286 /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215E1B9F0F10002539F7 /* DTTimePeriodChain.m */; }; 36DCD8CE1C76027C004DE286 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 055819771834172E00B44B00 /* LaunchAtLoginController.m */; }; 36DCD8CF1C76027C004DE286 /* AHProxySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB41C3D95E600A64968 /* AHProxySettings.m */; }; 36DCD8D01C76027C004DE286 /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215C1B9F0F10002539F7 /* DTTimePeriod.m */; }; 36DCD8D21C76027C004DE286 /* App.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0531DCD81844070A007F0A96 /* App.xib */; }; 36DCD8D31C76027C004DE286 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 05DB995B1C3D4B80008B5159 /* Images.xcassets */; }; 36DCD8D41C76027C004DE286 /* DateTools.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 7B9A21551B9F0F10002539F7 /* DateTools.bundle */; }; 7B9A21531B9F0BE6002539F7 /* STPrivilegedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21501B9F0BE6002539F7 /* STPrivilegedTask.m */; }; 7B9A21651B9F0F10002539F7 /* DateTools.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 7B9A21551B9F0F10002539F7 /* DateTools.bundle */; }; 7B9A21661B9F0F10002539F7 /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21581B9F0F10002539F7 /* DTConstants.m */; }; 7B9A21671B9F0F10002539F7 /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215A1B9F0F10002539F7 /* DTError.m */; }; 7B9A21681B9F0F10002539F7 /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215C1B9F0F10002539F7 /* DTTimePeriod.m */; }; 7B9A21691B9F0F10002539F7 /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215E1B9F0F10002539F7 /* DTTimePeriodChain.m */; }; 7B9A216A1B9F0F10002539F7 /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21601B9F0F10002539F7 /* DTTimePeriodCollection.m */; }; 7B9A216B1B9F0F10002539F7 /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21621B9F0F10002539F7 /* DTTimePeriodGroup.m */; }; 7B9A216C1B9F0F10002539F7 /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21641B9F0F10002539F7 /* NSDate+DateTools.m */; }; A22AF329189F823E0011DFCD /* NSColor+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = A22AF328189F823E0011DFCD /* NSColor+Hex.m */; }; A281BDB5189066DE00C380A4 /* ExecutablePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB4189066DE00C380A4 /* ExecutablePlugin.m */; }; A281BDB81890670800C380A4 /* HTMLPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB71890670800C380A4 /* HTMLPlugin.m */; }; AE3C167A1C93F71B009AF7CA /* NSString+Emojize.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303D81C935BDE00AF5499 /* NSString+Emojize.m */; }; AE4B1E391C9B2B0F009540AD /* NSString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303EC1C93984C00AF5499 /* NSString+ANSI.m */; }; AE4B1E4D1C9C631B009540AD /* PluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE02C1833274100409786 /* PluginManager.m */; }; AE4B1E4E1C9C631B009540AD /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 059971951833394500EA6D8D /* Plugin.m */; }; AE4B1E4F1C9C631B009540AD /* ExecutablePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB4189066DE00C380A4 /* ExecutablePlugin.m */; }; AE4B1E501C9C632A009540AD /* PluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 05DAE02C1833274100409786 /* PluginManager.m */; }; AE4B1E511C9C632A009540AD /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 059971951833394500EA6D8D /* Plugin.m */; }; AE4B1E521C9C632A009540AD /* ExecutablePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB4189066DE00C380A4 /* ExecutablePlugin.m */; }; AE4B1E531C9C6351009540AD /* HTMLPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB71890670800C380A4 /* HTMLPlugin.m */; }; AE4B1E541C9C6351009540AD /* NSUserDefaults+Settings.m in Sources */ = {isa = PBXBuildFile; fileRef = 05322A48183406E2004D9AFE /* NSUserDefaults+Settings.m */; }; AE4B1E551C9C6351009540AD /* NSString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303EC1C93984C00AF5499 /* NSString+ANSI.m */; }; AE4B1E561C9C6351009540AD /* NSString+Emojize.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303D81C935BDE00AF5499 /* NSString+Emojize.m */; }; AE4B1E571C9C6351009540AD /* AHProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB21C3D95E600A64968 /* AHProxy.m */; }; AE4B1E581C9C6351009540AD /* AHProxySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB41C3D95E600A64968 /* AHProxySettings.m */; }; AE4B1E591C9C6351009540AD /* NSTask+useSystemProxies.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB61C3D95E600A64968 /* NSTask+useSystemProxies.m */; }; AE4B1E5A1C9C6351009540AD /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21581B9F0F10002539F7 /* DTConstants.m */; }; AE4B1E5B1C9C6351009540AD /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215A1B9F0F10002539F7 /* DTError.m */; }; AE4B1E5C1C9C6351009540AD /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215C1B9F0F10002539F7 /* DTTimePeriod.m */; }; AE4B1E5D1C9C6351009540AD /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215E1B9F0F10002539F7 /* DTTimePeriodChain.m */; }; AE4B1E5E1C9C6351009540AD /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21601B9F0F10002539F7 /* DTTimePeriodCollection.m */; }; AE4B1E5F1C9C6351009540AD /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21621B9F0F10002539F7 /* DTTimePeriodGroup.m */; }; AE4B1E601C9C6351009540AD /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21641B9F0F10002539F7 /* NSDate+DateTools.m */; }; AE4B1E611C9C6351009540AD /* STPrivilegedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21501B9F0BE6002539F7 /* STPrivilegedTask.m */; }; AE4B1E621C9C6351009540AD /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 055819771834172E00B44B00 /* LaunchAtLoginController.m */; }; AE4B1E631C9C6351009540AD /* NSColor+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = A22AF328189F823E0011DFCD /* NSColor+Hex.m */; }; AE4B1E641C9C636A009540AD /* HTMLPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A281BDB71890670800C380A4 /* HTMLPlugin.m */; }; AE4B1E651C9C636A009540AD /* NSUserDefaults+Settings.m in Sources */ = {isa = PBXBuildFile; fileRef = 05322A48183406E2004D9AFE /* NSUserDefaults+Settings.m */; }; AE4B1E661C9C636A009540AD /* NSString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303EC1C93984C00AF5499 /* NSString+ANSI.m */; }; AE4B1E671C9C636A009540AD /* NSString+Emojize.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303D81C935BDE00AF5499 /* NSString+Emojize.m */; }; AE4B1E681C9C636A009540AD /* AHProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB21C3D95E600A64968 /* AHProxy.m */; }; AE4B1E691C9C636A009540AD /* AHProxySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB41C3D95E600A64968 /* AHProxySettings.m */; }; AE4B1E6A1C9C636A009540AD /* NSTask+useSystemProxies.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB61C3D95E600A64968 /* NSTask+useSystemProxies.m */; }; AE4B1E6B1C9C636A009540AD /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21581B9F0F10002539F7 /* DTConstants.m */; }; AE4B1E6C1C9C636A009540AD /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215A1B9F0F10002539F7 /* DTError.m */; }; AE4B1E6D1C9C636A009540AD /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215C1B9F0F10002539F7 /* DTTimePeriod.m */; }; AE4B1E6E1C9C636A009540AD /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A215E1B9F0F10002539F7 /* DTTimePeriodChain.m */; }; AE4B1E6F1C9C636A009540AD /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21601B9F0F10002539F7 /* DTTimePeriodCollection.m */; }; AE4B1E701C9C636A009540AD /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21621B9F0F10002539F7 /* DTTimePeriodGroup.m */; }; AE4B1E711C9C636A009540AD /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21641B9F0F10002539F7 /* NSDate+DateTools.m */; }; AE4B1E721C9C636A009540AD /* STPrivilegedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9A21501B9F0BE6002539F7 /* STPrivilegedTask.m */; }; AE4B1E731C9C636A009540AD /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 055819771834172E00B44B00 /* LaunchAtLoginController.m */; }; AE4B1E741C9C636A009540AD /* NSColor+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = A22AF328189F823E0011DFCD /* NSColor+Hex.m */; }; AE7303DB1C935BDE00AF5499 /* NSString+Emojize.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303D81C935BDE00AF5499 /* NSString+Emojize.m */; }; AE7303DD1C93721600AF5499 /* TestPlugins in Resources */ = {isa = PBXBuildFile; fileRef = AE7303DC1C93721600AF5499 /* TestPlugins */; }; AE7303E01C93848400AF5499 /* PluginManager+Test.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303DF1C93848400AF5499 /* PluginManager+Test.m */; }; AE7303ED1C93984C00AF5499 /* NSString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = AE7303EC1C93984C00AF5499 /* NSString+ANSI.m */; }; F8D03BB71C3D95E600A64968 /* AHProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB21C3D95E600A64968 /* AHProxy.m */; }; F8D03BB81C3D95E600A64968 /* AHProxySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB41C3D95E600A64968 /* AHProxySettings.m */; }; F8D03BB91C3D95E600A64968 /* NSTask+useSystemProxies.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D03BB61C3D95E600A64968 /* NSTask+useSystemProxies.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 05DAE015183323DD00409786 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 05DADFE8183323DD00409786 /* Project object */; proxyType = 1; remoteGlobalIDString = 05DADFEF183323DD00409786; remoteInfo = BitBar; }; 36372DC01C9424DB0005EB32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 05DADFE8183323DD00409786 /* Project object */; proxyType = 1; remoteGlobalIDString = 36DCD8BC1C76027C004DE286; remoteInfo = BitBarDistro; }; 36D0CDFE1D0AF009001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 8DC2EF5B0486A6940098B216; remoteInfo = Sparkle; }; 36D0CE001D0AF009001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 61B5F90209C4CEE200B25A18; remoteInfo = "Sparkle Test App"; }; 36D0CE021D0AF009001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 612279D90DB5470200AB99EA; remoteInfo = "Sparkle Unit Tests"; }; 36D0CE041D0AF009001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 5D06E8D00FD68C7C005AE3F6; remoteInfo = BinaryDelta; }; 36D0CE061D0AF009001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 55C14BB7136EEF1500649790; remoteInfo = Autoupdate; }; 36D0CE081D0AF009001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 726B2B5D1C645FC900388755; remoteInfo = "UI Tests"; }; 36D0CE0C1D0AF012001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 36D0CE121D0AF019001AFB77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 3ACB54592561958E00407243 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; proxyType = 2; remoteGlobalIDString = 722954B41D04ADAF00ECF9CA; remoteInfo = fileop; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 36D0CE0F1D0AF013001AFB77 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 36D0CE0B1D0AF012001AFB77 /* Sparkle.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; 36D0CE151D0AF019001AFB77 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 36D0CE111D0AF019001AFB77 /* Sparkle.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 0531DCD81844070A007F0A96 /* App.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = App.xib; sourceTree = ""; }; 05322A47183406E2004D9AFE /* NSUserDefaults+Settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+Settings.h"; sourceTree = ""; }; 05322A48183406E2004D9AFE /* NSUserDefaults+Settings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+Settings.m"; sourceTree = ""; }; 055819761834172E00B44B00 /* LaunchAtLoginController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = ""; }; 055819771834172E00B44B00 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; 05590ACE1C3E74C100352E77 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 059971941833394500EA6D8D /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Plugin.h; sourceTree = ""; }; 059971951833394500EA6D8D /* Plugin.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = Plugin.m; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 0599719718333DB300EA6D8D /* PluginTest.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PluginTest.m; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 05DADFF0183323DD00409786 /* BitBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BitBar.app; sourceTree = BUILT_PRODUCTS_DIR; }; 05DADFFB183323DD00409786 /* BitBar-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "BitBar-Info.plist"; sourceTree = ""; }; 05DAE006183323DD00409786 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = BitBar/AppDelegate.m; sourceTree = ""; }; 05DAE011183323DD00409786 /* BitBarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BitBarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 05DAE019183323DD00409786 /* BitBarTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "BitBarTests-Info.plist"; sourceTree = ""; }; 05DAE02B1833274100409786 /* PluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginManager.h; sourceTree = ""; }; 05DAE02C1833274100409786 /* PluginManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PluginManager.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 05DAE02E1833276C00409786 /* PluginManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PluginManagerTest.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 05DB995B1C3D4B80008B5159 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 36372DD61C9424DB0005EB32 /* BitBarDistroTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BitBarDistroTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sparkle.xcodeproj; path = Vendor/Sparkle/Sparkle.xcodeproj; sourceTree = SOURCE_ROOT; }; 36DCD8D81C76027C004DE286 /* BitBarDistro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BitBarDistro.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7B5D08F91BA8EF6300400886 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../README.md; sourceTree = ""; }; 7B9A214F1B9F0BE6002539F7 /* STPrivilegedTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPrivilegedTask.h; path = Vendor/STPrivilegedTask/STPrivilegedTask.h; sourceTree = SOURCE_ROOT; }; 7B9A21501B9F0BE6002539F7 /* STPrivilegedTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STPrivilegedTask.m; path = Vendor/STPrivilegedTask/STPrivilegedTask.m; sourceTree = SOURCE_ROOT; }; 7B9A21551B9F0F10002539F7 /* DateTools.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = DateTools.bundle; sourceTree = ""; }; 7B9A21561B9F0F10002539F7 /* DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateTools.h; sourceTree = ""; }; 7B9A21571B9F0F10002539F7 /* DTConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTConstants.h; sourceTree = ""; }; 7B9A21581B9F0F10002539F7 /* DTConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTConstants.m; sourceTree = ""; }; 7B9A21591B9F0F10002539F7 /* DTError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTError.h; sourceTree = ""; }; 7B9A215A1B9F0F10002539F7 /* DTError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTError.m; sourceTree = ""; }; 7B9A215B1B9F0F10002539F7 /* DTTimePeriod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriod.h; sourceTree = ""; }; 7B9A215C1B9F0F10002539F7 /* DTTimePeriod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriod.m; sourceTree = ""; }; 7B9A215D1B9F0F10002539F7 /* DTTimePeriodChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriodChain.h; sourceTree = ""; }; 7B9A215E1B9F0F10002539F7 /* DTTimePeriodChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodChain.m; sourceTree = ""; }; 7B9A215F1B9F0F10002539F7 /* DTTimePeriodCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriodCollection.h; sourceTree = ""; }; 7B9A21601B9F0F10002539F7 /* DTTimePeriodCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodCollection.m; sourceTree = ""; }; 7B9A21611B9F0F10002539F7 /* DTTimePeriodGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriodGroup.h; sourceTree = ""; }; 7B9A21621B9F0F10002539F7 /* DTTimePeriodGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodGroup.m; sourceTree = ""; }; 7B9A21631B9F0F10002539F7 /* NSDate+DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+DateTools.h"; sourceTree = ""; }; 7B9A21641B9F0F10002539F7 /* NSDate+DateTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+DateTools.m"; sourceTree = ""; }; A22AF327189F823E0011DFCD /* NSColor+Hex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSColor+Hex.h"; sourceTree = ""; }; A22AF328189F823E0011DFCD /* NSColor+Hex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSColor+Hex.m"; sourceTree = ""; wrapsLines = 1; }; A281BDB3189066DE00C380A4 /* ExecutablePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutablePlugin.h; sourceTree = ""; }; A281BDB4189066DE00C380A4 /* ExecutablePlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ExecutablePlugin.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A281BDB61890670800C380A4 /* HTMLPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLPlugin.h; sourceTree = ""; }; A281BDB71890670800C380A4 /* HTMLPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = HTMLPlugin.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; AE7303D61C935BDE00AF5499 /* emojis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emojis.h; sourceTree = ""; }; AE7303D71C935BDE00AF5499 /* NSString+Emojize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Emojize.h"; sourceTree = ""; }; AE7303D81C935BDE00AF5499 /* NSString+Emojize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Emojize.m"; sourceTree = ""; }; AE7303DC1C93721600AF5499 /* TestPlugins */ = {isa = PBXFileReference; lastKnownFileType = folder; path = TestPlugins; sourceTree = ""; }; AE7303DE1C93848400AF5499 /* PluginManager+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PluginManager+Test.h"; sourceTree = ""; }; AE7303DF1C93848400AF5499 /* PluginManager+Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PluginManager+Test.m"; sourceTree = ""; }; AE7303EB1C93984C00AF5499 /* NSString+ANSI.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = "NSString+ANSI.h"; sourceTree = ""; tabWidth = 2; }; AE7303EC1C93984C00AF5499 /* NSString+ANSI.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = "NSString+ANSI.m"; sourceTree = ""; tabWidth = 2; }; F8D03BB11C3D95E600A64968 /* AHProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AHProxy.h; sourceTree = ""; }; F8D03BB21C3D95E600A64968 /* AHProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AHProxy.m; sourceTree = ""; }; F8D03BB31C3D95E600A64968 /* AHProxySettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AHProxySettings.h; sourceTree = ""; }; F8D03BB41C3D95E600A64968 /* AHProxySettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AHProxySettings.m; sourceTree = ""; }; F8D03BB51C3D95E600A64968 /* NSTask+useSystemProxies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTask+useSystemProxies.h"; sourceTree = ""; }; F8D03BB61C3D95E600A64968 /* NSTask+useSystemProxies.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTask+useSystemProxies.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 05DAE00E183323DD00409786 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 36372DD01C9424DB0005EB32 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 36D0CE0E1D0AF013001AFB77 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 36D0CE0A1D0AF012001AFB77 /* Sparkle.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 36D0CE141D0AF019001AFB77 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 36D0CE101D0AF018001AFB77 /* Sparkle.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 05322A46183406D7004D9AFE /* Settings */ = { isa = PBXGroup; children = ( 05322A47183406E2004D9AFE /* NSUserDefaults+Settings.h */, 05322A48183406E2004D9AFE /* NSUserDefaults+Settings.m */, ); name = Settings; path = BitBar; sourceTree = ""; }; 055819751834171900B44B00 /* LaunchAtLoginController */ = { isa = PBXGroup; children = ( 055819761834172E00B44B00 /* LaunchAtLoginController.h */, 055819771834172E00B44B00 /* LaunchAtLoginController.m */, ); name = LaunchAtLoginController; path = ../Vendor/LaunchAtLoginController; sourceTree = ""; }; 055EB10C183472C400FF83A6 /* Vendor */ = { isa = PBXGroup; children = ( 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */, AE7303D51C935BDE00AF5499 /* NSStringEmojize */, F8D03BB01C3D95E600A64968 /* AHProxySettings */, 7B9A21541B9F0F10002539F7 /* DateTools */, 7B9A214F1B9F0BE6002539F7 /* STPrivilegedTask.h */, 7B9A21501B9F0BE6002539F7 /* STPrivilegedTask.m */, 055819751834171900B44B00 /* LaunchAtLoginController */, A22AF327189F823E0011DFCD /* NSColor+Hex.h */, A22AF328189F823E0011DFCD /* NSColor+Hex.m */, ); name = Vendor; path = BitBar; sourceTree = ""; }; 05DADFE7183323DD00409786 = { isa = PBXGroup; children = ( 05DAE006183323DD00409786 /* AppDelegate.m */, 05DAE0271833241D00409786 /* Plugins */, 05322A46183406D7004D9AFE /* Settings */, AE3C16771C93B135009AF7CA /* Extensions */, 055EB10C183472C400FF83A6 /* Vendor */, 05DADFFA183323DD00409786 /* Supporting Files */, 05DAE017183323DD00409786 /* BitBarTests */, 05DADFF1183323DD00409786 /* Products */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; }; 05DADFF1183323DD00409786 /* Products */ = { isa = PBXGroup; children = ( 05DADFF0183323DD00409786 /* BitBar.app */, 05DAE011183323DD00409786 /* BitBarTests.xctest */, 36DCD8D81C76027C004DE286 /* BitBarDistro.app */, 36372DD61C9424DB0005EB32 /* BitBarDistroTests.xctest */, ); name = Products; sourceTree = ""; }; 05DADFFA183323DD00409786 /* Supporting Files */ = { isa = PBXGroup; children = ( 05DB995B1C3D4B80008B5159 /* Images.xcassets */, 7B5D08F91BA8EF6300400886 /* README.md */, 05DADFFB183323DD00409786 /* BitBar-Info.plist */, 0531DCD81844070A007F0A96 /* App.xib */, 05590ACE1C3E74C100352E77 /* CHANGELOG.md */, ); name = "Supporting Files"; path = BitBar; sourceTree = ""; }; 05DAE017183323DD00409786 /* BitBarTests */ = { isa = PBXGroup; children = ( AE7303DC1C93721600AF5499 /* TestPlugins */, 05DAE02E1833276C00409786 /* PluginManagerTest.m */, 0599719718333DB300EA6D8D /* PluginTest.m */, AE4B1E4C1C9B7C4B009540AD /* Test Extensions */, 05DAE018183323DD00409786 /* Supporting Files */, ); path = BitBarTests; sourceTree = ""; }; 05DAE018183323DD00409786 /* Supporting Files */ = { isa = PBXGroup; children = ( 05DAE019183323DD00409786 /* BitBarTests-Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 05DAE0271833241D00409786 /* Plugins */ = { isa = PBXGroup; children = ( 05DAE02B1833274100409786 /* PluginManager.h */, 05DAE02C1833274100409786 /* PluginManager.m */, 059971941833394500EA6D8D /* Plugin.h */, 059971951833394500EA6D8D /* Plugin.m */, A281BDB3189066DE00C380A4 /* ExecutablePlugin.h */, A281BDB4189066DE00C380A4 /* ExecutablePlugin.m */, A281BDB61890670800C380A4 /* HTMLPlugin.h */, A281BDB71890670800C380A4 /* HTMLPlugin.m */, ); name = Plugins; path = BitBar; sourceTree = ""; }; 36D0CDF31D0AF009001AFB77 /* Products */ = { isa = PBXGroup; children = ( 36D0CDFF1D0AF009001AFB77 /* Sparkle.framework */, 36D0CE011D0AF009001AFB77 /* Sparkle Test App.app */, 36D0CE031D0AF009001AFB77 /* Sparkle Unit Tests.xctest */, 36D0CE051D0AF009001AFB77 /* BinaryDelta */, 36D0CE071D0AF009001AFB77 /* Autoupdate.app */, 36D0CE091D0AF009001AFB77 /* UI Tests.xctest */, 3ACB545A2561958E00407243 /* fileop */, ); name = Products; sourceTree = ""; }; 7B9A21541B9F0F10002539F7 /* DateTools */ = { isa = PBXGroup; children = ( 7B9A21551B9F0F10002539F7 /* DateTools.bundle */, 7B9A21561B9F0F10002539F7 /* DateTools.h */, 7B9A21571B9F0F10002539F7 /* DTConstants.h */, 7B9A21581B9F0F10002539F7 /* DTConstants.m */, 7B9A21591B9F0F10002539F7 /* DTError.h */, 7B9A215A1B9F0F10002539F7 /* DTError.m */, 7B9A215B1B9F0F10002539F7 /* DTTimePeriod.h */, 7B9A215C1B9F0F10002539F7 /* DTTimePeriod.m */, 7B9A215D1B9F0F10002539F7 /* DTTimePeriodChain.h */, 7B9A215E1B9F0F10002539F7 /* DTTimePeriodChain.m */, 7B9A215F1B9F0F10002539F7 /* DTTimePeriodCollection.h */, 7B9A21601B9F0F10002539F7 /* DTTimePeriodCollection.m */, 7B9A21611B9F0F10002539F7 /* DTTimePeriodGroup.h */, 7B9A21621B9F0F10002539F7 /* DTTimePeriodGroup.m */, 7B9A21631B9F0F10002539F7 /* NSDate+DateTools.h */, 7B9A21641B9F0F10002539F7 /* NSDate+DateTools.m */, ); name = DateTools; path = Vendor/DateTools/DateTools; sourceTree = SOURCE_ROOT; }; AE3C16771C93B135009AF7CA /* Extensions */ = { isa = PBXGroup; children = ( AE7303EB1C93984C00AF5499 /* NSString+ANSI.h */, AE7303EC1C93984C00AF5499 /* NSString+ANSI.m */, ); name = Extensions; path = BitBar; sourceTree = ""; }; AE4B1E4C1C9B7C4B009540AD /* Test Extensions */ = { isa = PBXGroup; children = ( AE7303DE1C93848400AF5499 /* PluginManager+Test.h */, AE7303DF1C93848400AF5499 /* PluginManager+Test.m */, ); name = "Test Extensions"; sourceTree = ""; }; AE7303D51C935BDE00AF5499 /* NSStringEmojize */ = { isa = PBXGroup; children = ( AE7303D61C935BDE00AF5499 /* emojis.h */, AE7303D71C935BDE00AF5499 /* NSString+Emojize.h */, AE7303D81C935BDE00AF5499 /* NSString+Emojize.m */, ); name = NSStringEmojize; path = Vendor/NSStringEmojize/NSStringEmojize; sourceTree = SOURCE_ROOT; }; F8D03BB01C3D95E600A64968 /* AHProxySettings */ = { isa = PBXGroup; children = ( F8D03BB11C3D95E600A64968 /* AHProxy.h */, F8D03BB21C3D95E600A64968 /* AHProxy.m */, F8D03BB31C3D95E600A64968 /* AHProxySettings.h */, F8D03BB41C3D95E600A64968 /* AHProxySettings.m */, F8D03BB51C3D95E600A64968 /* NSTask+useSystemProxies.h */, F8D03BB61C3D95E600A64968 /* NSTask+useSystemProxies.m */, ); name = AHProxySettings; path = Vendor/AHProxySettings/AHProxySettings; sourceTree = SOURCE_ROOT; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 05DADFEF183323DD00409786 /* BitBar */ = { isa = PBXNativeTarget; buildConfigurationList = 05DAE021183323DD00409786 /* Build configuration list for PBXNativeTarget "BitBar" */; buildPhases = ( 05DADFEC183323DD00409786 /* Sources */, 05DADFEE183323DD00409786 /* Resources */, 36D0CE0E1D0AF013001AFB77 /* Frameworks */, 36D0CE0F1D0AF013001AFB77 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 36D0CE0D1D0AF012001AFB77 /* PBXTargetDependency */, ); name = BitBar; productName = BitBar; productReference = 05DADFF0183323DD00409786 /* BitBar.app */; productType = "com.apple.product-type.application"; }; 05DAE010183323DD00409786 /* BitBarTests */ = { isa = PBXNativeTarget; buildConfigurationList = 05DAE024183323DD00409786 /* Build configuration list for PBXNativeTarget "BitBarTests" */; buildPhases = ( 05DAE00D183323DD00409786 /* Sources */, 05DAE00E183323DD00409786 /* Frameworks */, 05DAE00F183323DD00409786 /* Resources */, ); buildRules = ( ); dependencies = ( 05DAE016183323DD00409786 /* PBXTargetDependency */, ); name = BitBarTests; productName = BitBarTests; productReference = 05DAE011183323DD00409786 /* BitBarTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 36372DBC1C9424DB0005EB32 /* BitBarDistroTests */ = { isa = PBXNativeTarget; buildConfigurationList = 36372DD31C9424DB0005EB32 /* Build configuration list for PBXNativeTarget "BitBarDistroTests" */; buildPhases = ( 36372DC11C9424DB0005EB32 /* Sources */, 36372DD01C9424DB0005EB32 /* Frameworks */, 36372DD11C9424DB0005EB32 /* Resources */, ); buildRules = ( ); dependencies = ( 36372DBF1C9424DB0005EB32 /* PBXTargetDependency */, ); name = BitBarDistroTests; productName = BitBarTests; productReference = 36372DD61C9424DB0005EB32 /* BitBarDistroTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 36DCD8BC1C76027C004DE286 /* BitBarDistro */ = { isa = PBXNativeTarget; buildConfigurationList = 36DCD8D51C76027C004DE286 /* Build configuration list for PBXNativeTarget "BitBarDistro" */; buildPhases = ( 36DCD8BD1C76027C004DE286 /* Sources */, 36DCD8D11C76027C004DE286 /* Resources */, 36D0CE141D0AF019001AFB77 /* Frameworks */, 36D0CE151D0AF019001AFB77 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 36D0CE131D0AF019001AFB77 /* PBXTargetDependency */, ); name = BitBarDistro; productName = BitBar; productReference = 36DCD8D81C76027C004DE286 /* BitBarDistro.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 05DADFE8183323DD00409786 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0700; ORGANIZATIONNAME = "Bit Bar"; TargetAttributes = { 05DADFEF183323DD00409786 = { DevelopmentTeam = B3T8QSC4HG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Sandbox = { enabled = 0; }; }; }; 05DAE010183323DD00409786 = { TestTargetID = 05DADFEF183323DD00409786; }; 36372DBC1C9424DB0005EB32 = { TestTargetID = 36DCD8BC1C76027C004DE286; }; 36DCD8BC1C76027C004DE286 = { DevelopmentTeam = B3T8QSC4HG; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 05DADFEB183323DD00409786 /* Build configuration list for PBXProject "BitBar" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( English, en, Base, ); mainGroup = 05DADFE7183323DD00409786; productRefGroup = 05DADFF1183323DD00409786 /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 36D0CDF31D0AF009001AFB77 /* Products */; ProjectRef = 36D0CDF21D0AF009001AFB77 /* Sparkle.xcodeproj */; }, ); projectRoot = ""; targets = ( 05DADFEF183323DD00409786 /* BitBar */, 05DAE010183323DD00409786 /* BitBarTests */, 36DCD8BC1C76027C004DE286 /* BitBarDistro */, 36372DBC1C9424DB0005EB32 /* BitBarDistroTests */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 36D0CDFF1D0AF009001AFB77 /* Sparkle.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = Sparkle.framework; remoteRef = 36D0CDFE1D0AF009001AFB77 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 36D0CE011D0AF009001AFB77 /* Sparkle Test App.app */ = { isa = PBXReferenceProxy; fileType = wrapper.application; path = "Sparkle Test App.app"; remoteRef = 36D0CE001D0AF009001AFB77 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 36D0CE031D0AF009001AFB77 /* Sparkle Unit Tests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; path = "Sparkle Unit Tests.xctest"; remoteRef = 36D0CE021D0AF009001AFB77 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 36D0CE051D0AF009001AFB77 /* BinaryDelta */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.executable"; path = BinaryDelta; remoteRef = 36D0CE041D0AF009001AFB77 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 36D0CE071D0AF009001AFB77 /* Autoupdate.app */ = { isa = PBXReferenceProxy; fileType = wrapper.application; path = Autoupdate.app; remoteRef = 36D0CE061D0AF009001AFB77 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 36D0CE091D0AF009001AFB77 /* UI Tests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; path = "UI Tests.xctest"; remoteRef = 36D0CE081D0AF009001AFB77 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 3ACB545A2561958E00407243 /* fileop */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.executable"; path = fileop; remoteRef = 3ACB54592561958E00407243 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ 05DADFEE183323DD00409786 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 0531DCD91844070A007F0A96 /* App.xib in Resources */, 05DB995C1C3D4B80008B5159 /* Images.xcassets in Resources */, 7B9A21651B9F0F10002539F7 /* DateTools.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 05DAE00F183323DD00409786 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( AE7303DD1C93721600AF5499 /* TestPlugins in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36372DD11C9424DB0005EB32 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 36372DD21C9424DB0005EB32 /* TestPlugins in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36DCD8D11C76027C004DE286 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 36DCD8D21C76027C004DE286 /* App.xib in Resources */, 36DCD8D31C76027C004DE286 /* Images.xcassets in Resources */, 36DCD8D41C76027C004DE286 /* DateTools.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 05DADFEC183323DD00409786 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( AE3C167A1C93F71B009AF7CA /* NSString+Emojize.m in Sources */, F8D03BB71C3D95E600A64968 /* AHProxy.m in Sources */, 05DAE007183323DD00409786 /* AppDelegate.m in Sources */, 7B9A21661B9F0F10002539F7 /* DTConstants.m in Sources */, A281BDB81890670800C380A4 /* HTMLPlugin.m in Sources */, 7B9A21531B9F0BE6002539F7 /* STPrivilegedTask.m in Sources */, A281BDB5189066DE00C380A4 /* ExecutablePlugin.m in Sources */, 7B9A216B1B9F0F10002539F7 /* DTTimePeriodGroup.m in Sources */, F8D03BB91C3D95E600A64968 /* NSTask+useSystemProxies.m in Sources */, 05DAE02D1833274100409786 /* PluginManager.m in Sources */, 7B9A21671B9F0F10002539F7 /* DTError.m in Sources */, 7B9A216A1B9F0F10002539F7 /* DTTimePeriodCollection.m in Sources */, 05322A49183406E2004D9AFE /* NSUserDefaults+Settings.m in Sources */, 7B9A216C1B9F0F10002539F7 /* NSDate+DateTools.m in Sources */, A22AF329189F823E0011DFCD /* NSColor+Hex.m in Sources */, 059971961833394500EA6D8D /* Plugin.m in Sources */, 7B9A21691B9F0F10002539F7 /* DTTimePeriodChain.m in Sources */, AE7303ED1C93984C00AF5499 /* NSString+ANSI.m in Sources */, 055819781834172E00B44B00 /* LaunchAtLoginController.m in Sources */, F8D03BB81C3D95E600A64968 /* AHProxySettings.m in Sources */, 7B9A21681B9F0F10002539F7 /* DTTimePeriod.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 05DAE00D183323DD00409786 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( AE4B1E531C9C6351009540AD /* HTMLPlugin.m in Sources */, AE4B1E541C9C6351009540AD /* NSUserDefaults+Settings.m in Sources */, AE4B1E551C9C6351009540AD /* NSString+ANSI.m in Sources */, AE4B1E561C9C6351009540AD /* NSString+Emojize.m in Sources */, AE4B1E571C9C6351009540AD /* AHProxy.m in Sources */, AE4B1E581C9C6351009540AD /* AHProxySettings.m in Sources */, AE4B1E591C9C6351009540AD /* NSTask+useSystemProxies.m in Sources */, AE4B1E5A1C9C6351009540AD /* DTConstants.m in Sources */, AE4B1E5B1C9C6351009540AD /* DTError.m in Sources */, AE4B1E5C1C9C6351009540AD /* DTTimePeriod.m in Sources */, AE4B1E5D1C9C6351009540AD /* DTTimePeriodChain.m in Sources */, AE4B1E5E1C9C6351009540AD /* DTTimePeriodCollection.m in Sources */, AE4B1E5F1C9C6351009540AD /* DTTimePeriodGroup.m in Sources */, AE4B1E601C9C6351009540AD /* NSDate+DateTools.m in Sources */, AE4B1E611C9C6351009540AD /* STPrivilegedTask.m in Sources */, AE4B1E621C9C6351009540AD /* LaunchAtLoginController.m in Sources */, AE4B1E631C9C6351009540AD /* NSColor+Hex.m in Sources */, AE4B1E4D1C9C631B009540AD /* PluginManager.m in Sources */, AE4B1E4E1C9C631B009540AD /* Plugin.m in Sources */, AE4B1E4F1C9C631B009540AD /* ExecutablePlugin.m in Sources */, 0599719818333DB300EA6D8D /* PluginTest.m in Sources */, 05DAE02F1833276C00409786 /* PluginManagerTest.m in Sources */, AE7303E01C93848400AF5499 /* PluginManager+Test.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36372DC11C9424DB0005EB32 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( AE4B1E641C9C636A009540AD /* HTMLPlugin.m in Sources */, AE4B1E651C9C636A009540AD /* NSUserDefaults+Settings.m in Sources */, AE4B1E661C9C636A009540AD /* NSString+ANSI.m in Sources */, AE4B1E671C9C636A009540AD /* NSString+Emojize.m in Sources */, AE4B1E681C9C636A009540AD /* AHProxy.m in Sources */, AE4B1E691C9C636A009540AD /* AHProxySettings.m in Sources */, AE4B1E6A1C9C636A009540AD /* NSTask+useSystemProxies.m in Sources */, AE4B1E6B1C9C636A009540AD /* DTConstants.m in Sources */, AE4B1E6C1C9C636A009540AD /* DTError.m in Sources */, AE4B1E6D1C9C636A009540AD /* DTTimePeriod.m in Sources */, AE4B1E6E1C9C636A009540AD /* DTTimePeriodChain.m in Sources */, AE4B1E6F1C9C636A009540AD /* DTTimePeriodCollection.m in Sources */, AE4B1E701C9C636A009540AD /* DTTimePeriodGroup.m in Sources */, AE4B1E711C9C636A009540AD /* NSDate+DateTools.m in Sources */, AE4B1E721C9C636A009540AD /* STPrivilegedTask.m in Sources */, AE4B1E731C9C636A009540AD /* LaunchAtLoginController.m in Sources */, AE4B1E741C9C636A009540AD /* NSColor+Hex.m in Sources */, AE4B1E501C9C632A009540AD /* PluginManager.m in Sources */, AE4B1E511C9C632A009540AD /* Plugin.m in Sources */, AE4B1E521C9C632A009540AD /* ExecutablePlugin.m in Sources */, 36372DCC1C9424DB0005EB32 /* PluginTest.m in Sources */, 36372DCE1C9424DB0005EB32 /* PluginManagerTest.m in Sources */, 36372DCF1C9424DB0005EB32 /* PluginManager+Test.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 36DCD8BD1C76027C004DE286 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 36DCD8BE1C76027C004DE286 /* AHProxy.m in Sources */, 36DCD8BF1C76027C004DE286 /* AppDelegate.m in Sources */, 36DCD8C01C76027C004DE286 /* DTConstants.m in Sources */, 36DCD8C11C76027C004DE286 /* HTMLPlugin.m in Sources */, 36DCD8C21C76027C004DE286 /* STPrivilegedTask.m in Sources */, 36DCD8C31C76027C004DE286 /* ExecutablePlugin.m in Sources */, 36DCD8C41C76027C004DE286 /* DTTimePeriodGroup.m in Sources */, 36DCD8C51C76027C004DE286 /* NSTask+useSystemProxies.m in Sources */, 36DCD8C61C76027C004DE286 /* PluginManager.m in Sources */, 36DCD8C71C76027C004DE286 /* DTError.m in Sources */, 36DCD8C81C76027C004DE286 /* DTTimePeriodCollection.m in Sources */, 36DCD8C91C76027C004DE286 /* NSUserDefaults+Settings.m in Sources */, 36DCD8CA1C76027C004DE286 /* NSDate+DateTools.m in Sources */, AE4B1E391C9B2B0F009540AD /* NSString+ANSI.m in Sources */, 36DCD8CB1C76027C004DE286 /* NSColor+Hex.m in Sources */, 36DCD8CC1C76027C004DE286 /* Plugin.m in Sources */, 36DCD8CD1C76027C004DE286 /* DTTimePeriodChain.m in Sources */, 36DCD8CE1C76027C004DE286 /* LaunchAtLoginController.m in Sources */, 36DCD8CF1C76027C004DE286 /* AHProxySettings.m in Sources */, 36DCD8D01C76027C004DE286 /* DTTimePeriod.m in Sources */, AE7303DB1C935BDE00AF5499 /* NSString+Emojize.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 05DAE016183323DD00409786 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 05DADFEF183323DD00409786 /* BitBar */; targetProxy = 05DAE015183323DD00409786 /* PBXContainerItemProxy */; }; 36372DBF1C9424DB0005EB32 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 36DCD8BC1C76027C004DE286 /* BitBarDistro */; targetProxy = 36372DC01C9424DB0005EB32 /* PBXContainerItemProxy */; }; 36D0CE0D1D0AF012001AFB77 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Sparkle; targetProxy = 36D0CE0C1D0AF012001AFB77 /* PBXContainerItemProxy */; }; 36D0CE131D0AF019001AFB77 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Sparkle; targetProxy = 36D0CE121D0AF019001AFB77 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 05DAE01F183323DD00409786 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = c11; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 11.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 05DAE020183323DD00409786 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = c11; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 11.0; SDKROOT = macosx; }; name = Release; }; 05DAE022183323DD00409786 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1.10.2; DEVELOPMENT_TEAM = B3T8QSC4HG; HEADER_SEARCH_PATHS = "./Vendor/**"; INFOPLIST_FILE = "BitBar/BitBar-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 1.10.2; PRODUCT_BUNDLE_IDENTIFIER = "com.matryer.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; WRAPPER_EXTENSION = app; }; name = Debug; }; 05DAE023183323DD00409786 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1.10.2; DEVELOPMENT_TEAM = B3T8QSC4HG; HEADER_SEARCH_PATHS = "./Vendor/**"; INFOPLIST_FILE = "BitBar/BitBar-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 1.10.2; PRODUCT_BUNDLE_IDENTIFIER = "com.matryer.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; WRAPPER_EXTENSION = app; }; name = Release; }; 05DAE025183323DD00409786 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = "BitBarTests/BitBarTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.bitbarapp.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BitBar.app/Contents/MacOS/BitBar"; WRAPPER_EXTENSION = xctest; }; name = Debug; }; 05DAE026183323DD00409786 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); GCC_PRECOMPILE_PREFIX_HEADER = NO; INFOPLIST_FILE = "BitBarTests/BitBarTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.bitbarapp.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BitBar.app/Contents/MacOS/BitBar"; WRAPPER_EXTENSION = xctest; }; name = Release; }; 36372DD41C9424DB0005EB32 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", DISTRO, ); INFOPLIST_FILE = "BitBarTests/BitBarTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.bitbarapp.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BitBarDistro.app/Contents/MacOS/BitBarDistro"; WRAPPER_EXTENSION = xctest; }; name = Debug; }; 36372DD51C9424DB0005EB32 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREPROCESSOR_DEFINITIONS = DISTRO; INFOPLIST_FILE = "BitBarTests/BitBarTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.bitbarapp.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BitBarDistro.app/Contents/MacOS/BitBarDistro"; WRAPPER_EXTENSION = xctest; }; name = Release; }; 36DCD8D61C76027C004DE286 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1.10.2; DEVELOPMENT_TEAM = B3T8QSC4HG; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", DISTRO, ); INFOPLIST_FILE = "$(SRCROOT)/BitBar/BitBar-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 1.10.2; PRODUCT_BUNDLE_IDENTIFIER = "com.matryer.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; WRAPPER_EXTENSION = app; }; name = Debug; }; 36DCD8D71C76027C004DE286 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1.10.2; DEVELOPMENT_TEAM = B3T8QSC4HG; GCC_PREPROCESSOR_DEFINITIONS = DISTRO; INFOPLIST_FILE = "$(SRCROOT)/BitBar/BitBar-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.9; MARKETING_VERSION = 1.10.2; PRODUCT_BUNDLE_IDENTIFIER = "com.matryer.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; WRAPPER_EXTENSION = app; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 05DADFEB183323DD00409786 /* Build configuration list for PBXProject "BitBar" */ = { isa = XCConfigurationList; buildConfigurations = ( 05DAE01F183323DD00409786 /* Debug */, 05DAE020183323DD00409786 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 05DAE021183323DD00409786 /* Build configuration list for PBXNativeTarget "BitBar" */ = { isa = XCConfigurationList; buildConfigurations = ( 05DAE022183323DD00409786 /* Debug */, 05DAE023183323DD00409786 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 05DAE024183323DD00409786 /* Build configuration list for PBXNativeTarget "BitBarTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 05DAE025183323DD00409786 /* Debug */, 05DAE026183323DD00409786 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 36372DD31C9424DB0005EB32 /* Build configuration list for PBXNativeTarget "BitBarDistroTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 36372DD41C9424DB0005EB32 /* Debug */, 36372DD51C9424DB0005EB32 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 36DCD8D51C76027C004DE286 /* Build configuration list for PBXNativeTarget "BitBarDistro" */ = { isa = XCConfigurationList; buildConfigurations = ( 36DCD8D61C76027C004DE286 /* Debug */, 36DCD8D71C76027C004DE286 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 05DADFE8183323DD00409786 /* Project object */; } ================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/project.xcworkspace/xcshareddata/BitBar.xccheckout ================================================ IDESourceControlProjectFavoriteDictionaryKey IDESourceControlProjectIdentifier B2EB30C0-666C-46EA-85F2-0586829722B1 IDESourceControlProjectName BitBar IDESourceControlProjectOriginsDictionary 300A25E6089E8365F1B9BCC0BF644BF304850FDB https://github.com/kamenevn/bitbar.git IDESourceControlProjectPath App/BitBar.xcodeproj IDESourceControlProjectRelativeInstallPathDictionary 300A25E6089E8365F1B9BCC0BF644BF304850FDB ../../.. IDESourceControlProjectURL https://github.com/kamenevn/bitbar.git IDESourceControlProjectVersion 111 IDESourceControlProjectWCCIdentifier 300A25E6089E8365F1B9BCC0BF644BF304850FDB IDESourceControlProjectWCConfigurations IDESourceControlRepositoryExtensionIdentifierKey public.vcs.git IDESourceControlWCCIdentifierKey 300A25E6089E8365F1B9BCC0BF644BF304850FDB IDESourceControlWCCName bitbar ================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/project.xcworkspace/xcshareddata/BitBar.xcscmblueprint ================================================ { "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "300A25E6089E8365F1B9BCC0BF644BF304850FDB", "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { }, "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { "C0AF55779579B522417152BFAB32AD2A49AFBDFE" : 0, "EF1CF5E1F342919DE309B5C9DAEDDCF1D12D0402" : 0, "4FA437A61B0C4384CD084682AFD40F7D2624731D" : 0, "EFCDA5DAAD3299C621AB4AD8A1985311413BE697" : 0, "5A18CC4105103F3DCAAD74EC93EFD3FF8BB5508A" : 0, "F8622BA04EBD09F8BA8DC9C0C199149407552BFA" : 0, "300A25E6089E8365F1B9BCC0BF644BF304850FDB" : 0 }, "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "B2EB30C0-666C-46EA-85F2-0586829722B1", "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { "C0AF55779579B522417152BFAB32AD2A49AFBDFE" : "bitbar\/App\/Vendor\/AHProxySettings\/", "EF1CF5E1F342919DE309B5C9DAEDDCF1D12D0402" : "bitbar\/App\/Vendor\/Sparkle\/", "4FA437A61B0C4384CD084682AFD40F7D2624731D" : "bitbar\/App\/Vendor\/LaunchAtLoginController\/", "EFCDA5DAAD3299C621AB4AD8A1985311413BE697" : "bitbar\/App\/Vendor\/NSStringEmojize\/", "5A18CC4105103F3DCAAD74EC93EFD3FF8BB5508A" : "bitbar\/App\/Vendor\/STPrivilegedTask\/", "F8622BA04EBD09F8BA8DC9C0C199149407552BFA" : "bitbar\/App\/Vendor\/DateTools\/", "300A25E6089E8365F1B9BCC0BF644BF304850FDB" : "bitbar" }, "DVTSourceControlWorkspaceBlueprintNameKey" : "BitBar", "DVTSourceControlWorkspaceBlueprintVersion" : 204, "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "App\/BitBar.xcodeproj", "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/kamenevn\/bitbar.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "300A25E6089E8365F1B9BCC0BF644BF304850FDB" }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/biocross\/LaunchAtLoginController--with-ARC-.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "4FA437A61B0C4384CD084682AFD40F7D2624731D" }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/sveinbjornt\/STPrivilegedTask.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "5A18CC4105103F3DCAAD74EC93EFD3FF8BB5508A" }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/eahrold\/AHProxySettings.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "C0AF55779579B522417152BFAB32AD2A49AFBDFE" }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/sparkle-project\/Sparkle.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EF1CF5E1F342919DE309B5C9DAEDDCF1D12D0402" }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/diy\/NSStringEmojize.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EFCDA5DAAD3299C621AB4AD8A1985311413BE697" }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/MatthewYork\/DateTools.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F8622BA04EBD09F8BA8DC9C0C199149407552BFA" } ] } ================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/xcshareddata/xcschemes/BitBar.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/BitBar.xcodeproj/xcshareddata/xcschemes/BitBarDistro.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/BitBarTests/BitBarTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSUIElement ================================================ FILE: archive/bitbar/App/BitBarTests/PluginManager+Test.h ================================================ // // PluginManager+Test.h // BitBar // // Created by Kent Karlsson on 3/11/16. // Copyright © 2016 Bit Bar. All rights reserved. // #import #import "PluginManager.h" @interface PluginManager (Test) + (NSString*)pluginPath; + (PluginManager*)testManager; @end ================================================ FILE: archive/bitbar/App/BitBarTests/PluginManager+Test.m ================================================ // // PluginManager+Test.m // BitBar // // Created by Kent Karlsson on 3/11/16. // Copyright © 2016 Bit Bar. All rights reserved. // #import "PluginManager+Test.h" // This class is just here so that we can get the path to the test bundle @interface BBTestClass : NSObject + (NSString*)pluginPath; @end @implementation BBTestClass + (NSString*)pluginPath { return [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:@"TestPlugins"]; } @end @implementation PluginManager (Test) + (NSString*)pluginPath { return [BBTestClass pluginPath]; } + (PluginManager*)testManager { id delegate = [NSApplication sharedApplication].delegate; return [delegate valueForKey:@"pluginManager"]; } @end ================================================ FILE: archive/bitbar/App/BitBarTests/PluginManagerTest.m ================================================ // // PluginManagerTest.m // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // #import #import "PluginManager+Test.h" #import "Plugin.h" @interface PluginManagerTest : XCTestCase @end @implementation PluginManagerTest - (void)testInit { PluginManager *manager = [PluginManager testManager]; XCTAssert([manager.path isEqualToString:[PluginManager pluginPath]]); XCTAssertNotNil(manager.path); } - (void)testPluginFiles { PluginManager *manager = [PluginManager testManager]; NSArray *pluginFiles = manager.plugins; XCTAssertEqual((NSUInteger)3, [pluginFiles count], @"pluginFiles count"); } - (void)testPlugins { PluginManager *manager = [PluginManager testManager]; NSArray *plugins = manager.plugins; XCTAssertEqual((NSUInteger)3, [plugins count], @"plugins count"); Plugin *one = [plugins objectAtIndex:0]; XCTAssertNotNil(one, @"one shouldn't be nil"); XCTAssertEqual(manager, one.manager, @"manager"); XCTAssert([one.name isEqualToString:@"one.10s.sh"], @"name"); XCTAssert([one.path isEqualToString:[[PluginManager pluginPath] stringByAppendingPathComponent:@"one.10s.sh"]], @"path"); } - (void)testStatusBar { PluginManager *manager = [PluginManager testManager]; NSStatusBar *statusBar = manager.statusBar; XCTAssertNotNil(statusBar, @"statusBar"); XCTAssertEqual([NSStatusBar systemStatusBar], statusBar, @"statusBar should default to system one"); // set on explicitly NSStatusBar *newBar = NSStatusBar.new; manager.statusBar = newBar; XCTAssertEqual(newBar, manager.statusBar); } @end ================================================ FILE: archive/bitbar/App/BitBarTests/PluginTest.m ================================================ // // PluginTest.m // BitBar // // Created by Mat Ryer on 11/12/13. // Copyright (c) 2013 Bit Bar. All rights reserved. // #import #import "ExecutablePlugin.h" #import "PluginManager+Test.h" @interface PluginTest : XCTestCase @end @implementation PluginTest - (void)testInitWithManager { PluginManager *manager = [PluginManager testManager]; XCTAssertNotNil(manager); Plugin *p = [Plugin.alloc initWithManager:manager]; XCTAssertNotNil(p); XCTAssertEqual(p.manager, manager); XCTAssertEqual((NSInteger)-1, p.currentLine); XCTAssertEqual((NSInteger)5, p.cycleLinesIntervalSeconds); } - (void)testStatusItem { PluginManager *manager = [PluginManager testManager]; Plugin *p = [Plugin.alloc initWithManager:manager]; NSStatusItem *item = p.statusItem; XCTAssertNotNil(item, @"item nil?"); XCTAssertEqual((CGFloat)NSVariableStatusItemLength, item.length, @"length == NSVariableStatusItemLength"); // make sure it has a menu XCTAssertNotNil(item.menu, @"menu"); } - (void)testExample { PluginManager *manager = [PluginManager testManager]; Plugin *p = [Plugin.alloc initWithManager:manager]; p.name = @"name.10s.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)10, [p.refreshIntervalSeconds doubleValue], @"10s"); p.name = @"name.10m.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(10*60), [p.refreshIntervalSeconds doubleValue], @"10m"); p.name = @"name.10h.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(10*60*60), [p.refreshIntervalSeconds doubleValue], @"10h"); p.name = @"name.10d.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(10*60*60*24), [p.refreshIntervalSeconds doubleValue], @"10d"); p.name = @"name.10S.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)10, [p.refreshIntervalSeconds doubleValue], @"10S"); p.name = @"name.10M.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(10*60), [p.refreshIntervalSeconds doubleValue], @"10M"); p.name = @"name.10H.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(10*60*60), [p.refreshIntervalSeconds doubleValue], @"10H"); p.name = @"name.10D.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(10*60*60*24), [p.refreshIntervalSeconds doubleValue], @"10D"); // and some failures p.name = @"name.10.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(60), [p.refreshIntervalSeconds doubleValue], @"10"); p.name = @"name.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(60), [p.refreshIntervalSeconds doubleValue], @"name.sh"); p.name = @"name.bollocks.sh"; p.refreshIntervalSeconds = nil; XCTAssertEqual((double)(60), [p.refreshIntervalSeconds doubleValue], @"name.sh"); } - (void)testRefreshContentByExecutingCommandSuccess { PluginManager *manager = [PluginManager testManager]; ExecutablePlugin *p = [ExecutablePlugin.alloc initWithManager:manager]; p.name = @"one.10s.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; XCTAssertEqual(YES, [p refreshContentByExecutingCommand]); XCTAssert([p.content isEqualToString:@"This is just a test.\n"], @"Content"); XCTAssert([[p allContent] isEqualToString:@"This is just a test.\n"], @"all content"); XCTAssertEqual(NO, p.lastCommandWasError); p.name = @"two.5m.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; XCTAssertEqual(NO, [p refreshContentByExecutingCommand]); XCTAssert([p.content isEqualToString:@""], @"content"); XCTAssert([p.allContent isEqualToString:@"⚠️\n---\nSomething went tits up."], @"all content"); XCTAssertEqual(YES, p.lastCommandWasError); } - (void)testRefreshContentByExecutingCommandError { PluginManager *manager = [PluginManager testManager]; ExecutablePlugin *p = [ExecutablePlugin.alloc initWithManager:manager]; p.name = @"two.5m.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; XCTAssertEqual(NO, [p refreshContentByExecutingCommand]); XCTAssert([p.errorContent isEqualToString:@"Something went tits up."], @"Error content"); XCTAssert([p.allContent isEqualToString:@"⚠️\n---\nSomething went tits up."], @"all content"); XCTAssertEqual(YES, p.lastCommandWasError); p.name = @"one.10s.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; XCTAssertEqual(YES, [p refreshContentByExecutingCommand]); XCTAssert([p.errorContent isEqualToString:@""], @"Error content"); XCTAssert([p.allContent isEqualToString:@"This is just a test.\n"], @"all content"); XCTAssertEqual(NO, p.lastCommandWasError); } - (void)testContentLines { PluginManager *manager = [PluginManager testManager]; Plugin *p = [Plugin.alloc initWithManager:manager]; p.content = @"---\nHello\nWorld\nOf\nBitBar"; NSArray *lines = p.allContentLines; XCTAssertEqual((NSUInteger)4, lines.count); XCTAssert([lines[0] isEqualToString:@"Hello"]); XCTAssert([lines[1] isEqualToString:@"World"]); XCTAssert([lines[2] isEqualToString:@"Of"]); XCTAssert([lines[3] isEqualToString:@"BitBar"]); p.content = @"---\n Hello \t \n\tWorld\t\n Of \n BitBar"; [p contentHasChanged]; lines = p.allContentLines; XCTAssertEqual((NSUInteger)4, lines.count); XCTAssert([lines[0] isEqualToString:@" Hello \t "]); XCTAssert([lines[1] isEqualToString:@"\tWorld\t"]); XCTAssert([lines[2] isEqualToString:@" Of "]); XCTAssert([lines[3] isEqualToString:@" BitBar"]); p.content = @"---\n\n\n Hello \t \n\t\n\n\nWorld\t\n\n\n\n Of \n BitBar"; [p contentHasChanged]; lines = p.allContentLines; XCTAssertEqual((NSUInteger)4, lines.count); XCTAssert([lines[0] isEqualToString:@" Hello \t "]); XCTAssert([lines[1] isEqualToString:@"World\t"]); XCTAssert([lines[2] isEqualToString:@" Of "]); XCTAssert([lines[3] isEqualToString:@" BitBar"]); p.content = @"---\n\n\n Hello \t \n\t\nWorld\n\n---\nThe World\t\n\n\n\n Of \n BitBar"; [p contentHasChanged]; lines = p.allContentLines; XCTAssertEqual((NSUInteger)6, lines.count); XCTAssert([lines[0] isEqualToString:@" Hello \t "]); XCTAssert([lines[1] isEqualToString:@"World"]); XCTAssert([lines[2] isEqualToString:@"---"]); XCTAssert([lines[3] isEqualToString:@"The World\t"]); XCTAssert([lines[4] isEqualToString:@" Of "]); XCTAssert([lines[5] isEqualToString:@" BitBar"]); } - (void)testIsMultiline { PluginManager *manager = [PluginManager testManager]; Plugin *p = [Plugin.alloc initWithManager:manager]; p.content = @"Hello\nWorld\nOf\nBitBar"; XCTAssertEqual(YES, p.isMultiline); p.content = @"One line mate"; XCTAssertEqual(NO, p.isMultiline); } - (void)testRefresh { PluginManager *manager = [PluginManager testManager]; ExecutablePlugin *p = [ExecutablePlugin.alloc initWithManager:manager]; p.name = @"three.7d.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; XCTAssertEqual(YES, [p refresh]); [self keyValueObservingExpectationForObject:p keyPath:@"currentLine" expectedValue:0]; [self waitForExpectationsWithTimeout:5 handler:^(NSError * _Nullable error) { XCTAssert([p.statusItem.title isEqualToString:@"line 1"]); }]; } - (void)testCycleLinesAndCurrentLine { PluginManager *manager = [PluginManager testManager]; ExecutablePlugin *p = [ExecutablePlugin.alloc initWithManager:manager]; p.name = @"three.7d.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; XCTAssertEqual((NSInteger)-1, p.currentLine); [p refresh]; [self keyValueObservingExpectationForObject:p keyPath:@"currentLine" expectedValue:0]; [self waitForExpectationsWithTimeout:5 handler:^(NSError * _Nullable error) { XCTAssertEqual((NSInteger)0, p.currentLine); XCTAssert([p.statusItem.title isEqualToString:@"line 1"]); [p cycleLines]; XCTAssertEqual((NSInteger)1, p.currentLine); XCTAssert([p.statusItem.title isEqualToString:@"line 2"]); [p cycleLines]; XCTAssertEqual((NSInteger)2, p.currentLine); XCTAssert([p.statusItem.title isEqualToString:@"line 3"]); [p cycleLines]; XCTAssertEqual((NSInteger)0, p.currentLine); XCTAssert([p.statusItem.title isEqualToString:@"line 1"]); [p cycleLines]; XCTAssertEqual((NSInteger)1, p.currentLine); XCTAssert([p.statusItem.title isEqualToString:@"line 2"]); [p cycleLines]; XCTAssertEqual((NSInteger)2, p.currentLine); XCTAssert([p.statusItem.title isEqualToString:@"line 3"]); }]; } - (void)testRebuildMenuForStatusItem { PluginManager *manager = [PluginManager testManager]; ExecutablePlugin *p = [ExecutablePlugin.alloc initWithManager:manager]; p.name = @"three.7d.sh"; p.path = [[PluginManager pluginPath] stringByAppendingPathComponent:p.name]; [p refreshContentByExecutingCommand]; [p rebuildMenuForStatusItem:p.statusItem]; NSUInteger itemCount = 3; #ifdef DISTRO itemCount += 2; #else itemCount += 3; #endif XCTAssertEqual(itemCount, [[p.statusItem.menu itemArray] count]); } - (void)testParameterANSI { PluginManager *manager = [PluginManager testManager]; Plugin *p = [Plugin.alloc initWithManager:manager]; NSMenuItem* item; NSString* helloWorld = @"\033[1;31mH\033[0mello \033[32mW\033[0morld"; // test disabling ansi parsing item = [p buildMenuItemForLine:[NSString stringWithFormat:@"%@ | ansi=false", helloWorld]]; XCTAssert([item.title isEqualToString:helloWorld]); // unchanged // test foreground and resetting item = [p buildMenuItemForLine:[NSString stringWithFormat:@"%@", helloWorld]]; XCTAssertEqual(item.title.length, 11); NSDictionary *hAttr, *wAttr, *nAttr; hAttr = [item.attributedTitle attributesAtIndex:0 effectiveRange:nil]; // H nAttr = [item.attributedTitle attributesAtIndex:1 effectiveRange:nil]; // space wAttr = [item.attributedTitle attributesAtIndex:6 effectiveRange:nil]; // W XCTAssertNotEqual(hAttr[NSForegroundColorAttributeName], wAttr[NSForegroundColorAttributeName]); // different colors XCTAssertNil(nAttr[NSForegroundColorAttributeName]); // no color // test background, resetting and that font isn't touched item = [p buildMenuItemForLine:@"a\033[40mb\033[0mc | font=courier"]; NSDictionary *attr; attr = [item.attributedTitle attributesAtIndex:0 effectiveRange:nil]; // a, no background, font courier XCTAssertNil(attr[NSBackgroundColorAttributeName]); XCTAssert([[(NSFont*)attr[NSFontAttributeName] fontName] isEqualToString:@"Courier"]); attr = [item.attributedTitle attributesAtIndex:1 effectiveRange:nil]; // b, has background, font courier XCTAssertNotNil(attr[NSBackgroundColorAttributeName]); XCTAssert([[(NSFont*)attr[NSFontAttributeName] fontName] isEqualToString:@"Courier"]); attr = [item.attributedTitle attributesAtIndex:2 effectiveRange:nil]; // c, no background, font courier XCTAssertNil(attr[NSBackgroundColorAttributeName]); XCTAssert([[(NSFont*)attr[NSFontAttributeName] fontName] isEqualToString:@"Courier"]); } - (void)testEmoji { PluginManager *manager = [PluginManager testManager]; Plugin *p = [Plugin.alloc initWithManager:manager]; p.content = @":dog:\n:dog: | emojize=false\n:made_up:\n"; [p rebuildMenuForStatusItem:p.statusItem]; NSArray* items = p.statusItem.menu.itemArray; XCTAssertEqual(((NSMenuItem*)items[0]).title.length, 2); // should parse (dog is 2 UTF-16 characters) XCTAssertEqual(((NSMenuItem*)items[1]).title.length, 5); // should not XCTAssertEqual(((NSMenuItem*)items[2]).title.length, 9); // should not } - (void)testSubmenus { PluginManager *manager = [PluginManager testManager]; Plugin *p = [[Plugin alloc] initWithManager:manager]; NSString *item = @"Main menu"; NSString *subItem = @"Sub menu"; NSString *subItem2 = @"Sub menu item two"; NSString *subItem2Sub = @"Sub, sub, menu item"; p.content = [NSString stringWithFormat:@"---\n%@\n--%@\n-----\n--%@\n----%@", item, subItem, subItem2, subItem2Sub]; [p rebuildMenuForStatusItem:p.statusItem]; NSArray *items = p.statusItem.menu.itemArray; XCTAssertEqualObjects(items[0].title, item); items = items[0].submenu.itemArray; XCTAssertEqual(items.count, 3); XCTAssertEqualObjects(items[0].title, subItem); XCTAssertNil(items[0].submenu); XCTAssertTrue(items[1].separatorItem); XCTAssertEqualObjects(items[2].title, subItem2); XCTAssertEqual(items[2].submenu.itemArray.count, 1); XCTAssertEqualObjects(items[2].submenu.itemArray[0].title, subItem2Sub); } - (void)testStatusItemAction { PluginManager *manager = [PluginManager testManager]; Plugin *p = [[Plugin alloc] initWithManager:manager]; p.content = @"No action\n|href=\n|bash=\n|refresh="; for (NSUInteger i = 0; i < p.titleLines.count + 1; i++) { [p cycleLines]; NSUInteger currentLine = i % p.titleLines.count; XCTAssertEqual(p.currentLine, currentLine); if (currentLine == 0) { XCTAssertNotNil(p.statusItem.menu); XCTAssertEqual(p.statusItem.action, NULL); XCTAssertNil(p.statusItem.target); } else { XCTAssertNil(p.statusItem.menu); XCTAssertEqual(p.statusItem.action, NSSelectorFromString(@"statusItemClicked")); XCTAssertEqual(p.statusItem.target, p); NSDictionary *params = [p dictionaryForLine:p.titleLines[currentLine]]; if (currentLine == 1) { XCTAssertNotNil(params[@"href"]); } else if (currentLine == 2) { XCTAssertNotNil(params[@"bash"]); } else if (currentLine == 3) { XCTAssertNotNil(params[@"refresh"]); } } } } @end ================================================ FILE: archive/bitbar/App/BitBarTests/TestPlugins/one.10s.sh ================================================ #!/bin/bash # one.sh - a test BitBar plugin # BitBar # # Created by Mat Ryer on 11/12/13. # Copyright (c) 2013 Bit Bar. All rights reserved. echo "This is just a test." ================================================ FILE: archive/bitbar/App/BitBarTests/TestPlugins/three.7d.sh ================================================ #!/bin/bash # three.sh - this test contains multiple lines with whitespace # BitBar # # Created by Mat Ryer on 11/12/13. # Copyright (c) 2013 Bit Bar. All rights reserved. echo "" echo "line 1" echo " " echo "line 2" echo " " echo "line 3" ================================================ FILE: archive/bitbar/App/BitBarTests/TestPlugins/two.5m.sh ================================================ #!/bin/bash # two.sh # BitBar # # Created by Mat Ryer on 11/12/13. # Copyright (c) 2013 Bit Bar. All rights reserved. echoerr() { echo -n "$@" 1>&2; } echoerr Something went tits up. exit 1 ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/.gitignore ================================================ # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # # Pods/ ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxyExampe/AppDelegate.h ================================================ // // AppDelegate.h // AHProxyExampe // // Created by Eldon on 11/11/14. // Copyright (c) 2014 Eldon Ahrold. All rights reserved. // #import @interface AppDelegate : NSObject @property (weak) IBOutlet NSTextField *HTTPProxy; @property (weak) IBOutlet NSTextField *HTTPSProxy; @property (weak) IBOutlet NSTextField *FTPProxy; @property (weak) IBOutlet NSTextField *SOCKSProxy; @property (weak) IBOutlet NSTextField *AUTOProxy; @property (weak) IBOutlet NSButton *useFailoverButton; @property (weak) IBOutlet NSButton *runButton; @property (weak) IBOutlet NSButton *runWithoutButton; @property (weak) IBOutlet NSTextField *runStatusTF; @property (weak) IBOutlet NSTextField *testURL; - (IBAction)refresh:(id)sender; - (IBAction)runTaskWithProxy:(NSButton *)sender; @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxyExampe/AppDelegate.m ================================================ // // AppDelegate.m // AHProxyExampe // // Created by Eldon on 11/11/14. // Copyright (c) 2014 Eldon Ahrold. All rights reserved. // #import "AppDelegate.h" #import "AHProxySettings.h" #import "NSTask+useSystemProxies.h" @interface AppDelegate () { AHProxySettings *_settings; NSTask *_task; } @property (weak) IBOutlet NSWindow *window; @end @implementation AppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application _testURL.stringValue = @"https://github.com"; [self refresh:nil]; } - (void)applicationWillTerminate:(NSNotification *)aNotification { } - (IBAction)refresh:(id)sender { _settings = [[AHProxySettings alloc] init]; _settings.useAutoDetectAsFailover = _useFailoverButton.state; _settings.destinationURL = _testURL.stringValue; _HTTPProxy.stringValue = _settings.HTTPProxy ? _settings.HTTPProxy.exportString : @""; _HTTPSProxy.stringValue = _settings.HTTPSProxy ? _settings.HTTPSProxy.exportString : @""; _FTPProxy.stringValue = _settings.FTPProxy ? _settings.FTPProxy.exportString : @""; _SOCKSProxy.stringValue = _settings.SOCKSProxy ? _settings.SOCKSProxy.exportString : @""; _AUTOProxy.stringValue = _settings.autoDetectedProxies ? [[_settings.autoDetectedProxies firstObject] exportString] : @""; } - (IBAction)runTaskWithProxy:(NSButton *)sender { [self setupTask]; [_task useSystemProxiesForDestination:_testURL.stringValue]; NSLog(@"%@",_task.environment); [_task launch]; } - (IBAction)runTaskWithOutProxy:(id)sender { [self setupTask]; [_task launch]; } - (void)setupTask { _runStatusTF.stringValue = @"Checking..."; [_runButton setEnabled:NO]; [_runWithoutButton setEnabled:NO]; _task = [[NSTask alloc] init]; _task.launchPath = @"/usr/bin/curl"; _task.arguments = @[ @"-k", _testURL.stringValue ]; _task.standardOutput = [NSPipe pipe]; _task.standardError = [NSPipe pipe]; __weak typeof(self) weakSelf = self; [_task setTerminationHandler:^(NSTask *task) { typeof(self) strongSelf = weakSelf; if (task.terminationStatus > 0) { NSData *data = [[task.standardError fileHandleForReading] readDataToEndOfFile]; NSString *errString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",errString); strongSelf.runStatusTF.stringValue = [NSString stringWithFormat:@"Unable to connect to server [error code:%d]",task.terminationStatus ]; } else { strongSelf.runStatusTF.stringValue = @"Successfully connected to server"; } [strongSelf.runButton setEnabled:YES]; [strongSelf.runWithoutButton setEnabled:YES]; }]; } - (IBAction)cancelTask:(id)sender { if (_task && _task.isRunning) { [_task terminate]; } } @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxyExampe/Base.lproj/MainMenu.xib ================================================ Default Left to Right Right to Left Default Left to Right Right to Left ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxyExampe/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "mac", "size" : "16x16", "scale" : "1x" }, { "idiom" : "mac", "size" : "16x16", "scale" : "2x" }, { "idiom" : "mac", "size" : "32x32", "scale" : "1x" }, { "idiom" : "mac", "size" : "32x32", "scale" : "2x" }, { "idiom" : "mac", "size" : "128x128", "scale" : "1x" }, { "idiom" : "mac", "size" : "128x128", "scale" : "2x" }, { "idiom" : "mac", "size" : "256x256", "scale" : "1x" }, { "idiom" : "mac", "size" : "256x256", "scale" : "2x" }, { "idiom" : "mac", "size" : "512x512", "scale" : "1x" }, { "idiom" : "mac", "size" : "512x512", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxyExampe/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile CFBundleIdentifier com.eeaapps.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright Copyright © 2014 Eldon Ahrold. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass NSApplication ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxyExampe/main.m ================================================ // // main.m // AHProxyExampe // // Created by Eldon on 11/11/14. // Copyright (c) 2014 Eldon Ahrold. All rights reserved. // #import int main(int argc, const char * argv[]) { return NSApplicationMain(argc, argv); } ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings/AHProxy.h ================================================ // AHProxy.h // // Copyright (c) 2014 Eldon Ahrold // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import /** * Proxy Type */ typedef NS_ENUM(NSInteger, AHProxyType) { /** * Undefined type */ kAHProxyTypeUnknown, /** * HTTP type */ kAHProxyTypeHTTP, /** * HTTPS type */ kAHProxyTypeHTTPS, /** * FTP type */ kAHProxyTypeFTP, /** * SOCKS type */ kAHProxyTypeSOCKS, }; /** * Proxy Object */ @interface AHProxy : NSObject /** * Proxy Server URL */ @property (copy, nonatomic, readonly) NSString *server; /** * Proxy Server Port */ @property (copy, nonatomic, readonly) NSNumber *port; /** * Proxy Server User */ @property (copy, nonatomic, readonly) NSString *user; /** * Proxy Server Password */ @property (copy, nonatomic, readonly) NSString *password; /** * Type of Proxy Server */ @property (assign, nonatomic, readonly) AHProxyType type; /** * export string appropriate for shell (url encoded) */ @property (copy, nonatomic, readonly) NSString *exportString; @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings/AHProxy.m ================================================ // AHProxy.m // // Copyright (c) 2014 Eldon Ahrold // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AHProxy.h" #import @interface AHProxy () @property (copy, nonatomic, readwrite) NSString *server; @property (copy, nonatomic, readwrite) NSNumber *port; @property (copy, nonatomic, readwrite) NSString *user; @property (copy, nonatomic, readwrite) NSString *password; @property (assign, nonatomic, readwrite) AHProxyType type; @end NSString *urlEncodedString(NSString *string) { NSString *result = CFBridgingRelease( CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, CFSTR(":/?#[]@$&’+,;="), kCFStringEncodingUTF8)); return result; } @implementation AHProxy - (NSString *)description { return self.exportString; } - (NSString *)password { if (!_password && (_server && _user)) { CFTypeRef results = NULL; NSData *passwordData = nil; NSDictionary *query = @{ (__bridge id)kSecClass : (__bridge id)kSecClassInternetPassword, (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne, (__bridge id)kSecAttrService : _server, (__bridge id)kSecAttrAccount : _user, (__bridge id)kSecReturnData : @YES, }; if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &results) == errSecSuccess) { passwordData = CFBridgingRelease(results); if (passwordData) { _password = [[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding]; } } } return _password; } - (NSString *)exportString { NSString *exportString = nil; if (_server) { NSMutableString *workingExportString = [[NSMutableString alloc] initWithString:[self prefixForType]]; if (_user && self.password) { [workingExportString appendFormat:@"%@:%@@", _user, urlEncodedString(_password)]; } [workingExportString appendString:_server]; if (_port) { [workingExportString appendFormat:@":%@", _port]; } exportString = [NSString stringWithString:workingExportString]; } return exportString; } - (NSString *)prefixForType { NSString *prefix; switch (_type) { case kAHProxyTypeFTP: case kAHProxyTypeHTTP: prefix = @"http://"; break; case kAHProxyTypeHTTPS: prefix = @"https://"; break; case kAHProxyTypeSOCKS: prefix = @"socks://"; break; default: prefix = @""; break; } return prefix; } @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings/AHProxySettings.h ================================================ // AHProxySettings.h // // Copyright (c) 2014 Eldon Ahrold // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import "AHProxy.h" /** * String for key representing export HTTP_PROXY for NSTasks */ extern NSString *const kAHProxyExportHTTP; /** * String for key representing export HTTPS_PROXY for NSTasks */ extern NSString *const kAHProxyExportHTTPS; /** * String for key representing export FTP_PROXY for NSTasks */ extern NSString *const kAHProxyExportFTP; /** * String for key representing export ALL_PROXY which is used for socks exports * for NSTasks */ extern NSString *const kAHProxyExportSOCKS; /** * String for key representing export NO_PROXY for NSTasks */ extern NSString *const kAHProxyExportExceptions; /** * Access information about the current system proxy configuration */ @interface AHProxySettings : NSObject /** * AHProxy object representing the System Web Proxy (HTTP) */ @property (copy, nonatomic, readonly) AHProxy *HTTPProxy; /** * AHProxy object representing the System Secure Web Proxy (HTTPS) */ @property (copy, nonatomic, readonly) AHProxy *HTTPSProxy; /** * AHProxy object representing the System FTP Proxy */ @property (copy, nonatomic, readonly) AHProxy *FTPProxy; /** * AHProxy object representing the System SOCKS Proxy (HTTP) */ @property (copy, nonatomic, readonly) AHProxy *SOCKSProxy; /** * Array of automatically discovered AHProxy objects from PAC / WPAD */ @property (copy, nonatomic, readonly) NSArray *autoDetectedProxies; /** * List of bypassed hosts and domains used by the system */ @property (copy, nonatomic, readonly) NSArray *exceptionsList; /** * URL used during the execution a PAC script to determine the appropriate * proxies */ @property (copy, nonatomic) NSString *destinationURL; /** * Return a dictionary suitable for NSTask's proxies environment */ @property (copy, nonatomic) NSDictionary *taskDictionary; /** * Whether to use auto-detected proxies as failover for HTTP, HTTPS, and FTP * @note Defaults to true */ @property (nonatomic, assign) BOOL useAutoDetectAsFailover; /** * Initialize the * * @param destURL URL used during the execution a PAC script to determine the * appropriate proxies * * @return initialized AHProxySettings */ - (instancetype)initWithDestination:(NSString *)destURL; @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings/AHProxySettings.m ================================================ // AHProxySettings.m // // Copyright (c) 2014 Eldon Ahrold // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "AHProxySettings.h" #import NSString *const kAHProxyExportHTTP = @"HTTP_PROXY"; NSString *const kAHProxyExportHTTPS = @"HTTPS_PROXY"; NSString *const kAHProxyExportFTP = @"FTP_PROXY"; NSString *const kAHProxyExportExceptions = @"NO_PROXY"; NSString *const kAHProxyExportSOCKS = @"ALL_PROXY"; NSString *const kNSProxyHTTP; NSString *const kNSProxyHTTPS; @interface AHProxy () @property (copy, nonatomic, readwrite) NSString *server; @property (copy, nonatomic, readwrite) NSNumber *port; @property (copy, nonatomic, readwrite) NSString *user; @property (assign, nonatomic, readwrite) AHProxyType type; @end @implementation AHProxySettings { NSDictionary *_systemProxies; NSString *_pacFunction; BOOL _pacFileRetrievalFailed; } @synthesize autoDetectedProxies = _autoDetectedProxies; @synthesize exceptionsList = _exceptionsList; - (id)init { self = [super init]; if (self) { _systemProxies = CFBridgingRelease(SCDynamicStoreCopyProxies(NULL)); _useAutoDetectAsFailover = YES; } return self; } - (instancetype)initWithDestination:(NSString *)destURL { self = [self init]; if (self) { _destinationURL = destURL; } return self; } - (AHProxy *)HTTPProxy { return [self proxyForType:@"HTTP"]; } - (AHProxy *)HTTPSProxy { return [self proxyForType:@"HTTPS"]; } - (AHProxy *)FTPProxy { return [self proxyForType:@"FTP"]; } - (AHProxy *)SOCKSProxy { return [self proxyForType:@"SOCKS"]; } - (NSArray *)autoDetectedProxies { if (_systemProxies[@"ProxyAutoConfigEnable"] || _systemProxies[@"ProxyAutoDiscoveryEnable"]) { if (!_pacFunction && !_pacFileRetrievalFailed) { NSString *urlString = _systemProxies[@"ProxyAutoConfigURLString"]; if (urlString && urlString.length > 0) { NSURL *pacFileURL = [NSURL URLWithString:urlString]; NSURLRequest *req = [NSURLRequest requestWithURL:pacFileURL cachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0]; // Initialize our response and error objects NSHTTPURLResponse *resp; NSError *error = nil; // Get the PAC file data NSData *reqData = [NSURLConnection sendSynchronousRequest:req returningResponse:&resp error:&error]; if (reqData && (![resp respondsToSelector:@selector(statusCode)] || resp.statusCode < 400)) { _pacFunction = [[NSString alloc] initWithData:reqData encoding:NSASCIIStringEncoding]; } else { _pacFileRetrievalFailed = YES; NSLog(@"Error retrieving PAC file. %@",error ? error.localizedDescription:@""); } } } if (_pacFunction && self.destinationURL) { CFErrorRef err; NSURL *url = [NSURL URLWithString:_destinationURL]; NSArray *proxies = CFBridgingRelease( CFNetworkCopyProxiesForAutoConfigurationScript( (__bridge CFStringRef)(_pacFunction), (__bridge CFURLRef)(url), &err)); if (err) { NSLog(@"%@", CFBridgingRelease(err)); } else { NSMutableArray *workingProxies = [NSMutableArray new]; for (NSDictionary *proxyDict in proxies) { AHProxy *proxy = [[AHProxy alloc] init]; proxy.server = proxyDict[@"kCFProxyHostNameKey"]; proxy.port = proxyDict[@"kCFProxyPortNumberKey"]; if (proxy.server && proxy.port) { [workingProxies addObject:proxy]; } } if (workingProxies.count) { _autoDetectedProxies = [NSArray arrayWithArray:workingProxies]; } } } } return _autoDetectedProxies; } - (NSArray *)exceptionsList { _exceptionsList = _systemProxies[@"ExceptionsList"]; return _exceptionsList; } - (NSString *)destinationURL { if (!_destinationURL) { _destinationURL = @"http://0.0.0.0"; } return _destinationURL; } - (NSDictionary *)taskDictionary { NSMutableDictionary *exportDictionary = [[NSMutableDictionary alloc] initWithCapacity:5]; AHProxy *httpProxy = self.HTTPProxy; if (httpProxy) { NSString *proxy = httpProxy.exportString; if (proxy) { [exportDictionary setObject:proxy forKey:kAHProxyExportHTTP]; [exportDictionary setObject:proxy forKey:[kAHProxyExportHTTP lowercaseString]]; } } AHProxy *httpsProxy = self.HTTPSProxy; if (httpsProxy) { NSString *proxy = httpsProxy.exportString; if (proxy) { [exportDictionary setObject:proxy forKey:kAHProxyExportHTTPS]; [exportDictionary setObject:proxy forKey:[kAHProxyExportHTTPS lowercaseString]]; } } AHProxy *socksProxy = self.SOCKSProxy; if (socksProxy) { NSString *proxy = socksProxy.exportString; if (proxy) { [exportDictionary setObject:proxy forKey:kAHProxyExportSOCKS]; [exportDictionary setObject:proxy forKey:[kAHProxyExportSOCKS lowercaseString]]; } } NSArray *autoDetectedProxies = self.autoDetectedProxies; if (autoDetectedProxies) { NSString *proxy = [[autoDetectedProxies firstObject] exportString]; if (!httpProxy) { [exportDictionary setObject:proxy forKey:kAHProxyExportHTTP]; [exportDictionary setObject:proxy forKey:[kAHProxyExportHTTP lowercaseString]]; } if (!httpsProxy) { [exportDictionary setObject:proxy forKey:kAHProxyExportHTTPS]; [exportDictionary setObject:proxy forKey:[kAHProxyExportHTTPS lowercaseString]]; } } // If there are not keys added to the dictionary yet, skip the exception // list if ((exportDictionary.count > 0) && self.exceptionsList) { NSMutableArray *exceptionList = [[NSMutableArray alloc] init]; // The NO_PROXY key is formatted differently than set in system // you can't start with wild cards, instead you specify // domain extensions directly (e.g. *.mit.edu is just .mit.edu) for (NSString *exception in _exceptionsList) { if (exception.length > 2 && [[exception substringToIndex:2] isEqualToString:@"*."]) { [exceptionList addObject:[exception substringFromIndex:1]]; } else { [exceptionList addObject:exception]; } } NSString *exceptions = [exceptionList componentsJoinedByString:@","]; [exportDictionary setObject:exceptions forKey:kAHProxyExportExceptions]; } return exportDictionary.count ? [NSDictionary dictionaryWithDictionary:exportDictionary] : nil; } #pragma mark - Private - (AHProxy *)proxyForType:(NSString *)type { AHProxy *proxy = nil; NSString *typeEnabled = [type stringByAppendingString:@"Enable"]; if (_systemProxies[typeEnabled]) { NSString *proxyServer = [type stringByAppendingString:@"Proxy"]; // If there is a value for the key init an AHProxy object. if (_systemProxies[proxyServer]) { proxy = [[AHProxy alloc] init]; proxy.server = _systemProxies[proxyServer]; // Set the port. NSString *proxyPort = [type stringByAppendingString:@"Port"]; proxy.port = _systemProxies[proxyPort]; // Set the user. NSString *proxyUser = [type stringByAppendingString:@"User"]; proxy.user = _systemProxies[proxyUser]; // Set the type. if ([type isEqualToString:@"HTTP"]) { proxy.type = kAHProxyTypeHTTP; } else if ([type isEqualToString:@"HTTPS"]) { proxy.type = kAHProxyTypeHTTPS; } else if ([type isEqualToString:@"FTP"]) { proxy.type = kAHProxyTypeFTP; } else if ([type isEqualToString:@"SOCKS"]) { proxy.type = kAHProxyTypeSOCKS; } } } // unless disabled try to if (!proxy && _useAutoDetectAsFailover) { if ([type isEqualToString:@"HTTP"] || [type isEqualToString:@"HTTPS"] || [type isEqualToString:@"FTP"]) { proxy = [self.autoDetectedProxies firstObject]; } } return proxy; } @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings/NSTask+useSystemProxies.h ================================================ // // NSTask+useSystemProxies.h // AHProxySettings // // Copyright (c) 2014 Eldon Ahrold // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import /** * Set the task to use system proxies */ @interface NSTask (useSystemProxies) /** * Use system proxies */ - (void)useSystemProxies; /** * Dynamically determine proxies based on supplied url * * @param URLString destination url to check against PAC / WPAD file */ - (void)useSystemProxiesForDestination:(NSString *)URLString; @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings/NSTask+useSystemProxies.m ================================================ // // NSTask+useSystemProxies.m // AHProxySettings // // Copyright (c) 2014 Eldon Ahrold // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "NSTask+useSystemProxies.h" #import "AHProxySettings.h" @implementation NSTask (useSystemProxies) - (void)useSystemProxies { [self useSystemProxiesForDestination:nil]; } - (void)useSystemProxiesForDestination:(NSString *)URLString { AHProxySettings *settings; if (URLString) { settings = [[AHProxySettings alloc] initWithDestination:URLString]; } else { settings = [[AHProxySettings alloc] init]; } if (settings.taskDictionary) { NSMutableDictionary *environment = [[NSMutableDictionary alloc]init]; if (!self.environment) { [environment addEntriesFromDictionary:[[NSProcessInfo processInfo] environment]]; } else { [environment addEntriesFromDictionary:self.environment]; } [environment addEntriesFromDictionary:settings.taskDictionary]; self.environment = [NSDictionary dictionaryWithDictionary:environment]; } } @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings.podspec ================================================ Pod::Spec.new do |spec| spec.name = 'AHProxySettings' spec.version = '0.1.1' spec.platform = :osx spec.license = 'MIT' spec.summary = 'Objective-c lib to easily acquire the current system proxy settings' spec.homepage = 'https://github.com/eahrold/AHProxySettings' spec.authors = { 'Eldon Ahrold' => 'eldon.ahrold@gmail.com' } spec.source = { :git => 'https://github.com/eahrold/AHProxySettings.git', :tag => "v#{spec.version}" } spec.requires_arc = true spec.frameworks = 'Foundation','SystemConfiguration','Security' spec.public_header_files = 'AHProxySettings/*.h' spec.source_files = 'AHProxySettings/*.{h,m}' end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ BE013D281A10693600C29B2E /* AHProxySettingsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = BE013D271A10693600C29B2E /* AHProxySettingsTest.m */; }; BE013D2C1A10696C00C29B2E /* libAHProxySettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BECB2D6C1A0E699400AFA935 /* libAHProxySettings.a */; }; BE013D2E1A1069F800C29B2E /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE013D2D1A1069F800C29B2E /* SystemConfiguration.framework */; }; BE013D301A1069FE00C29B2E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE013D2F1A1069FE00C29B2E /* Security.framework */; }; BE013D461A11435700C29B2E /* AHProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = BE013D441A11435700C29B2E /* AHProxy.h */; }; BE013D471A11435700C29B2E /* AHProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = BE013D451A11435700C29B2E /* AHProxy.m */; }; BE582A611A14741E007A1C11 /* NSTask+useSystemProxies.h in Headers */ = {isa = PBXBuildFile; fileRef = BE582A5F1A14741E007A1C11 /* NSTask+useSystemProxies.h */; }; BE582A621A14741E007A1C11 /* NSTask+useSystemProxies.m in Sources */ = {isa = PBXBuildFile; fileRef = BE582A601A14741E007A1C11 /* NSTask+useSystemProxies.m */; }; BECB2D701A0E699400AFA935 /* AHProxySettings.h in Headers */ = {isa = PBXBuildFile; fileRef = BECB2D6F1A0E699400AFA935 /* AHProxySettings.h */; }; BECB2D721A0E699400AFA935 /* AHProxySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = BECB2D711A0E699400AFA935 /* AHProxySettings.m */; }; BED077D81A1313E3009690DD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BED077D71A1313E3009690DD /* main.m */; }; BED077DB1A1313E3009690DD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BED077DA1A1313E3009690DD /* AppDelegate.m */; }; BED077DD1A1313E3009690DD /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BED077DC1A1313E3009690DD /* Images.xcassets */; }; BED077E01A1313E3009690DD /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BED077DE1A1313E3009690DD /* MainMenu.xib */; }; BED077F51A131708009690DD /* libAHProxySettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BECB2D6C1A0E699400AFA935 /* libAHProxySettings.a */; }; BED077F71A13176E009690DD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE013D2F1A1069FE00C29B2E /* Security.framework */; }; BED077F81A13177A009690DD /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE013D2D1A1069F800C29B2E /* SystemConfiguration.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ BE013D2A1A10696700C29B2E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BECB2D641A0E699400AFA935 /* Project object */; proxyType = 1; remoteGlobalIDString = BECB2D6B1A0E699400AFA935; remoteInfo = AHProxySettings; }; BED077F31A1316FF009690DD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BECB2D641A0E699400AFA935 /* Project object */; proxyType = 1; remoteGlobalIDString = BECB2D6B1A0E699400AFA935; remoteInfo = AHProxySettings; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ BE013D271A10693600C29B2E /* AHProxySettingsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AHProxySettingsTest.m; sourceTree = ""; }; BE013D2D1A1069F800C29B2E /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; BE013D2F1A1069FE00C29B2E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; BE013D441A11435700C29B2E /* AHProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AHProxy.h; sourceTree = ""; }; BE013D451A11435700C29B2E /* AHProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AHProxy.m; sourceTree = ""; }; BE582A5F1A14741E007A1C11 /* NSTask+useSystemProxies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTask+useSystemProxies.h"; sourceTree = ""; }; BE582A601A14741E007A1C11 /* NSTask+useSystemProxies.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTask+useSystemProxies.m"; sourceTree = ""; }; BECB2D6C1A0E699400AFA935 /* libAHProxySettings.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAHProxySettings.a; sourceTree = BUILT_PRODUCTS_DIR; }; BECB2D6F1A0E699400AFA935 /* AHProxySettings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AHProxySettings.h; sourceTree = ""; }; BECB2D711A0E699400AFA935 /* AHProxySettings.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AHProxySettings.m; sourceTree = ""; }; BECB2D771A0E699400AFA935 /* AHProxySettingsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AHProxySettingsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; BECB2D7A1A0E699400AFA935 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BED077D31A1313E3009690DD /* AHProxyExampe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AHProxyExampe.app; sourceTree = BUILT_PRODUCTS_DIR; }; BED077D61A1313E3009690DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BED077D71A1313E3009690DD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; BED077D91A1313E3009690DD /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; BED077DA1A1313E3009690DD /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; BED077DC1A1313E3009690DD /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; BED077DF1A1313E3009690DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ BECB2D691A0E699400AFA935 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; BECB2D741A0E699400AFA935 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BE013D301A1069FE00C29B2E /* Security.framework in Frameworks */, BE013D2E1A1069F800C29B2E /* SystemConfiguration.framework in Frameworks */, BE013D2C1A10696C00C29B2E /* libAHProxySettings.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; BED077D01A1313E3009690DD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( BED077F51A131708009690DD /* libAHProxySettings.a in Frameworks */, BED077F71A13176E009690DD /* Security.framework in Frameworks */, BED077F81A13177A009690DD /* SystemConfiguration.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ BECB2D631A0E699400AFA935 = { isa = PBXGroup; children = ( BECB2D6E1A0E699400AFA935 /* AHProxySettings */, BECB2D781A0E699400AFA935 /* AHProxySettingsTests */, BED077D41A1313E3009690DD /* AHProxyExampe */, BED077F61A131759009690DD /* Frameworks */, BECB2D6D1A0E699400AFA935 /* Products */, ); sourceTree = ""; }; BECB2D6D1A0E699400AFA935 /* Products */ = { isa = PBXGroup; children = ( BECB2D6C1A0E699400AFA935 /* libAHProxySettings.a */, BECB2D771A0E699400AFA935 /* AHProxySettingsTests.xctest */, BED077D31A1313E3009690DD /* AHProxyExampe.app */, ); name = Products; sourceTree = ""; }; BECB2D6E1A0E699400AFA935 /* AHProxySettings */ = { isa = PBXGroup; children = ( BECB2D6F1A0E699400AFA935 /* AHProxySettings.h */, BECB2D711A0E699400AFA935 /* AHProxySettings.m */, BE013D441A11435700C29B2E /* AHProxy.h */, BE013D451A11435700C29B2E /* AHProxy.m */, BE582A5F1A14741E007A1C11 /* NSTask+useSystemProxies.h */, BE582A601A14741E007A1C11 /* NSTask+useSystemProxies.m */, ); path = AHProxySettings; sourceTree = ""; }; BECB2D781A0E699400AFA935 /* AHProxySettingsTests */ = { isa = PBXGroup; children = ( BE013D271A10693600C29B2E /* AHProxySettingsTest.m */, BECB2D791A0E699400AFA935 /* Supporting Files */, ); path = AHProxySettingsTests; sourceTree = ""; }; BECB2D791A0E699400AFA935 /* Supporting Files */ = { isa = PBXGroup; children = ( BECB2D7A1A0E699400AFA935 /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; BED077D41A1313E3009690DD /* AHProxyExampe */ = { isa = PBXGroup; children = ( BED077D91A1313E3009690DD /* AppDelegate.h */, BED077DA1A1313E3009690DD /* AppDelegate.m */, BED077DC1A1313E3009690DD /* Images.xcassets */, BED077DE1A1313E3009690DD /* MainMenu.xib */, BED077D51A1313E3009690DD /* Supporting Files */, ); path = AHProxyExampe; sourceTree = ""; }; BED077D51A1313E3009690DD /* Supporting Files */ = { isa = PBXGroup; children = ( BED077D61A1313E3009690DD /* Info.plist */, BED077D71A1313E3009690DD /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; BED077F61A131759009690DD /* Frameworks */ = { isa = PBXGroup; children = ( BE013D2F1A1069FE00C29B2E /* Security.framework */, BE013D2D1A1069F800C29B2E /* SystemConfiguration.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ BECB2D6A1A0E699400AFA935 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( BE013D461A11435700C29B2E /* AHProxy.h in Headers */, BE582A611A14741E007A1C11 /* NSTask+useSystemProxies.h in Headers */, BECB2D701A0E699400AFA935 /* AHProxySettings.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ BECB2D6B1A0E699400AFA935 /* AHProxySettings */ = { isa = PBXNativeTarget; buildConfigurationList = BECB2D7D1A0E699400AFA935 /* Build configuration list for PBXNativeTarget "AHProxySettings" */; buildPhases = ( BECB2D681A0E699400AFA935 /* Sources */, BECB2D691A0E699400AFA935 /* Frameworks */, BECB2D6A1A0E699400AFA935 /* Headers */, ); buildRules = ( ); dependencies = ( ); name = AHProxySettings; productName = AHProxySettingsShellExport; productReference = BECB2D6C1A0E699400AFA935 /* libAHProxySettings.a */; productType = "com.apple.product-type.library.static"; }; BECB2D761A0E699400AFA935 /* AHProxySettingsTests */ = { isa = PBXNativeTarget; buildConfigurationList = BECB2D801A0E699400AFA935 /* Build configuration list for PBXNativeTarget "AHProxySettingsTests" */; buildPhases = ( BECB2D731A0E699400AFA935 /* Sources */, BECB2D741A0E699400AFA935 /* Frameworks */, BECB2D751A0E699400AFA935 /* Resources */, ); buildRules = ( ); dependencies = ( BE013D2B1A10696700C29B2E /* PBXTargetDependency */, ); name = AHProxySettingsTests; productName = AHProxySettingsShellExportTests; productReference = BECB2D771A0E699400AFA935 /* AHProxySettingsTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; BED077D21A1313E3009690DD /* AHProxyExampe */ = { isa = PBXNativeTarget; buildConfigurationList = BED077F11A1313E3009690DD /* Build configuration list for PBXNativeTarget "AHProxyExampe" */; buildPhases = ( BED077CF1A1313E3009690DD /* Sources */, BED077D01A1313E3009690DD /* Frameworks */, BED077D11A1313E3009690DD /* Resources */, ); buildRules = ( ); dependencies = ( BED077F41A1316FF009690DD /* PBXTargetDependency */, ); name = AHProxyExampe; productName = AHProxyExampe; productReference = BED077D31A1313E3009690DD /* AHProxyExampe.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ BECB2D641A0E699400AFA935 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0600; ORGANIZATIONNAME = "Eldon Ahrold"; TargetAttributes = { BECB2D6B1A0E699400AFA935 = { CreatedOnToolsVersion = 6.0.1; }; BECB2D761A0E699400AFA935 = { CreatedOnToolsVersion = 6.0.1; }; BED077D21A1313E3009690DD = { CreatedOnToolsVersion = 6.0.1; }; }; }; buildConfigurationList = BECB2D671A0E699400AFA935 /* Build configuration list for PBXProject "AHProxySettings" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = BECB2D631A0E699400AFA935; productRefGroup = BECB2D6D1A0E699400AFA935 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( BECB2D6B1A0E699400AFA935 /* AHProxySettings */, BECB2D761A0E699400AFA935 /* AHProxySettingsTests */, BED077D21A1313E3009690DD /* AHProxyExampe */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ BECB2D751A0E699400AFA935 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; BED077D11A1313E3009690DD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( BED077DD1A1313E3009690DD /* Images.xcassets in Resources */, BED077E01A1313E3009690DD /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ BECB2D681A0E699400AFA935 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BECB2D721A0E699400AFA935 /* AHProxySettings.m in Sources */, BE013D471A11435700C29B2E /* AHProxy.m in Sources */, BE582A621A14741E007A1C11 /* NSTask+useSystemProxies.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BECB2D731A0E699400AFA935 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BE013D281A10693600C29B2E /* AHProxySettingsTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; BED077CF1A1313E3009690DD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( BED077DB1A1313E3009690DD /* AppDelegate.m in Sources */, BED077D81A1313E3009690DD /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ BE013D2B1A10696700C29B2E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BECB2D6B1A0E699400AFA935 /* AHProxySettings */; targetProxy = BE013D2A1A10696700C29B2E /* PBXContainerItemProxy */; }; BED077F41A1316FF009690DD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BECB2D6B1A0E699400AFA935 /* AHProxySettings */; targetProxy = BED077F31A1316FF009690DD /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ BED077DE1A1313E3009690DD /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( BED077DF1A1313E3009690DD /* Base */, ); name = MainMenu.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ BECB2D7B1A0E699400AFA935 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; BECB2D7C1A0E699400AFA935 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; name = Release; }; BECB2D7E1A0E699400AFA935 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { EXECUTABLE_PREFIX = lib; OTHER_LDFLAGS = ""; PRODUCT_NAME = AHProxySettings; }; name = Debug; }; BECB2D7F1A0E699400AFA935 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { EXECUTABLE_PREFIX = lib; OTHER_LDFLAGS = ""; PRODUCT_NAME = AHProxySettings; }; name = Release; }; BECB2D811A0E699400AFA935 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = AHProxySettingsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = AHProxySettingsTests; }; name = Debug; }; BECB2D821A0E699400AFA935 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); INFOPLIST_FILE = AHProxySettingsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = AHProxySettingsTests; }; name = Release; }; BED077ED1A1313E3009690DD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = AHProxyExampe/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; BED077EE1A1313E3009690DD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = AHProxyExampe/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ BECB2D671A0E699400AFA935 /* Build configuration list for PBXProject "AHProxySettings" */ = { isa = XCConfigurationList; buildConfigurations = ( BECB2D7B1A0E699400AFA935 /* Debug */, BECB2D7C1A0E699400AFA935 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BECB2D7D1A0E699400AFA935 /* Build configuration list for PBXNativeTarget "AHProxySettings" */ = { isa = XCConfigurationList; buildConfigurations = ( BECB2D7E1A0E699400AFA935 /* Debug */, BECB2D7F1A0E699400AFA935 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BECB2D801A0E699400AFA935 /* Build configuration list for PBXNativeTarget "AHProxySettingsTests" */ = { isa = XCConfigurationList; buildConfigurations = ( BECB2D811A0E699400AFA935 /* Debug */, BECB2D821A0E699400AFA935 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; BED077F11A1313E3009690DD /* Build configuration list for PBXNativeTarget "AHProxyExampe" */ = { isa = XCConfigurationList; buildConfigurations = ( BED077ED1A1313E3009690DD /* Debug */, BED077EE1A1313E3009690DD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = BECB2D641A0E699400AFA935 /* Project object */; } ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettings.xcodeproj/xcshareddata/xcschemes/AHProxySettings.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettingsTests/AHProxySettingsTest.m ================================================ // // AHProxySettingsTest.m // AHProxySettings // // Created by Eldon on 11/9/14. // Copyright (c) 2014 Eldon Ahrold. All rights reserved. // #import #import #import "AHProxySettings.h" @interface AHProxySettingsTest : XCTestCase @end @implementation AHProxySettingsTest - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testAll { // When running this test enable Auto Detect, HTTP, HTTPS, SOCKS, FTP AHProxySettings *settings = [[AHProxySettings alloc] initWithDestination:@"https://github.com"]; XCTAssertNotNil(settings.HTTPProxy ,@"No HTTP proxy returned"); NSLog(@"%@", settings.HTTPProxy); XCTAssertNotNil(settings.HTTPSProxy ,@"No HTTPS proxy returned"); NSLog(@"%@",settings.HTTPSProxy); XCTAssertNotNil(settings.SOCKSProxy ,@"No SOCKS proxy returned"); NSLog(@"%@",settings.SOCKSProxy); XCTAssertNotNil(settings.FTPProxy ,@"No FTP proxy returned"); NSLog(@"%@",settings.FTPProxy); XCTAssertNotNil(settings.autoDetectedProxies ,@"No Auto Detected Proxies returned"); for (AHProxy *p in settings.autoDetectedProxies) { NSLog(@"%@",p); } } - (void)testAutoDetect { // When running this test only have Auto Proxy Discovery Enabled AHProxySettings *settings = [[AHProxySettings alloc] initWithDestination:@"https://github.com"]; NSLog(@"Proxy for localhost%@",settings.taskDictionary); XCTAssertNotNil(settings.autoDetectedProxies ,@"No proxy returned"); XCTAssertNotNil(settings.HTTPProxy,@"A proxy returned, but should not have"); settings.useAutoDetectAsFailover = NO; XCTAssertNil(settings.HTTPProxy,@"A proxy returned, but should not have"); settings.destinationURL = @"http://localhost"; XCTAssertNil(settings.autoDetectedProxies,@"A proxy returned, but should not have"); XCTAssertNil(settings.HTTPProxy,@"A proxy returned, but should not have"); } - (void)testTaskDictOn { // Have some proxies enabled when running this test AHProxySettings *settings = [[AHProxySettings alloc] init]; XCTAssertNotNil(settings.taskDictionary,@"Couldn't determine task dictionary"); } - (void)testTaskDict { // Disable all proxies before running this test AHProxySettings *settings = [[AHProxySettings alloc] init]; NSDictionary *dict = settings.taskDictionary; XCTAssertNil(dict,@"There should be not task dictionary: %@",dict); } - (void)testPerformanceExample { // This is an example of a performance test case. [self measureBlock:^{ // Put the code you want to measure the time of here. [self testAll]; }]; } @end ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/AHProxySettingsTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier com.eeaapps.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/LICENSE ================================================ // Copyright (c) 2014 Eldon Ahrold ( https://github.com/eahrold/AHLaunchCtl ) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. ================================================ FILE: archive/bitbar/App/Vendor/AHProxySettings/README.md ================================================ #AHProxySettings ### Simple lib to access the OSX system proxy settings ```Objective-c AHProxySettings *settings = [[AHProxySettings alloc] init]; NSLog(@"%@",settings.HTTPProxy.exportString); NSLog(@"%@",settings.HTTPSProxy.exportString); NSLog(@"%@",settings.FTProxy.exportString); NSLog(@"%@",settings.SOCKSProxy.exportString); NSLog(@"%@",settings.autoDetectedProxies); // By default HTTP(S) and FTP proxy objects will be // populated with a proxy returned from a .PAC / WPAD request. // To disable this behavior settings.useAutoDetectAsFailover = NO; ``` But the reason this project was started was to get the system proxy values into an NSTask, so there's a category for that to. ``` #import "NSTask+useSystemProxies.h" NSTask *task = [NSTask alloc] init]; task.launchPath = @"/usr/bin/curl" task.arguments = @[ @"-k", _testURL.stringValue ]; [task useSystemProxies]; // This is equivalent to // export HTTP_PROXY = ... // export HTTPS_PROXY = ... // export FTP_PROXY = ... // export NO_PROXY = ... // based on your system settings // or to dynamically determine if a proxy // is needed based on a PAC file [task useSystemProxiesForDestination:@"github.com"]; [task launch]; // ... then do what you will with the results ... // ``` if not using CocoaPods, make sure to include `-ObjC` in you build setting's other linker flags so the category isn't optimized out. ================================================ FILE: archive/bitbar/App/Vendor/DateTools/.gitignore ================================================ # Xcode .DS_Store */build/* *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata profile *.moved-aside DerivedData .idea/ *.hmap *.xccheckout #CocoaPods Pods ================================================ FILE: archive/bitbar/App/Vendor/DateTools/.travis.yml ================================================ language: objective-c before_script: - gem install xcpretty script: - xcodebuild -project Tests/DateToolsTests/DateToolsTests.xcodeproj -scheme DateToolsTests -sdk iphonesimulator test | xcpretty -c ================================================ FILE: archive/bitbar/App/Vendor/DateTools/CREDITS.md ================================================ Credits ======= Portions from NSDate+TimeAgo Originally based on code Christopher Pickslay posted to Forrst. Used with permission. http://twitter.com/cpickslay Ramon Torres began support for internationalization/localization. Added es strings. http://rtorres.me/ Dennis Zhuang added zh_Hans Chinese Simplified strings. http://fnil.net/ Mozart Petter added pt_BR Brazilian Portuguese strings. http://www.mozartpetter.com/ Stéphane Gerardot added fr French strings. Marco Sanson added it Italian strings. http://marcosanson.tumblr.com/ Almas Adilbek added ru Russian strings. Extended logic to support Russian idioms. http://mixdesign.kz/ Mallox51 added de German strings. https://github.com/Mallox51 Tieme van Veen added nl Dutch strings. http://www.tiemevanveen.nl Árpád Goretity added hu Hungarian strings. http://apaczai.elte.hu/~13akga/ Anajavi added fi Finnish strings. https://github.com/anajavi Tonydyb added ja Japanese strings. Vinhnx added vi Vietnamese strings. http://vinhnx.github.io/ Ronail added zh_Hant Traditional Chinese strings. https://github.com/ronail SorinAntohi added ro Romanian strings. https://github.com/SorinAntohi spookd added da Danish strings. https://github.com/spookd Barrett Jacobsen added cs Czech strings. https://github.com/barrettj Dmitry Shmidt added nb Norwegian strings. https://github.com/shmidt Martins Rudens added lv Latvian strings. https://github.com/rudensm Osman Saral added tr Turkish strings. https://github.com/osrl analogstyle added ko Korean strings. http://almacreative.net/ Flavio Caetano fixed pt Portuguese strings. http://flaviocaetano.com kolarski added bg Bulgarian strings. http://github.com/kolarski Vladimir Kofman added he Hebrew strings. https://github.com/vladimirkofman Viraf Sarkari added ar Arabic, gre Greek, pl Polish, sv Swedish, and th Thai strings. https://github.com/viraf Vasyl Skrypii added uk Ukranian strings. https://github.com/medlay Maggi Trymbill added is Icelandic strings. https://github.com/grundvollur Ikhsan Assaat added id Indonesian strings. http://ikhsan.me Marc added ca Catalan strings. http://marcboquet.com/ Steffan Harries added cy Welsh strings. https://github.com/Bendihossan mjanda added es and cs short format strings https://github.com/mjanda Niklas Fahl added the de short format strings https://github.com/fahlout Vlad Cacuic added the ro short format strings https://github.com/vrcaciuc frin added sl Slovenian strings. http://github.com/frin Nikhil Nigade added hi (Hindi) and gu (Gujarati) strings. https://github.com/dezinezync Faiz Mokhtar added ms Malay strings. https://github.com/faizmokhtar ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTConstants.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import FOUNDATION_EXPORT const long long SECONDS_IN_YEAR; FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_28; FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_29; FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_30; FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_31; FOUNDATION_EXPORT const NSInteger SECONDS_IN_WEEK; FOUNDATION_EXPORT const NSInteger SECONDS_IN_DAY; FOUNDATION_EXPORT const NSInteger SECONDS_IN_HOUR; FOUNDATION_EXPORT const NSInteger SECONDS_IN_MINUTE; FOUNDATION_EXPORT const NSInteger MILLISECONDS_IN_DAY; #import "DTError.h" ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTConstants.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTConstants.h" const long long SECONDS_IN_YEAR = 31556900; const NSInteger SECONDS_IN_MONTH_28 = 2419200; const NSInteger SECONDS_IN_MONTH_29 = 2505600; const NSInteger SECONDS_IN_MONTH_30 = 2592000; const NSInteger SECONDS_IN_MONTH_31 = 2678400; const NSInteger SECONDS_IN_WEEK = 604800; const NSInteger SECONDS_IN_DAY = 86400; const NSInteger SECONDS_IN_HOUR = 3600; const NSInteger SECONDS_IN_MINUTE = 60; const NSInteger MILLISECONDS_IN_DAY = 86400000; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTError.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import #pragma mark - Domain extern NSString *const DTErrorDomain; #pragma mark - Status Codes static const NSUInteger DTInsertOutOfBoundsException = 0; static const NSUInteger DTRemoveOutOfBoundsException = 1; static const NSUInteger DTBadTypeException = 2; @interface DTError : NSObject +(void)throwInsertOutOfBoundsException:(NSInteger)index array:(NSArray *)array; +(void)throwRemoveOutOfBoundsException:(NSInteger)index array:(NSArray *)array; +(void)throwBadTypeException:(id)obj expectedClass:(Class)classType; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTError.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTError.h" #pragma mark - Domain NSString *const DTErrorDomain = @"com.mattyork.dateTools"; @implementation DTError +(void)throwInsertOutOfBoundsException:(NSInteger)index array:(NSArray *)array{ //Handle possible zero bounds NSInteger arrayUpperBound = (array.count == 0)? 0:array.count; //Create info for error NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to insert DTTimePeriod at index %ld but the group is of size [0...%ld].", (long)index, (long)arrayUpperBound],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try an index within the bounds or the group.", nil)}; //Handle Error NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTInsertOutOfBoundsException userInfo:userInfo]; [self printErrorWithCallStack:error]; } +(void)throwRemoveOutOfBoundsException:(NSInteger)index array:(NSArray *)array{ //Handle possible zero bounds NSInteger arrayUpperBound = (array.count == 0)? 0:array.count; //Create info for error NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to remove DTTimePeriod at index %ld but the group is of size [0...%ld].", (long)index, (long)arrayUpperBound],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try an index within the bounds of the group.", nil)}; //Handle Error NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTRemoveOutOfBoundsException userInfo:userInfo]; [self printErrorWithCallStack:error]; } +(void)throwBadTypeException:(id)obj expectedClass:(Class)classType{ //Create info for error NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to insert object of class %@ when expecting object of class %@.", NSStringFromClass([obj class]), NSStringFromClass(classType)],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try again by inserting a DTTimePeriod object.", nil)}; //Handle Error NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTBadTypeException userInfo:userInfo]; [self printErrorWithCallStack:error]; } +(void)printErrorWithCallStack:(NSError *)error{ //Print error NSLog(@"%@", error); //Print call stack for (NSString *symbol in [NSThread callStackSymbols]) { NSLog(@"\n\n %@", symbol); } } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriod.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import typedef NS_ENUM(NSUInteger, DTTimePeriodRelation){ DTTimePeriodRelationAfter, DTTimePeriodRelationStartTouching, DTTimePeriodRelationStartInside, DTTimePeriodRelationInsideStartTouching, DTTimePeriodRelationEnclosingStartTouching, DTTimePeriodRelationEnclosing, DTTimePeriodRelationEnclosingEndTouching, DTTimePeriodRelationExactMatch, DTTimePeriodRelationInside, DTTimePeriodRelationInsideEndTouching, DTTimePeriodRelationEndInside, DTTimePeriodRelationEndTouching, DTTimePeriodRelationBefore, DTTimePeriodRelationNone //One or more of the dates does not exist }; typedef NS_ENUM(NSUInteger, DTTimePeriodSize) { DTTimePeriodSizeSecond, DTTimePeriodSizeMinute, DTTimePeriodSizeHour, DTTimePeriodSizeDay, DTTimePeriodSizeWeek, DTTimePeriodSizeMonth, DTTimePeriodSizeYear }; typedef NS_ENUM(NSUInteger, DTTimePeriodInterval) { DTTimePeriodIntervalOpen, DTTimePeriodIntervalClosed }; typedef NS_ENUM(NSUInteger, DTTimePeriodAnchor) { DTTimePeriodAnchorStart, DTTimePeriodAnchorCenter, DTTimePeriodAnchorEnd }; @interface DTTimePeriod : NSObject /** * The start date for a DTTimePeriod representing the starting boundary of the time period */ @property (nonatomic,strong) NSDate *StartDate; /** * The end date for a DTTimePeriod representing the ending boundary of the time period */ @property (nonatomic,strong) NSDate *EndDate; #pragma mark - Custom Init / Factory Methods -(instancetype)initWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate; +(instancetype)timePeriodWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate; +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size startingAt:(NSDate *)date; +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount startingAt:(NSDate *)date; +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size endingAt:(NSDate *)date; +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount endingAt:(NSDate *)date; +(instancetype)timePeriodWithAllTime; #pragma mark - Time Period Information -(BOOL)hasStartDate; -(BOOL)hasEndDate; -(BOOL)isMoment; -(double)durationInYears; -(double)durationInWeeks; -(double)durationInDays; -(double)durationInHours; -(double)durationInMinutes; -(double)durationInSeconds; #pragma mark - Time Period Relationship -(BOOL)isEqualToPeriod:(DTTimePeriod *)period; -(BOOL)isInside:(DTTimePeriod *)period; -(BOOL)contains:(DTTimePeriod *)period; -(BOOL)overlapsWith:(DTTimePeriod *)period; -(BOOL)intersects:(DTTimePeriod *)period; -(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period; -(NSTimeInterval)gapBetween:(DTTimePeriod *)period; #pragma mark - Date Relationships -(BOOL)containsDate:(NSDate *)date interval:(DTTimePeriodInterval)interval; #pragma mark - Period Manipulation #pragma mark Shifts -(void)shiftEarlierWithSize:(DTTimePeriodSize)size; -(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; -(void)shiftLaterWithSize:(DTTimePeriodSize)size; -(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; #pragma mark Lengthen / Shorten -(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size; -(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount; -(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size; -(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount; #pragma mark - Helper Methods -(DTTimePeriod *)copy; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriod.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTTimePeriod.h" #import "NSDate+DateTools.h" @interface DTTimePeriod () @end @implementation DTTimePeriod #pragma mark - Custom Init / Factory Methods /** * Initializes an instance of DTTimePeriod from a given start and end date * * @param startDate NSDate - Desired start date * @param endDate NSDate - Desired end date * * @return DTTimePeriod - new instance */ -(instancetype)initWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{ if (self = [super init]) { self.StartDate = startDate; self.EndDate = endDate; } return self; } /** * Returns a new instance of DTTimePeriod from a given start and end date * * @param startDate NSDate - Desired start date * @param endDate NSDate - Desired end date * * @return DTTimePeriod - new instance */ +(instancetype)timePeriodWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{ return [[DTTimePeriod alloc] initWithStartDate:startDate endDate:endDate]; } /** * Returns a new instance of DTTimePeriod that starts on the provided start date * and is of the size provided * * @param size DTTimePeriodSize - Desired size of the new time period * @param date NSDate - Desired start date of the new time period * * @return DTTimePeriod - new instance */ +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size startingAt:(NSDate *)date{ return [[DTTimePeriod alloc] initWithStartDate:date endDate:[DTTimePeriod dateWithAddedTime:size amount:1 baseDate:date]]; } /** * Returns a new instance of DTTimePeriod that starts on the provided start date * and is of the size provided. The amount represents a multipler to the size (e.g. "2 weeks" or "4 years") * * @param size DTTimePeriodSize - Desired size of the new time period * @param amount NSInteger - Desired multiplier of the size provided * @param date NSDate - Desired start date of the new time period * * @return DTTimePeriod - new instance */ +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount startingAt:(NSDate *)date{ return [[DTTimePeriod alloc] initWithStartDate:date endDate:[DTTimePeriod dateWithAddedTime:size amount:amount baseDate:date]]; } /** * Returns a new instance of DTTimePeriod that ends on the provided end date * and is of the size provided * * @param size DTTimePeriodSize - Desired size of the new time period * @param date NSDate - Desired end date of the new time period * * @return DTTimePeriod - new instance */ +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size endingAt:(NSDate *)date{ return [[DTTimePeriod alloc] initWithStartDate:[DTTimePeriod dateWithSubtractedTime:size amount:1 baseDate:date] endDate:date]; } /** * Returns a new instance of DTTimePeriod that ends on the provided end date * and is of the size provided. The amount represents a multipler to the size (e.g. "2 weeks" or "4 years") * * @param size DTTimePeriodSize - Desired size of the new time period * @param amount NSInteger - Desired multiplier of the size provided * @param date NSDate - Desired end date of the new time period * * @return DTTimePeriod - new instance */ +(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount endingAt:(NSDate *)date{ return [[DTTimePeriod alloc] initWithStartDate:[DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:date] endDate:date]; } /** * Returns a new instance of DTTimePeriod that represents the largest time period available. * The start date is in the distant past and the end date is in the distant future. * * @return DTTimePeriod - new instance */ +(instancetype)timePeriodWithAllTime{ return [[DTTimePeriod alloc] initWithStartDate:[NSDate distantPast] endDate:[NSDate distantFuture]]; } /** * Method serving the various factory methods as well as a few others. * Returns a date with time added to a given base date. Includes multiplier amount. * * @param size DTTimePeriodSize - Desired size of the new time period * @param amount NSInteger - Desired multiplier of the size provided * @param date NSDate - Desired end date of the new time period * * @return NSDate - new instance */ +(NSDate *)dateWithAddedTime:(DTTimePeriodSize)size amount:(NSInteger)amount baseDate:(NSDate *)date{ switch (size) { case DTTimePeriodSizeSecond: return [date dateByAddingSeconds:amount]; break; case DTTimePeriodSizeMinute: return [date dateByAddingMinutes:amount]; break; case DTTimePeriodSizeHour: return [date dateByAddingHours:amount]; break; case DTTimePeriodSizeDay: return [date dateByAddingDays:amount]; break; case DTTimePeriodSizeWeek: return [date dateByAddingWeeks:amount]; break; case DTTimePeriodSizeMonth: return [date dateByAddingMonths:amount]; break; case DTTimePeriodSizeYear: return [date dateByAddingYears:amount]; break; default: break; } return date; } /** * Method serving the various factory methods as well as a few others. * Returns a date with time subtracted from a given base date. Includes multiplier amount. * * @param size DTTimePeriodSize - Desired size of the new time period * @param amount NSInteger - Desired multiplier of the size provided * @param date NSDate - Desired end date of the new time period * * @return NSDate - new instance */ +(NSDate *)dateWithSubtractedTime:(DTTimePeriodSize)size amount:(NSInteger)amount baseDate:(NSDate *)date{ switch (size) { case DTTimePeriodSizeSecond: return [date dateBySubtractingSeconds:amount]; break; case DTTimePeriodSizeMinute: return [date dateBySubtractingMinutes:amount]; break; case DTTimePeriodSizeHour: return [date dateBySubtractingHours:amount]; break; case DTTimePeriodSizeDay: return [date dateBySubtractingDays:amount]; break; case DTTimePeriodSizeWeek: return [date dateBySubtractingWeeks:amount]; break; case DTTimePeriodSizeMonth: return [date dateBySubtractingMonths:amount]; break; case DTTimePeriodSizeYear: return [date dateBySubtractingYears:amount]; break; default: break; } return date; } #pragma mark - Time Period Information /** * Returns a boolean representing whether the receiver's StartDate exists * Returns YES if StartDate is not nil, otherwise NO * * @return BOOL */ -(BOOL)hasStartDate { return (self.StartDate)? YES:NO; } /** * Returns a boolean representing whether the receiver's EndDate exists * Returns YES if EndDate is not nil, otherwise NO * * @return BOOL */ -(BOOL)hasEndDate { return (self.EndDate)? YES:NO; } /** * Returns a boolean representing whether the receiver is a "moment", that is the start and end dates are the same. * Returns YES if receiver is a moment, otherwise NO * * @return BOOL */ -(BOOL)isMoment{ if (self.StartDate && self.EndDate) { if ([self.StartDate isEqualToDate:self.EndDate]) { return YES; } } return NO; } /** * Returns the duration of the receiver in years * * @return NSInteger */ -(double)durationInYears { if (self.StartDate && self.EndDate) { return [self.StartDate yearsEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in weeks * * @return double */ -(double)durationInWeeks { if (self.StartDate && self.EndDate) { return [self.StartDate weeksEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in days * * @return double */ -(double)durationInDays { if (self.StartDate && self.EndDate) { return [self.StartDate daysEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in hours * * @return double */ -(double)durationInHours { if (self.StartDate && self.EndDate) { return [self.StartDate hoursEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in minutes * * @return double */ -(double)durationInMinutes { if (self.StartDate && self.EndDate) { return [self.StartDate minutesEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in seconds * * @return double */ -(double)durationInSeconds { if (self.StartDate && self.EndDate) { return [self.StartDate secondsEarlierThan:self.EndDate]; } return 0; } #pragma mark - Time Period Relationship /** * Returns a BOOL representing whether the receiver's start and end dates exatcly match a given time period * Returns YES if the two periods are the same, otherwise NO * * @param period DTTimePeriod - Time period to compare to receiver * * @return BOOL */ -(BOOL)isEqualToPeriod:(DTTimePeriod *)period{ if ([self.StartDate isEqualToDate:period.StartDate] && [self.EndDate isEqualToDate:period.EndDate]) { return YES; } return NO; } /** * Returns a BOOL representing whether the receiver's start and end dates exatcly match a given time period or is contained within them * Returns YES if the receiver is inside the given time period, otherwise NO * * @param period DTTimePeriod - Time period to compare to receiver * * @return BOOL */ -(BOOL)isInside:(DTTimePeriod *)period{ if ([period.StartDate isEarlierThanOrEqualTo:self.StartDate] && [period.EndDate isLaterThanOrEqualTo:self.EndDate]) { return YES; } return NO; } /** * Returns a BOOL representing whether the given time period's start and end dates exatcly match the receivers' or is contained within them * Returns YES if the receiver is inside the given time period, otherwise NO * * @param period DTTimePeriod - Time period to compare to receiver * * @return BOOL */ -(BOOL)contains:(DTTimePeriod *)period{ if ([self.StartDate isEarlierThanOrEqualTo:period.StartDate] && [self.EndDate isLaterThanOrEqualTo:period.EndDate]) { return YES; } return NO; } /** * Returns a BOOL representing whether the receiver and the given time period overlap. * This covers all space they share, minus instantaneous space (i.e. one's start date equals another's end date) * Returns YES if they overlap, otherwise NO * * @param period DTTimePeriod - Time period to compare to receiver * * @return BOOL */ -(BOOL)overlapsWith:(DTTimePeriod *)period{ //Outside -> Inside if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThan:self.StartDate]) { return YES; } //Enclosing else if ([period.StartDate isLaterThanOrEqualTo:self.StartDate] && [period.EndDate isEarlierThanOrEqualTo:self.EndDate]){ return YES; } //Inside -> Out else if([period.StartDate isEarlierThan:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ return YES; } return NO; } /** * Returns a BOOL representing whether the receiver and the given time period overlap. * This covers all space they share, including instantaneous space (i.e. one's start date equals another's end date) * Returns YES if they overlap, otherwise NO * * @param period DTTimePeriod - Time period to compare to receiver * * @return BOOL */ -(BOOL)intersects:(DTTimePeriod *)period{ //Outside -> Inside if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThanOrEqualTo:self.StartDate]) { return YES; } //Enclosing else if ([period.StartDate isLaterThanOrEqualTo:self.StartDate] && [period.EndDate isEarlierThanOrEqualTo:self.EndDate]){ return YES; } //Inside -> Out else if([period.StartDate isEarlierThanOrEqualTo:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ return YES; } return NO; } /** * Returns the relationship of the receiver to a given time period * * @param period DTTimePeriod - Time period to compare to receiver * * @return DTTimePeriodRelation */ -(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period{ //Make sure that all start and end points exist for comparison if (self.StartDate && self.EndDate && period.StartDate && period.EndDate) { //Make sure time periods are of positive durations if ([self.StartDate isEarlierThan:self.EndDate] && [period.StartDate isEarlierThan:period.EndDate]) { //Make comparisons if ([period.EndDate isEarlierThan:self.StartDate]) { return DTTimePeriodRelationAfter; } else if ([period.EndDate isEqualToDate:self.StartDate]){ return DTTimePeriodRelationStartTouching; } else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){ return DTTimePeriodRelationStartInside; } else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isLaterThan:self.EndDate]){ return DTTimePeriodRelationInsideStartTouching; } else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){ return DTTimePeriodRelationEnclosingStartTouching; } else if ([period.StartDate isLaterThan:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){ return DTTimePeriodRelationEnclosing; } else if ([period.StartDate isLaterThan:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){ return DTTimePeriodRelationEnclosingEndTouching; } else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){ return DTTimePeriodRelationExactMatch; } else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThan:self.EndDate]){ return DTTimePeriodRelationInside; } else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){ return DTTimePeriodRelationInsideEndTouching; } else if ([period.StartDate isEarlierThan:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ return DTTimePeriodRelationEndInside; } else if ([period.StartDate isEqualToDate:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){ return DTTimePeriodRelationEndTouching; } else if ([period.StartDate isLaterThan:self.EndDate]){ return DTTimePeriodRelationBefore; } } } return DTTimePeriodRelationNone; } /** * Returns the gap in seconds between the receiver and provided time period * Returns 0 if the time periods intersect, otherwise returns the gap between. * * @param period <#period description#> * * @return <#return value description#> */ -(NSTimeInterval)gapBetween:(DTTimePeriod *)period{ if ([self.EndDate isEarlierThan:period.StartDate]) { return ABS([self.EndDate timeIntervalSinceDate:period.StartDate]); } else if ([period.EndDate isEarlierThan:self.StartDate]){ return ABS([period.EndDate timeIntervalSinceDate:self.StartDate]); } return 0; } #pragma mark - Date Relationships /** * Returns a BOOL representing whether the provided date is contained in the receiver. * * @param date NSDate - Date to evaluate * @param interval DTTimePeriodInterval representing evaluation type (Closed includes StartDate and EndDate in evaluation, Open does not) * * @return <#return value description#> */ -(BOOL)containsDate:(NSDate *)date interval:(DTTimePeriodInterval)interval{ if (interval == DTTimePeriodIntervalOpen) { if ([self.StartDate isEarlierThan:date] && [self.EndDate isLaterThan:date]) { return YES; } else { return NO; } } else if (interval == DTTimePeriodIntervalClosed){ if ([self.StartDate isEarlierThanOrEqualTo:date] && [self.EndDate isLaterThanOrEqualTo:date]) { return YES; } else { return NO; } } return NO; } #pragma mark - Period Manipulation /** * Shifts the StartDate and EndDate earlier by a given size amount * * @param size DTTimePeriodSize - Desired shift size */ -(void)shiftEarlierWithSize:(DTTimePeriodSize)size{ [self shiftEarlierWithSize:size amount:1]; } /** * Shifts the StartDate and EndDate earlier by a given size amount. Amount multiplies size. * * @param size DTTimePeriodSize - Desired shift size * @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") */ -(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.StartDate]; self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.EndDate]; } /** * Shifts the StartDate and EndDate later by a given size amount * * @param size DTTimePeriodSize - Desired shift size */ -(void)shiftLaterWithSize:(DTTimePeriodSize)size{ [self shiftLaterWithSize:size amount:1]; } /** * Shifts the StartDate and EndDate later by a given size amount. Amount multiplies size. * * @param size DTTimePeriodSize - Desired shift size * @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") */ -(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.StartDate]; self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.EndDate]; } #pragma mark Lengthen / Shorten /** * Lengthens the receiver by a given amount, anchored by a provided point * * @param anchor DTTimePeriodAnchor - Anchor point for the lengthen (the date that stays the same) * @param size DTTimePeriodSize - Desired lenghtening size */ -(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size{ [self lengthenWithAnchorDate:anchor size:size amount:1]; } /** * Lengthens the receiver by a given amount, anchored by a provided point. Amount multiplies size. * * @param anchor DTTimePeriodAnchor - Anchor point for the lengthen (the date that stays the same) * @param size DTTimePeriodSize - Desired lenghtening size * @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") */ -(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount{ switch (anchor) { case DTTimePeriodAnchorStart: self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.EndDate]; break; case DTTimePeriodAnchorCenter: self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount/2 baseDate:self.StartDate]; self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount/2 baseDate:self.EndDate]; break; case DTTimePeriodAnchorEnd: self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.StartDate]; break; default: break; } } /** * Shortens the receiver by a given amount, anchored by a provided point * * @param anchor DTTimePeriodAnchor - Anchor point for the shorten (the date that stays the same) * @param size DTTimePeriodSize - Desired shortening size */ -(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size{ [self shortenWithAnchorDate:anchor size:size amount:1]; } /** * Shortens the receiver by a given amount, anchored by a provided point. Amount multiplies size. * * @param anchor DTTimePeriodAnchor - Anchor point for the shorten (the date that stays the same) * @param size DTTimePeriodSize - Desired shortening size * @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years") */ -(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount{ switch (anchor) { case DTTimePeriodAnchorStart: self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.EndDate]; break; case DTTimePeriodAnchorCenter: self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount/2 baseDate:self.StartDate]; self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount/2 baseDate:self.EndDate]; break; case DTTimePeriodAnchorEnd: self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.StartDate]; break; default: break; } } #pragma mark - Helper Methods -(DTTimePeriod *)copy{ DTTimePeriod *period = [DTTimePeriod timePeriodWithStartDate:[NSDate dateWithTimeIntervalSince1970:self.StartDate.timeIntervalSince1970] endDate:[NSDate dateWithTimeIntervalSince1970:self.EndDate.timeIntervalSince1970]]; return period; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriodChain.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import #import "DTTimePeriodGroup.h" @interface DTTimePeriodChain : DTTimePeriodGroup { DTTimePeriod *First; DTTimePeriod *Last; } @property (nonatomic, readonly) DTTimePeriod *First; @property (nonatomic, readonly) DTTimePeriod *Last; #pragma mark - Custom Init / Factory Chain +(DTTimePeriodChain *)chain; #pragma mark - Chain Existence Manipulation -(void)addTimePeriod:(DTTimePeriod *)period; -(void)insertTimePeriod:(DTTimePeriod *)period atInedx:(NSInteger)index; -(void)removeTimePeriodAtIndex:(NSInteger)index; -(void)removeLatestTimePeriod; -(void)removeEarliestTimePeriod; #pragma mark - Chain Relationship -(BOOL)isEqualToChain:(DTTimePeriodChain *)chain; #pragma mark - Updates -(void)updateVariables; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriodChain.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTTimePeriodChain.h" #import "DTError.h" @interface DTTimePeriodChain () @end @implementation DTTimePeriodChain #pragma mark - Custom Init / Factory Chain +(DTTimePeriodChain *)chain{ return [[DTTimePeriodChain alloc] init]; } #pragma mark - Chain Existence Manipulation -(void)addTimePeriod:(DTTimePeriod *)period{ if ([period class] != [DTTimePeriod class]) { [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; return; } if (periods) { if (periods.count > 0) { //Create a modified period to be added based on size of passed in period DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds startingAt:[periods[periods.count - 1] EndDate]]; //Add object to periods array [periods addObject:modifiedPeriod]; } else { //Add object to periods array [periods addObject:period]; } } else { //Create new periods array periods = [NSMutableArray array]; //Add object to periods array [periods addObject:period]; } //Set object's variables with updated array values [self updateVariables]; } -(void)insertTimePeriod:(DTTimePeriod *)period atInedx:(NSInteger)index{ if ([period class] != [DTTimePeriod class]) { [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; return; } //Make sure the index is within the operable bounds of the periods array if (index == 0) { //Update bounds of period to make it fit in chain DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds endingAt:[periods[0] EndDate]]; //Insert the updated object at the beginning of the periods array [periods insertObject:modifiedPeriod atIndex:0]; } else if (index > 0 && index < periods.count) { //Shift time periods later if they fall after new period [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { //Shift later if (idx >= index) { [((DTTimePeriod *) obj) shiftLaterWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds]; } }]; //Update bounds of period to make it fit in chain DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds startingAt:[periods[index - 1] EndDate]]; //Insert the updated object at the beginning of the periods array [periods insertObject:modifiedPeriod atIndex:index]; //Set object's variables with updated array values [self updateVariables]; } else { [DTError throwInsertOutOfBoundsException:index array:periods]; } } -(void)removeTimePeriodAtIndex:(NSInteger)index{ //Make sure the index is within the operable bounds of the periods array if (index >= 0 && index < periods.count) { DTTimePeriod *period = periods[index]; //Shift time periods later if they fall after new period [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { //Shift earlier if (idx > index) { [((DTTimePeriod *) obj) shiftEarlierWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds]; } }]; //Remove object [periods removeObjectAtIndex:index]; //Set object's variables with updated array values [self updateVariables]; } else { [DTError throwRemoveOutOfBoundsException:index array:periods]; } } -(void)removeLatestTimePeriod{ if (periods.count > 0) { [periods removeLastObject]; //Update the object variables if (periods.count > 0) { //Set object's variables with updated array values [self updateVariables]; } else { [self setVariablesNil]; } } } -(void)removeEarliestTimePeriod{ if (periods > 0) { //Shift time periods earlier [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { //Shift earlier to account for removal of first element in periods array [((DTTimePeriod *) obj) shiftEarlierWithSize:DTTimePeriodSizeSecond amount:[periods[0] durationInSeconds]]; }]; //Remove first period [periods removeObjectAtIndex:0]; //Update the object variables if (periods.count > 0) { //Set object's variables with updated array values [self updateVariables]; } else { [self setVariablesNil]; } } } #pragma mark - Chain Relationship -(BOOL)isEqualToChain:(DTTimePeriodChain *)chain{ //Check class if ([chain class] != [DTTimePeriodChain class]) { [DTError throwBadTypeException:chain expectedClass:[DTTimePeriodChain class]]; return NO; } //Check group level characteristics for speed if (![self hasSameCharacteristicsAs:chain]) { return NO; } //Check whole chain __block BOOL isEqual = YES; [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (![chain[idx] isEqualToPeriod:obj]) { isEqual = NO; *stop = YES; } }]; return isEqual; } #pragma mark - Getters -(DTTimePeriod *)First{ return First; } -(DTTimePeriod *)Last{ return Last; } #pragma mark - Helper Methods -(void)updateVariables{ //Set helper variables StartDate = [periods[0] StartDate]; EndDate = [periods[periods.count - 1] EndDate]; First = periods[0]; Last = periods[periods.count -1]; } -(void)setVariablesNil{ //Set helper variables StartDate = nil; EndDate = nil; First = nil; Last = nil; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriodCollection.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import #import "DTTimePeriodGroup.h" @interface DTTimePeriodCollection : DTTimePeriodGroup #pragma mark - Custom Init / Factory Methods +(DTTimePeriodCollection *)collection; #pragma mark - Collection Manipulation -(void)addTimePeriod:(DTTimePeriod *)period; -(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index; -(void)removeTimePeriodAtIndex:(NSInteger)index; #pragma mark - Sorting -(void)sortByStartAscending; -(void)sortByStartDescending; -(void)sortByEndAscending; -(void)sortByEndDescending; -(void)sortByDurationAscending; -(void)sortByDurationDescending; #pragma mark - Collection Relationship -(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period; -(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date; -(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period; -(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period; -(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder; #pragma mark - Helper Methods -(DTTimePeriodCollection *)copy; #pragma mark - Updates -(void)updateVariables; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriodCollection.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTTimePeriodCollection.h" #import "DTError.h" #import "NSDate+DateTools.h" @implementation DTTimePeriodCollection #pragma mark - Custom Init / Factory Methods /** * Initializes a new instance of DTTimePeriodCollection * * @return DTTimePeriodCollection */ +(DTTimePeriodCollection *)collection{ return [[DTTimePeriodCollection alloc] init]; } #pragma mark - Collection Manipulation /** * Adds a time period to the reciever. * * @param period DTTimePeriod - The time period to add to the collection */ -(void)addTimePeriod:(DTTimePeriod *)period{ if ([period isKindOfClass:[DTTimePeriod class]]) { [periods addObject:period]; //Set object's variables with updated array values [self updateVariables]; } else { [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; } } /** * Inserts a time period to the receiver at a given index. * * @param period DTTimePeriod - The time period to insert into the collection * @param index NSInteger - The index in the collection the time period is to be added at */ -(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index{ if ([period class] != [DTTimePeriod class]) { [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; return; } if (index >= 0 && index < periods.count) { [periods insertObject:period atIndex:index]; //Set object's variables with updated array values [self updateVariables]; } else { [DTError throwInsertOutOfBoundsException:index array:periods]; } } /** * Removes the time period at a given index from the collection * * @param index NSInteger - The index in the collection the time period is to be removed from */ -(void)removeTimePeriodAtIndex:(NSInteger)index{ if (index >= 0 && index < periods.count) { [periods removeObjectAtIndex:index]; //Update the object variables if (periods.count > 0) { //Set object's variables with updated array values [self updateVariables]; } else { [self setVariablesNil]; } } else { [DTError throwRemoveOutOfBoundsException:index array:periods]; } } #pragma mark - Sorting /** * Sorts the time periods in the collection by earliest start date to latest start date. */ -(void)sortByStartAscending{ [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [((DTTimePeriod *) obj1).StartDate compare:((DTTimePeriod *) obj2).StartDate]; }]; } /** * Sorts the time periods in the collection by latest start date to earliest start date. */ -(void)sortByStartDescending{ [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [((DTTimePeriod *) obj2).StartDate compare:((DTTimePeriod *) obj1).StartDate]; }]; } /** * Sorts the time periods in the collection by earliest end date to latest end date. */ -(void)sortByEndAscending{ [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [((DTTimePeriod *) obj1).EndDate compare:((DTTimePeriod *) obj2).EndDate]; }]; } /** * Sorts the time periods in the collection by latest end date to earliest end date. */ -(void)sortByEndDescending{ [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [((DTTimePeriod *) obj2).EndDate compare:((DTTimePeriod *) obj1).EndDate]; }]; } /** * Sorts the time periods in the collection by how much time they span. Sorts smallest durations to longest. */ -(void)sortByDurationAscending{ [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { if (((DTTimePeriod *) obj1).durationInSeconds < ((DTTimePeriod *) obj2).durationInSeconds) { return NSOrderedAscending; } else { return NSOrderedDescending; } return NSOrderedSame; }]; } /** * Sorts the time periods in the collection by how much time they span. Sorts longest durations to smallest. */ -(void)sortByDurationDescending{ [periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { if (((DTTimePeriod *) obj1).durationInSeconds > ((DTTimePeriod *) obj2).durationInSeconds) { return NSOrderedAscending; } else { return NSOrderedDescending; } return NSOrderedSame; }]; } #pragma mark - Collection Relationship /** * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that fall inside a given time period. * Time periods of the receiver must have a start date and end date within the closed interval of the period provided to be included. * * @param period DTTimePeriod - The time period to check against the receiver's time periods. * * @return DTTimePeriodCollection */ -(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period{ DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; if ([period isKindOfClass:[DTTimePeriod class]]) { [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([((DTTimePeriod *) obj) isInside:period]) { [collection addTimePeriod:obj]; } }]; } else { [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; } return collection; } /** * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given date. * Time periods of the receiver must have a start date earlier than or equal to the comparison date and an end date later than or equal to the comparison date to be included * * @param date NSDate - The date to check against the receiver's time periods * * @return DTTimePeriodCollection */ -(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date{ DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; if ([date isKindOfClass:[NSDate class]]) { [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([((DTTimePeriod *) obj) containsDate:date interval:DTTimePeriodIntervalClosed]) { [collection addTimePeriod:obj]; } }]; } else { [DTError throwBadTypeException:date expectedClass:[NSDate class]]; } return collection; } /** * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given time period. * Intersection with the given time period includes other time periods that simply touch it. (i.e. one's start date is equal to another's end date) * * @param period DTTimePeriod - The time period to check against the receiver's time periods. * * @return DTTimePeriodCollection */ -(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period{ DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; if ([period isKindOfClass:[DTTimePeriod class]]) { [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([((DTTimePeriod *) obj) intersects:period]) { [collection addTimePeriod:obj]; } }]; } else { [DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]]; } return collection; } /** * Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that overlap a given time period. * Overlap with the given time period does NOT include other time periods that simply touch it. (i.e. one's start date is equal to another's end date) * * @param period DTTimePeriod - The time period to check against the receiver's time periods. * * @return DTTimePeriodCollection */ -(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period{ DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init]; [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([((DTTimePeriod *) obj) overlapsWith:period]) { [collection addTimePeriod:obj]; } }]; return collection; } /** * Returns a BOOL representing whether the receiver is equal to a given DTTimePeriodCollection. Equality requires the start and end dates to be the same, and all time periods to be the same. * * If you would like to take the order of the time periods in two collections into consideration, you may do so with the considerOrder BOOL * * @param collection DTTimePeriodCollection - The collection to compare with the receiver * @param considerOrder BOOL - Option for whether to account for the time periods order in the test for equality. YES considers order, NO does not. * * @return BOOL */ -(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder{ //Check class if ([collection class] != [DTTimePeriodCollection class]) { [DTError throwBadTypeException:collection expectedClass:[DTTimePeriodCollection class]]; return NO; } //Check group level characteristics for speed if (![self hasSameCharacteristicsAs:collection]) { return NO; } //Default to equality and look for inequality __block BOOL isEqual = YES; if (considerOrder) { [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (![collection[idx] isEqualToPeriod:obj]) { isEqual = NO; *stop = YES; } }]; } else { __block DTTimePeriodCollection *collectionCopy = [collection copy]; [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { __block BOOL innerMatch = NO; __block NSInteger matchIndex = 0; //We will remove matches to account for duplicates and to help speed for (int ii = 0; ii < collectionCopy.count; ii++) { if ([obj isEqualToPeriod:collectionCopy[ii]]) { innerMatch = YES; matchIndex = ii; break; } } //If there was a match found, stop if (!innerMatch) { isEqual = NO; *stop = YES; } else { [collectionCopy removeTimePeriodAtIndex:matchIndex]; } }]; } return isEqual; } #pragma mark - Helper Methods -(void)updateVariables{ //Set helper variables __block NSDate *startDate = [NSDate distantFuture]; __block NSDate *endDate = [NSDate distantPast]; [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([((DTTimePeriod *) obj).StartDate isEarlierThan:startDate]) { startDate = ((DTTimePeriod *) obj).StartDate; } if ([((DTTimePeriod *) obj).EndDate isLaterThan:endDate]) { endDate = ((DTTimePeriod *) obj).EndDate; } }]; //Make assignments after evaluation StartDate = startDate; EndDate = endDate; } -(void)setVariablesNil{ //Set helper variables StartDate = nil; EndDate = nil; } /** * Returns a new instance of DTTimePeriodCollection that is an exact copy of the receiver, but with differnt memory references, etc. * * @return DTTimePeriodCollection */ -(DTTimePeriodCollection *)copy{ DTTimePeriodCollection *collection = [DTTimePeriodCollection collection]; [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [collection addTimePeriod:[obj copy]]; }]; return collection; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriodGroup.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import #import "DTTimePeriod.h" @interface DTTimePeriodGroup : NSObject { @protected NSMutableArray *periods; NSDate *StartDate; NSDate *EndDate; } @property (nonatomic, readonly) NSDate *StartDate; @property (nonatomic, readonly) NSDate *EndDate; //Here we will use object subscripting to help create the illusion of an array - (id)objectAtIndexedSubscript:(NSUInteger)index; //getter - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index; //setter #pragma mark - Group Info -(double)durationInYears; -(double)durationInWeeks; -(double)durationInDays; -(double)durationInHours; -(double)durationInMinutes; -(double)durationInSeconds; -(NSDate *)StartDate; -(NSDate *)EndDate; -(NSInteger)count; #pragma mark - Chain Time Manipulation -(void)shiftEarlierWithSize:(DTTimePeriodSize)size; -(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; -(void)shiftLaterWithSize:(DTTimePeriodSize)size; -(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount; #pragma mark - Comparison -(BOOL)hasSameCharacteristicsAs:(DTTimePeriodGroup *)group; #pragma mark - Updates -(void)updateVariables; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DTTimePeriodGroup.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTTimePeriodGroup.h" #import "NSDate+DateTools.h" @interface DTTimePeriodGroup () @end @implementation DTTimePeriodGroup -(id) init { if (self = [super init]) { periods = [[NSMutableArray alloc] init]; } return self; } - (id)objectAtIndexedSubscript:(NSUInteger)index { return periods[index]; } - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index { periods[index] = obj; } #pragma mark - Group Info /** * Returns the duration of the receiver in years * * @return NSInteger */ -(double)durationInYears { if (self.StartDate && self.EndDate) { return [self.StartDate yearsEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in weeks * * @return double */ -(double)durationInWeeks { if (self.StartDate && self.EndDate) { return [self.StartDate weeksEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in days * * @return double */ -(double)durationInDays { if (self.StartDate && self.EndDate) { return [self.StartDate daysEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in hours * * @return double */ -(double)durationInHours { if (self.StartDate && self.EndDate) { return [self.StartDate hoursEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in minutes * * @return double */ -(double)durationInMinutes { if (self.StartDate && self.EndDate) { return [self.StartDate minutesEarlierThan:self.EndDate]; } return 0; } /** * Returns the duration of the receiver in seconds * * @return double */ -(double)durationInSeconds { if (self.StartDate && self.EndDate) { return [self.StartDate secondsEarlierThan:self.EndDate]; } return 0; } /** * Returns the NSDate representing the earliest date in the DTTimePeriodGroup (or subclass) * * @return NSDate */ -(NSDate *)StartDate{ return StartDate; } /** * Returns the NSDate representing the latest date in the DTTimePeriodGroup (or subclass) * * @return NSDate */ -(NSDate *)EndDate{ return EndDate; } /** * The total number of DTTimePeriods in the group * * @return NSInteger */ -(NSInteger)count{ return periods.count; } /** * Returns a BOOL if the receiver and the comparison group have the same metadata (i.e. number of periods, start & end date, etc.) * Returns YES if they share the same characteristics, otherwise NO * * @param group The group to compare with the receiver * * @return BOOL */ -(BOOL)hasSameCharacteristicsAs:(DTTimePeriodGroup *)group{ //Check characteristics first for speed if (group.count != self.count) { return NO; } else if (!group.StartDate && !group.EndDate && !self.StartDate && !self.EndDate){ return YES; } else if (![group.StartDate isEqualToDate:self.StartDate] || ![group.EndDate isEqualToDate:self.EndDate]){ return NO; } return YES; } #pragma mark - Chain Time Manipulation /** * Shifts all the time periods in the collection to an earlier date by the given size * * @param size DTTimePeriodSize - The desired size of the shift */ -(void)shiftEarlierWithSize:(DTTimePeriodSize)size{ [self shiftEarlierWithSize:size amount:1]; } /** * Shifts all the time periods in the collection to an earlier date by the given size and amount. * The amount acts as a multiplier to the size (i.e. "2 weeks" or "4 years") * * @param size DTTimePeriodSize - The desired size of the shift * @param amount NSInteger - Multiplier for the size */ -(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ if (periods) { [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [((DTTimePeriod *)obj) shiftEarlierWithSize:size amount:amount]; }]; [self updateVariables]; } } /** * Shifts all the time periods in the collection to a later date by the given size * * @param size DTTimePeriodSize - The desired size of the shift */ -(void)shiftLaterWithSize:(DTTimePeriodSize)size{ [self shiftLaterWithSize:size amount:1]; } /** * Shifts all the time periods in the collection to an later date by the given size and amount. * The amount acts as a multiplier to the size (i.e. "2 weeks" or "4 years") * * @param size DTTimePeriodSize - The desired size of the shift * @param amount NSInteger - Multiplier for the size */ -(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{ if (periods) { [periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [((DTTimePeriod *)obj) shiftLaterWithSize:size amount:amount]; }]; [self updateVariables]; } } #pragma mark - Updates -(void)updateVariables{} @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/bg.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "преди %d дена"; /* No comment provided by engineer. */ "%d hours ago" = "преди %d часа"; /* No comment provided by engineer. */ "%d minutes ago" = "преди %d минути"; /* No comment provided by engineer. */ "%d months ago" = "преди %d месеца"; /* No comment provided by engineer. */ "%d seconds ago" = "преди %d секунди"; /* No comment provided by engineer. */ "%d weeks ago" = "преди %d седмици"; /* No comment provided by engineer. */ "%d years ago" = "преди %d години"; /* No comment provided by engineer. */ "A minute ago" = "преди минута"; /* No comment provided by engineer. */ "An hour ago" = "преди час"; /* No comment provided by engineer. */ "Just now" = "току що"; /* No comment provided by engineer. */ "Last month" = "през последния месец"; /* No comment provided by engineer. */ "Last week" = "през последната седмица"; /* No comment provided by engineer. */ "Last year" = "през последната година"; /* No comment provided by engineer. */ "Yesterday" = "вчера"; /* No comment provided by engineer. */ "1 year ago" = "преди 1 година"; /* No comment provided by engineer. */ "1 month ago" = "преди 1 месец"; /* No comment provided by engineer. */ "1 week ago" = "преди 1 седмица"; /* No comment provided by engineer. */ "1 day ago" = "преди 1 ден"; /* No comment provided by engineer. */ "This morning" = "тази сутрин"; /* No comment provided by engineer. */ "This afternoon" = "тази вечер"; /* No comment provided by engineer. */ "Today" = "днес"; /* No comment provided by engineer. */ "This week" = "тази седмица"; /* No comment provided by engineer. */ "This month" = "този месец"; /* No comment provided by engineer. */ "This year" = "тази година"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/ca.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "Fa %d dies"; /* No comment provided by engineer. */ "%d hours ago" = "Fa %d hores"; /* No comment provided by engineer. */ "%d minutes ago" = "Fa %d minuts"; /* No comment provided by engineer. */ "%d months ago" = "Fa %d mesos"; /* No comment provided by engineer. */ "%d seconds ago" = "Fa %d segons"; /* No comment provided by engineer. */ "%d weeks ago" = "Fa %d setmanes"; /* No comment provided by engineer. */ "%d years ago" = "Fa %d anys"; /* No comment provided by engineer. */ "A minute ago" = "Fa un minut"; /* No comment provided by engineer. */ "An hour ago" = "Fa una hora"; /* No comment provided by engineer. */ "Just now" = "Fa un moment"; /* No comment provided by engineer. */ "Last month" = "El mes passat"; /* No comment provided by engineer. */ "Last week" = "La setmana passada"; /* No comment provided by engineer. */ "Last year" = "L'any passat"; /* No comment provided by engineer. */ "Yesterday" = "Ahir"; /* No comment provided by engineer. */ "1 year ago" = "Fa un any"; /* No comment provided by engineer. */ "1 month ago" = "Fa un mes"; /* No comment provided by engineer. */ "1 week ago" = "Fa una setmana"; /* No comment provided by engineer. */ "1 day ago" = "Fa un dia"; /* No comment provided by engineer. */ "This morning" = "Aquest matí"; /* No comment provided by engineer. */ "This afternoon" = "Aquesta tarda"; /* No comment provided by engineer. */ "Today" = "Avui"; /* No comment provided by engineer. */ "This week" = "Aquesta setmana"; /* No comment provided by engineer. */ "This month" = "Aquest mes"; /* No comment provided by engineer. */ "This year" = "Aquest any"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/cs.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "Před %d dny"; /* No comment provided by engineer. */ "%d hours ago" = "Před %d hodinami"; /* No comment provided by engineer. */ "%d minutes ago" = "Před %d minutami"; /* No comment provided by engineer. */ "%d months ago" = "Před %d měsíci"; /* No comment provided by engineer. */ "%d seconds ago" = "Před %d sekundami"; /* No comment provided by engineer. */ "%d weeks ago" = "Před %d týdny"; /* No comment provided by engineer. */ "%d years ago" = "Před %d lety"; /* No comment provided by engineer. */ "A minute ago" = "Před minutou"; /* No comment provided by engineer. */ "An hour ago" = "Před hodinou"; /* No comment provided by engineer. */ "Just now" = "Právě teď"; /* No comment provided by engineer. */ "Last month" = "Minulý měsíc"; /* No comment provided by engineer. */ "Last week" = "Minulý týden"; /* No comment provided by engineer. */ "Last year" = "Minulý rok"; /* No comment provided by engineer. */ "Yesterday" = "Včera"; /* No comment provided by engineer. */ "1 year ago" = "Před rokem"; /* No comment provided by engineer. */ "1 month ago" = "Před měsícem"; /* No comment provided by engineer. */ "1 week ago" = "Před týdnem"; /* No comment provided by engineer. */ "1 day ago" = "Předevčírem"; /* No comment provided by engineer. */ "This morning" = "Dnes dopoledne"; /* No comment provided by engineer. */ "This afternoon" = "Dnes odpoledne"; /* No comment provided by engineer. */ "Today" = "Dnes"; /* No comment provided by engineer. */ "This week" = "Tento týden"; /* No comment provided by engineer. */ "This month" = "Tento měsíc"; /* No comment provided by engineer. */ "This year" = "Letos"; /* Short format for */ "%dy" = "%dr"; // year "%dM" = "%dM"; // month "%dw" = "%dt"; // week "%dd" = "%dd"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/cy.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d diwrnod yn ôl"; /* No comment provided by engineer. */ "%d hours ago" = "%d awr yn ôl"; /* No comment provided by engineer. */ "%d minutes ago" = "%d munud yn ôl"; /* No comment provided by engineer. */ "%d months ago" = "%d mis yn ôl"; /* No comment provided by engineer. */ "%d seconds ago" = "%d eiliad yn ôl"; /* No comment provided by engineer. */ "%d weeks ago" = "%d wythnos yn ôl"; /* No comment provided by engineer. */ "%d years ago" = "%d mlynydd yn ôl"; /* No comment provided by engineer. */ "A minute ago" = "Un munud yn ôl"; /* No comment provided by engineer. */ "An hour ago" = "Un awr yn ôl"; /* No comment provided by engineer. */ "Just now" = "Nawr"; /* No comment provided by engineer. */ "Last month" = "Mis diwethaf"; /* No comment provided by engineer. */ "Last week" = "Wythnos diwethaf"; /* No comment provided by engineer. */ "Last year" = "Llynedd"; /* No comment provided by engineer. */ "Yesterday" = "Ddoe"; /* No comment provided by engineer. */ "1 year ago" = "1 flynydd yn ôl"; /* No comment provided by engineer. */ "1 month ago" = "1 mis yn ôl"; /* No comment provided by engineer. */ "1 week ago" = "1 wythnos yn ôl"; /* No comment provided by engineer. */ "1 day ago" = "1 diwrnod yn ôl"; /* No comment provided by engineer. */ "This morning" = "Y bore ma"; /* No comment provided by engineer. */ "This afternoon" = "Y penwythnos hon"; /* No comment provided by engineer. */ "Today" = "Heddiw"; /* No comment provided by engineer. */ "This week" = "Yr wythnos hon"; /* No comment provided by engineer. */ "This month" = "Y mis hon"; /* No comment provided by engineer. */ "This year" = "Eleni"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/da.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dage siden"; /* No comment provided by engineer. */ "%d hours ago" = "%d timer siden"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minutter siden"; /* No comment provided by engineer. */ "%d months ago" = "%d måneder siden"; /* No comment provided by engineer. */ "%d seconds ago" = "%d sekunder siden"; /* No comment provided by engineer. */ "%d weeks ago" = "%d uger siden"; /* No comment provided by engineer. */ "%d years ago" = "%d år siden"; /* No comment provided by engineer. */ "A minute ago" = "Et minut siden"; /* No comment provided by engineer. */ "An hour ago" = "En time siden"; /* No comment provided by engineer. */ "Just now" = "Lige nu"; /* No comment provided by engineer. */ "Last month" = "Sidste måned"; /* No comment provided by engineer. */ "Last week" = "Sidste uge"; /* No comment provided by engineer. */ "Last year" = "Sidste år"; /* No comment provided by engineer. */ "Yesterday" = "I går"; /* No comment provided by engineer. */ "1 year ago" = "1 år siden"; /* No comment provided by engineer. */ "1 month ago" = "1 måned siden"; /* No comment provided by engineer. */ "1 week ago" = "1 uge siden"; /* No comment provided by engineer. */ "1 day ago" = "1 dag siden"; /* No comment provided by engineer. */ "This morning" = "Her til morgen"; /* No comment provided by engineer. */ "This afternoon" = "Her til eftermiddag"; /* No comment provided by engineer. */ "Today" = "I dag"; /* No comment provided by engineer. */ "This week" = "Denne uge"; /* No comment provided by engineer. */ "This month" = "Denne måned"; /* No comment provided by engineer. */ "This year" = "Dette år"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/de.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "Vor %d Tagen"; /* No comment provided by engineer. */ "%d hours ago" = "Vor %d Stunden"; /* No comment provided by engineer. */ "%d minutes ago" = "Vor %d Minuten"; /* No comment provided by engineer. */ "%d months ago" = "Vor %d Monaten"; /* No comment provided by engineer. */ "%d seconds ago" = "Vor %d Sekunden"; /* No comment provided by engineer. */ "%d weeks ago" = "Vor %d Wochen"; /* No comment provided by engineer. */ "%d years ago" = "Vor %d Jahren"; /* No comment provided by engineer. */ "A minute ago" = "Vor einer Minute"; /* No comment provided by engineer. */ "An hour ago" = "Vor einer Stunde"; /* No comment provided by engineer. */ "Just now" = "Gerade eben"; /* No comment provided by engineer. */ "Last month" = "Letzten Monat"; /* No comment provided by engineer. */ "Last week" = "Letzte Woche"; /* No comment provided by engineer. */ "Last year" = "Letztes Jahr"; /* No comment provided by engineer. */ "Yesterday" = "Gestern"; /* No comment provided by engineer. */ "1 year ago" = "Vor 1 Jahr"; /* No comment provided by engineer. */ "1 month ago" = "Vor 1 Monat"; /* No comment provided by engineer. */ "1 week ago" = "Vor 1 Woche"; /* No comment provided by engineer. */ "1 day ago" = "Vor 1 Tag"; /* No comment provided by engineer. */ "This morning" = "Heute Morgen"; /* No comment provided by engineer. */ "This afternoon" = "Heute Nachmittag"; /* No comment provided by engineer. */ "Today" = "Heute"; /* No comment provided by engineer. */ "This week" = "Diese Woche"; /* No comment provided by engineer. */ "This month" = "Diesen Monat"; /* No comment provided by engineer. */ "This year" = "Dieses Jahr"; /* Short format for */ "%dy" = "%dJ"; // year "%dM" = "%dM"; // month "%dw" = "%dW"; // week "%dd" = "%dT"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/en.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d days ago"; /* No comment provided by engineer. */ "%d hours ago" = "%d hours ago"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minutes ago"; /* No comment provided by engineer. */ "%d months ago" = "%d months ago"; /* No comment provided by engineer. */ "%d seconds ago" = "%d seconds ago"; /* No comment provided by engineer. */ "%d weeks ago" = "%d weeks ago"; /* No comment provided by engineer. */ "%d years ago" = "%d years ago"; /* No comment provided by engineer. */ "A minute ago" = "A minute ago"; /* No comment provided by engineer. */ "An hour ago" = "An hour ago"; /* No comment provided by engineer. */ "Just now" = "Just now"; /* No comment provided by engineer. */ "Last month" = "Last month"; /* No comment provided by engineer. */ "Last week" = "Last week"; /* No comment provided by engineer. */ "Last year" = "Last year"; /* No comment provided by engineer. */ "Yesterday" = "Yesterday"; /* No comment provided by engineer. */ "1 year ago" = "1 year ago"; /* No comment provided by engineer. */ "1 month ago" = "1 month ago"; /* No comment provided by engineer. */ "1 week ago" = "1 week ago"; /* No comment provided by engineer. */ "1 day ago" = "1 day ago"; /* No comment provided by engineer. */ "1 hour ago" = "1 hour ago"; /* No comment provided by engineer. */ "1 minute ago" = "1 minute ago"; /* No comment provided by engineer. */ "1 second ago" = "1 second ago"; /* No comment provided by engineer. */ "This morning" = "This morning"; /* No comment provided by engineer. */ "This afternoon" = "This afternoon"; /* No comment provided by engineer. */ "Today" = "Today"; /* No comment provided by engineer. */ "This week" = "This week"; /* No comment provided by engineer. */ "This month" = "This month"; /* No comment provided by engineer. */ "This year" = "This year"; /* Short format for */ "%dy" = "%dy"; // year "%dM" = "%dM"; // month "%dw" = "%dw"; // week "%dd" = "%dd"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/es.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "Hace %d días"; /* No comment provided by engineer. */ "%d hours ago" = "Hace %d horas"; /* No comment provided by engineer. */ "%d minutes ago" = "Hace %d minutos"; /* No comment provided by engineer. */ "%d months ago" = "Hace %d meses"; /* No comment provided by engineer. */ "%d seconds ago" = "Hace %d segundos"; /* No comment provided by engineer. */ "%d weeks ago" = "Hace %d semanas"; /* No comment provided by engineer. */ "%d years ago" = "Hace %d años"; /* No comment provided by engineer. */ "A minute ago" = "Hace un minuto"; /* No comment provided by engineer. */ "An hour ago" = "Hace una hora"; /* No comment provided by engineer. */ "Just now" = "Ahora mismo"; /* No comment provided by engineer. */ "Last month" = "El mes pasado"; /* No comment provided by engineer. */ "Last week" = "La semana pasada"; /* No comment provided by engineer. */ "Last year" = "El año pasado"; /* No comment provided by engineer. */ "Yesterday" = "Ayer"; /* No comment provided by engineer. */ "1 year ago" = "Hace un año"; /* No comment provided by engineer. */ "1 month ago" = "Hace un mes"; /* No comment provided by engineer. */ "1 week ago" = "Hace una semana"; /* No comment provided by engineer. */ "1 day ago" = "Hace un día"; /* No comment provided by engineer. */ "This morning" = "Esta mañana"; /* No comment provided by engineer. */ "This afternoon" = "Esta tarde"; /* No comment provided by engineer. */ "Today" = "Hoy"; /* No comment provided by engineer. */ "This week" = "Esta semana"; /* No comment provided by engineer. */ "This month" = "Este mes"; /* No comment provided by engineer. */ "This year" = "Este año"; /* Short format for */ "%dy" = "%da"; // year "%dM" = "%dM"; // month "%dw" = "%dS"; // week "%dd" = "%dd"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/eu.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "Orain dela %d egun"; /* No comment provided by engineer. */ "%d hours ago" = "Orain dela %d ordu"; /* No comment provided by engineer. */ "%d minutes ago" = "Orain dela %d minutu"; /* No comment provided by engineer. */ "%d months ago" = "Orain dela %d hile"; /* No comment provided by engineer. */ "%d seconds ago" = "Orain dela %d segundu"; /* No comment provided by engineer. */ "%d weeks ago" = "Orain dela %d aste"; /* No comment provided by engineer. */ "%d years ago" = "Orain dela %d urte"; /* No comment provided by engineer. */ "A minute ago" = "Orain dela minutu bat"; /* No comment provided by engineer. */ "An hour ago" = "Orain dela ordu bat"; /* No comment provided by engineer. */ "Just now" = "Oraintxe bertan"; /* No comment provided by engineer. */ "Last month" = "Pasa den hilean"; /* No comment provided by engineer. */ "Last week" = "Pasa den astean"; /* No comment provided by engineer. */ "Last year" = "Pasa den urtean"; /* No comment provided by engineer. */ "Yesterday" = "Atzo"; /* No comment provided by engineer. */ "1 year ago" = "Orain dela urte bat"; /* No comment provided by engineer. */ "1 month ago" = "Orain dela hile bat"; /* No comment provided by engineer. */ "1 week ago" = "Orain dela aste bat"; /* No comment provided by engineer. */ "1 day ago" = "Orain dela egun bat"; /* No comment provided by engineer. */ "This morning" = "Gaur goizean"; /* No comment provided by engineer. */ "This afternoon" = "Gaur arratsaldean"; /* No comment provided by engineer. */ "Today" = "Gaur"; /* No comment provided by engineer. */ "This week" = "Aste honetan"; /* No comment provided by engineer. */ "This month" = "Hile honetan"; /* No comment provided by engineer. */ "This year" = "Urte honetan"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/fi.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d päivää sitten"; /* No comment provided by engineer. */ "%d hours ago" = "%d tuntia sitten"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minuuttia sitten"; /* No comment provided by engineer. */ "%d months ago" = "%d kuukautta sitten"; /* No comment provided by engineer. */ "%d seconds ago" = "%d sekuntia sitten"; /* No comment provided by engineer. */ "%d weeks ago" = "%d viikkoa sitten"; /* No comment provided by engineer. */ "%d years ago" = "%d vuotta sitten"; /* No comment provided by engineer. */ "A minute ago" = "Minuutti sitten"; /* No comment provided by engineer. */ "An hour ago" = "Tunti sitten"; /* No comment provided by engineer. */ "Just now" = "Juuri äsken"; /* No comment provided by engineer. */ "Last month" = "Viime kuussa"; /* No comment provided by engineer. */ "Last week" = "Viime viikolla"; /* No comment provided by engineer. */ "Last year" = "Viime vuonna"; /* No comment provided by engineer. */ "Yesterday" = "Eilen"; /* No comment provided by engineer. */ "1 year ago" = "Vuosi sitten"; /* No comment provided by engineer. */ "1 month ago" = "Kuukausi sitten"; /* No comment provided by engineer. */ "1 week ago" = "Viikko sitten"; /* No comment provided by engineer. */ "1 day ago" = "Vuorokausi sitten"; /* No comment provided by engineer. */ "This morning" = "Tänä aamuna"; /* No comment provided by engineer. */ "This afternoon" = "Tänä iltapäivänä"; /* No comment provided by engineer. */ "Today" = "Tänään"; /* No comment provided by engineer. */ "This week" = "Tällä viikolla"; /* No comment provided by engineer. */ "This month" = "Tässä kuussa"; /* No comment provided by engineer. */ "This year" = "Tänä vuonna"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/fr.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "Il y a %d jours"; /* No comment provided by engineer. */ "%d hours ago" = "Il y a %d heures"; /* No comment provided by engineer. */ "%d minutes ago" = "Il y a %d minutes"; /* No comment provided by engineer. */ "%d months ago" = "Il y a %d mois"; /* No comment provided by engineer. */ "%d seconds ago" = "Il y a %d secondes"; /* No comment provided by engineer. */ "%d weeks ago" = "Il y a %d semaines"; /* No comment provided by engineer. */ "%d years ago" = "Il y a %d ans"; /* No comment provided by engineer. */ "A minute ago" = "Il y a une minute"; /* No comment provided by engineer. */ "An hour ago" = "Il y a une heure"; /* No comment provided by engineer. */ "Just now" = "A l'instant"; /* No comment provided by engineer. */ "Last month" = "Le mois dernier"; /* No comment provided by engineer. */ "Last week" = "La semaine dernière"; /* No comment provided by engineer. */ "Last year" = "L'année dernière"; /* No comment provided by engineer. */ "Yesterday" = "Hier"; /* No comment provided by engineer. */ "1 year ago" = "Il y a 1 an"; /* No comment provided by engineer. */ "1 month ago" = "Il y a 1 mois"; /* No comment provided by engineer. */ "1 week ago" = "Il y a 1 semaine"; /* No comment provided by engineer. */ "1 day ago" = "Il y a 1 jour"; /* No comment provided by engineer. */ "This morning" = "Ce matin"; /* No comment provided by engineer. */ "This afternoon" = "Cet après-midi"; /* No comment provided by engineer. */ "Today" = "Aujourd'hui"; /* No comment provided by engineer. */ "This week" = "Cette semaine"; /* No comment provided by engineer. */ "This month" = "Ce mois-ci"; /* No comment provided by engineer. */ "This year" = "Cette année"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/gu.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d દિવસ પહેલા"; /* No comment provided by engineer. */ "%d hours ago" = "%d કલાક પહેલા"; /* No comment provided by engineer. */ "%d minutes ago" = "%d મિનિટ પહેલા"; /* No comment provided by engineer. */ "%d months ago" = "%d મહિના પહેલા"; /* No comment provided by engineer. */ "%d seconds ago" = "%d સેકન્ડ પહેલા"; /* No comment provided by engineer. */ "%d weeks ago" = "%d અઠવાડિયા પહેલા"; /* No comment provided by engineer. */ "%d years ago" = "%d વર્ષ પહેલા"; /* No comment provided by engineer. */ "A minute ago" = "એક મિનિટ પહેલા"; /* No comment provided by engineer. */ "An hour ago" = "એક કલાક પહેલા"; /* No comment provided by engineer. */ "Just now" = "હમણાં"; /* No comment provided by engineer. */ "Last month" = "ગયા મહિને"; /* No comment provided by engineer. */ "Last week" = "ગયા અઠવાડિયે"; /* No comment provided by engineer. */ "Last year" = "ગયા વર્ષે"; /* No comment provided by engineer. */ "Yesterday" = "ગઈ કાલે"; /* No comment provided by engineer. */ "1 year ago" = "1 વર્ષ પહેલાં"; /* No comment provided by engineer. */ "1 month ago" = "1 મહિનો પહેલા"; /* No comment provided by engineer. */ "1 week ago" = "1 અઠવાડિયું પહેલા"; /* No comment provided by engineer. */ "1 day ago" = "1 દિવસ પહેલાં"; /* No comment provided by engineer. */ "1 hour ago" = "1 કલાક પહેલા"; /* No comment provided by engineer. */ "1 minute ago" = "1 મિનિટ પહેલા"; /* No comment provided by engineer. */ "1 second ago" = "1 સેકન્ડ પહેલા"; /* No comment provided by engineer. */ "This morning" = "આ સવારે"; /* No comment provided by engineer. */ "This afternoon" = "આજે બપોરે"; /* No comment provided by engineer. */ "Today" = "આજે"; /* No comment provided by engineer. */ "This week" = "આ અઠવાડિયેું"; /* No comment provided by engineer. */ "This month" = "આ મહિને"; /* No comment provided by engineer. */ "This year" = "આ વર્ષે"; /* Short format for */ "%dy" = "%dy"; // year "%dM" = "%dM"; // month "%dw" = "%dw"; // week "%dd" = "%dd"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/he.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "לפני %d ימים"; /* No comment provided by engineer. */ "%d hours ago" = "לפני %d שעות"; /* No comment provided by engineer. */ "%d minutes ago" = "לפני %d דקות"; /* No comment provided by engineer. */ "%d months ago" = "לפני %d חודשים"; /* No comment provided by engineer. */ "%d seconds ago" = "לפני %d שניות"; /* No comment provided by engineer. */ "%d weeks ago" = "לפני %d שבועות"; /* No comment provided by engineer. */ "%d years ago" = "לפני %d שנים"; /* No comment provided by engineer. */ "A minute ago" = "לפני דקה"; /* No comment provided by engineer. */ "An hour ago" = "לפני שעה"; /* No comment provided by engineer. */ "Just now" = "ממש עכשיו"; /* No comment provided by engineer. */ "Last month" = "בחודש שעבר"; /* No comment provided by engineer. */ "Last week" = "בשבוע שעבר"; /* No comment provided by engineer. */ "Last year" = "בשנה שעברה"; /* No comment provided by engineer. */ "Yesterday" = "אתמול"; /* No comment provided by engineer. */ "1 year ago" = "לפני שנה"; /* No comment provided by engineer. */ "1 month ago" = "לפני חודש"; /* No comment provided by engineer. */ "1 week ago" = "לפני שבוע"; /* No comment provided by engineer. */ "1 day ago" = "לפני יום"; /* No comment provided by engineer. */ "This morning" = "הבוקר"; /* No comment provided by engineer. */ "This afternoon" = "בצהריים"; /* No comment provided by engineer. */ "Today" = "היום"; /* No comment provided by engineer. */ "This week" = "השבוע"; /* No comment provided by engineer. */ "This month" = "החודש"; /* No comment provided by engineer. */ "This year" = "השנה"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/hi.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d दिन पहले"; /* No comment provided by engineer. */ "%d hours ago" = "%d घंटे पहले"; /* No comment provided by engineer. */ "%d minutes ago" = "%d मिनट पहले"; /* No comment provided by engineer. */ "%d months ago" = "%d महीन पहले"; /* No comment provided by engineer. */ "%d seconds ago" = "%d सेकंड पहले"; /* No comment provided by engineer. */ "%d weeks ago" = "%d हफ्ते पहले"; /* No comment provided by engineer. */ "%d years ago" = "%d साल पहले"; /* No comment provided by engineer. */ "A minute ago" = "एक मिनट पहले"; /* No comment provided by engineer. */ "An hour ago" = "एक घंटे पहले"; /* No comment provided by engineer. */ "Just now" = "बस अभी"; /* No comment provided by engineer. */ "Last month" = "पिछले महीने"; /* No comment provided by engineer. */ "Last week" = "पिछले हफ्ते"; /* No comment provided by engineer. */ "Last year" = "पिछले साल"; /* No comment provided by engineer. */ "Yesterday" = "कल"; /* No comment provided by engineer. */ "1 year ago" = "1 साल पहले"; /* No comment provided by engineer. */ "1 month ago" = "1 महीने पहले"; /* No comment provided by engineer. */ "1 week ago" = "1 हफ्ते पहले"; /* No comment provided by engineer. */ "1 day ago" = "1 दिन पहले"; /* No comment provided by engineer. */ "1 hour ago" = "1 घंटे पहले"; /* No comment provided by engineer. */ "1 minute ago" = "1 मिनट पहले"; /* No comment provided by engineer. */ "1 second ago" = "1 सेकंड पहले"; /* No comment provided by engineer. */ "This morning" = "आज सुबह"; /* No comment provided by engineer. */ "This afternoon" = "यह दोपहर"; /* No comment provided by engineer. */ "Today" = "आज"; /* No comment provided by engineer. */ "This week" = "इस सप्ताह"; /* No comment provided by engineer. */ "This month" = "इस महीने"; /* No comment provided by engineer. */ "This year" = "इस साल"; /* Short format for */ "%dy" = "%dy"; // year "%dM" = "%dM"; // month "%dw" = "%dw"; // week "%dd" = "%dd"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/hr.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dana"; /* No comment provided by engineer. */ "%d hours ago" = "%d prime sati"; /* No comment provided by engineer. */ "%d minutes ago" = "%d prije minuta"; /* No comment provided by engineer. */ "%d months ago" = "%d prije nekoliko mjeseci"; /* No comment provided by engineer. */ "%d seconds ago" = "%d sekunde prije"; /* No comment provided by engineer. */ "%d weeks ago" = "%d prije nekoliko tjedana"; /* No comment provided by engineer. */ "%d years ago" = "%d prije nekoliko godina"; /* No comment provided by engineer. */ "A minute ago" = "prije minute"; /* No comment provided by engineer. */ "An hour ago" = "prije sat vremena"; /* No comment provided by engineer. */ "Just now" = "upravo sada"; /* No comment provided by engineer. */ "Last month" = "prosli mjesec"; /* No comment provided by engineer. */ "Last week" = "prosli tjedan"; /* No comment provided by engineer. */ "Last year" = "prosle godine"; /* No comment provided by engineer. */ "Yesterday" = "jucer"; /* No comment provided by engineer. */ "1 year ago" = "Prije 1 godina"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/hu.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d napja"; /* No comment provided by engineer. */ "%d hours ago" = "%d órája"; /* No comment provided by engineer. */ "%d minutes ago" = "%d perce"; /* No comment provided by engineer. */ "%d months ago" = "%d hónapja"; /* No comment provided by engineer. */ "%d seconds ago" = "%d másodperce"; /* No comment provided by engineer. */ "%d weeks ago" = "%d hete"; /* No comment provided by engineer. */ "%d years ago" = "%d éve"; /* No comment provided by engineer. */ "A minute ago" = "Egy perccel ezelőtt"; /* No comment provided by engineer. */ "An hour ago" = "Egy órával ezelőtt"; /* No comment provided by engineer. */ "Just now" = "Az imént"; /* No comment provided by engineer. */ "Last month" = "Az előző hónapban"; /* No comment provided by engineer. */ "Last week" = "Az előző héten"; /* No comment provided by engineer. */ "Last year" = "Tavaly"; /* No comment provided by engineer. */ "Yesterday" = "Tegnap"; /* No comment provided by engineer. */ "1 year ago" = "Tavaly"; /* No comment provided by engineer. */ "1 month ago" = "Egy hónapja"; /* No comment provided by engineer. */ "1 week ago" = "Egy hete"; /* No comment provided by engineer. */ "1 day ago" = "Tegnap"; /* No comment provided by engineer. */ "This morning" = "Ma reggel"; /* No comment provided by engineer. */ "This afternoon" = "Ma délután"; /* No comment provided by engineer. */ "Today" = "Ma"; /* No comment provided by engineer. */ "This week" = "Ezen a héten"; /* No comment provided by engineer. */ "This month" = "Ebben a hónapban"; /* No comment provided by engineer. */ "This year" = "Idén"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/id.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d hari yang lalu"; /* No comment provided by engineer. */ "%d hours ago" = "%d jam yang lalu"; /* No comment provided by engineer. */ "%d minutes ago" = "%d menit yang lalu"; /* No comment provided by engineer. */ "%d months ago" = "%d bulan yang lalu"; /* No comment provided by engineer. */ "%d seconds ago" = "%d detik yang lalu"; /* No comment provided by engineer. */ "%d weeks ago" = "%d minggu yang lalu"; /* No comment provided by engineer. */ "%d years ago" = "%d tahun yang lalu"; /* No comment provided by engineer. */ "A minute ago" = "Semenit yang lalu"; /* No comment provided by engineer. */ "An hour ago" = "Sejam yang lalu"; /* No comment provided by engineer. */ "Just now" = "Sekarang"; /* No comment provided by engineer. */ "Last month" = "Bulan lalu"; /* No comment provided by engineer. */ "Last week" = "Minggu lalu"; /* No comment provided by engineer. */ "Last year" = "Tahun lalu"; /* No comment provided by engineer. */ "Yesterday" = "Kemarin"; /* No comment provided by engineer. */ "1 year ago" = "1 tahun yang lalu"; /* No comment provided by engineer. */ "1 month ago" = "1 bulan yang lalu"; /* No comment provided by engineer. */ "1 week ago" = "1 minggu yang lalu"; /* No comment provided by engineer. */ "1 day ago" = "1 hari yang lalu"; /* No comment provided by engineer. */ "This morning" = "Pagi ini"; /* No comment provided by engineer. */ "This afternoon" = "Sore ini"; /* No comment provided by engineer. */ "Today" = "Hari ini"; /* No comment provided by engineer. */ "This week" = "Minggu ini"; /* No comment provided by engineer. */ "This month" = "Bulan ini"; /* No comment provided by engineer. */ "This year" = "Tahun ini"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/is.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dögum síðan"; /* No comment provided by engineer. */ "%d hours ago" = "%d klst. síðan"; /* No comment provided by engineer. */ "%d minutes ago" = "%d mínútum síðan"; /* No comment provided by engineer. */ "%d months ago" = "%d mánuðum síðan"; /* No comment provided by engineer. */ "%d seconds ago" = "%d sekúndum síðan"; /* No comment provided by engineer. */ "%d weeks ago" = "%d vikum síðan"; /* No comment provided by engineer. */ "%d years ago" = "%d árum síðan"; /* No comment provided by engineer. */ "A minute ago" = "Einni mínútu síðan"; /* No comment provided by engineer. */ "An hour ago" = "Einni klst. síðan"; /* No comment provided by engineer. */ "Just now" = "Rétt í þessu"; /* No comment provided by engineer. */ "Last month" = "Í síðasta mánuði"; /* No comment provided by engineer. */ "Last week" = "Í síðustu viku"; /* No comment provided by engineer. */ "Last year" = "Á síðasta ári"; /* No comment provided by engineer. */ "Yesterday" = "Í gær"; /* No comment provided by engineer. */ "1 year ago" = "1 ári síðan"; /* No comment provided by engineer. */ "1 month ago" = "1 mánuði síðan"; /* No comment provided by engineer. */ "1 week ago" = "1 viku síðan"; /* No comment provided by engineer. */ "1 day ago" = "1 degi síðan"; /* No comment provided by engineer. */ "This morning" = "Í morgun"; /* No comment provided by engineer. */ "This afternoon" = "Síðdegis"; /* No comment provided by engineer. */ "Today" = "Í dag"; /* No comment provided by engineer. */ "This week" = "Í þessari viku"; /* No comment provided by engineer. */ "This month" = "Í þessum mánuði"; /* No comment provided by engineer. */ "This year" = "Á þessu ári"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/it.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d giorni fa"; /* No comment provided by engineer. */ "%d hours ago" = "%d ore fa"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minuti fa"; /* No comment provided by engineer. */ "%d months ago" = "%d mesi fa"; /* No comment provided by engineer. */ "%d seconds ago" = "%d secondi fa"; /* No comment provided by engineer. */ "%d weeks ago" = "%d settimane fa"; /* No comment provided by engineer. */ "%d years ago" = "%d anni fa"; /* No comment provided by engineer. */ "A minute ago" = "Un minuto fa"; /* No comment provided by engineer. */ "An hour ago" = "Un'ora fa"; /* No comment provided by engineer. */ "Just now" = "Ora"; /* No comment provided by engineer. */ "Last month" = "Il mese scorso"; /* No comment provided by engineer. */ "Last week" = "La settimana scorsa"; /* No comment provided by engineer. */ "Last year" = "L'anno scorso"; /* No comment provided by engineer. */ "Yesterday" = "Ieri"; /* No comment provided by engineer. */ "1 year ago" = "Un anno fa"; /* No comment provided by engineer. */ "1 month ago" = "Un mese fa"; /* No comment provided by engineer. */ "1 week ago" = "Una settimana fa"; /* No comment provided by engineer. */ "1 day ago" = "Un giorno fa"; /* No comment provided by engineer. */ "This morning" = "Questa mattina"; /* No comment provided by engineer. */ "This afternoon" = "Questo pomeriggio"; /* No comment provided by engineer. */ "Today" = "Oggi"; /* No comment provided by engineer. */ "This week" = "Questa settimana"; /* No comment provided by engineer. */ "This month" = "Questo mese"; /* No comment provided by engineer. */ "This year" = "Quest'anno"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/ja.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d日前"; /* No comment provided by engineer. */ "%d hours ago" = "%d時間前"; /* No comment provided by engineer. */ "%d minutes ago" = "%d分前"; /* No comment provided by engineer. */ "%d months ago" = "%dヶ月前"; /* No comment provided by engineer. */ "%d seconds ago" = "%d秒前"; /* No comment provided by engineer. */ "%d weeks ago" = "%d週間前"; /* No comment provided by engineer. */ "%d years ago" = "%d年前"; /* No comment provided by engineer. */ "A minute ago" = "1分前"; /* No comment provided by engineer. */ "An hour ago" = "1時間前"; /* No comment provided by engineer. */ "Just now" = "たった今"; /* No comment provided by engineer. */ "Last month" = "先月"; /* No comment provided by engineer. */ "Last week" = "先週"; /* No comment provided by engineer. */ "Last year" = "去年"; /* No comment provided by engineer. */ "Yesterday" = "昨日"; /* No comment provided by engineer. */ "1 year ago" = "1年前"; /* No comment provided by engineer. */ "1 month ago" = "1ヶ月前"; /* No comment provided by engineer. */ "1 week ago" = "1週間前"; /* No comment provided by engineer. */ "1 day ago" = "1日前"; /* No comment provided by engineer. */ "1 hour ago" = "1時間前"; /* No comment provided by engineer. */ "1 minute ago" = "1分前"; /* No comment provided by engineer. */ "1 second ago" = "1秒前"; /* No comment provided by engineer. */ "This morning" = "午前"; /* No comment provided by engineer. */ "This afternoon" = "午後"; /* No comment provided by engineer. */ "Today" = "今日"; /* No comment provided by engineer. */ "This week" = "今週"; /* No comment provided by engineer. */ "This month" = "今月"; /* No comment provided by engineer. */ "This year" = "今年"; /* Short format for */ "%dy" = "%d年"; // year "%dM" = "%d月"; // month "%dw" = "%d週"; // week "%dd" = "%d日"; // day "%dh" = "%d時間"; // hour "%dm" = "%d分"; // minute "%ds" = "%d秒"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/ko.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d일전"; /* No comment provided by engineer. */ "%d hours ago" = "%d시간전"; /* No comment provided by engineer. */ "%d minutes ago" = "%d분전"; /* No comment provided by engineer. */ "%d months ago" = "%d개월전"; /* No comment provided by engineer. */ "%d seconds ago" = "%d초전"; /* No comment provided by engineer. */ "%d weeks ago" = "%d주전"; /* No comment provided by engineer. */ "%d years ago" = "%d년전"; /* No comment provided by engineer. */ "A minute ago" = "1분전"; /* No comment provided by engineer. */ "An hour ago" = "1시간전"; /* No comment provided by engineer. */ "Just now" = "방금전"; /* No comment provided by engineer. */ "Last month" = "지난달"; /* No comment provided by engineer. */ "Last week" = "지난주"; /* No comment provided by engineer. */ "Last year" = "지난해"; /* No comment provided by engineer. */ "Yesterday" = "어제"; /* No comment provided by engineer. */ "1 year ago" = "1년전"; /* No comment provided by engineer. */ "1 month ago" = "1개월전"; /* No comment provided by engineer. */ "1 week ago" = "1주전"; /* No comment provided by engineer. */ "1 day ago" = "1일전"; /* No comment provided by engineer. */ "This morning" = "오늘 아침"; /* No comment provided by engineer. */ "This afternoon" = "오늘 오후"; /* No comment provided by engineer. */ "Today" = "오늘"; /* No comment provided by engineer. */ "This week" = "이번주"; /* No comment provided by engineer. */ "This month" = "이번달"; /* No comment provided by engineer. */ "This year" = "올해"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/lv.lproj/DateTools.strings ================================================ "1 year ago" = "Pirms gada"; "1 month ago" = "Pirms mēneša"; "1 week ago" = "Pirms nedēļas"; "1 day ago" = "Pirms dienas"; "A minute ago" = "Pirms minūtes"; "An hour ago" = "Pirms stundas"; "Last month" = "Pagājušajā mēnesī"; "Last week" = "Pagājušajā nedēļā"; "Last year" = "Pagājušajā gadā"; "Just now" = "Tikko"; "Today" = "Šodien"; "Yesterday" = "Vakar"; "This morning" = "Šorīt"; "This afternoon" = "Pēcpusdienā"; "This week" = "Šonedēļ"; "This month" = "Šomēnes"; "This year" = "Šogad"; "%d seconds ago" = "Pirms %d sekundēm"; "%d minutes ago" = "Pirms %d minūtēm"; "%d hours ago" = "Pirms %d stundām"; "%d days ago" = "Pirms %d dienām"; "%d weeks ago" = "Pirms %d nedēļām"; "%d months ago" = "Pirms %d mēnešiem"; "%d years ago" = "Pirms %d gadiem"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/ms.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d hari yang lepas"; /* No comment provided by engineer. */ "%d hours ago" = "%d jam yang lepas"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minit yang lepas"; /* No comment provided by engineer. */ "%d months ago" = "%d bulan yang lepas"; /* No comment provided by engineer. */ "%d seconds ago" = "%d saat yang lepas"; /* No comment provided by engineer. */ "%d weeks ago" = "%d minggu yang lepas"; /* No comment provided by engineer. */ "%d years ago" = "%d tahun yang lepas"; /* No comment provided by engineer. */ "A minute ago" = "Seminit yang lepas"; /* No comment provided by engineer. */ "An hour ago" = "Sejam yang lepas"; /* No comment provided by engineer. */ "Just now" = "Sebentar tadi"; /* No comment provided by engineer. */ "Last month" = "Bulan lepas"; /* No comment provided by engineer. */ "Last week" = "Minggu lepas"; /* No comment provided by engineer. */ "Last year" = "Tahun lepas"; /* No comment provided by engineer. */ "Yesterday" = "Semalam"; /* No comment provided by engineer. */ "1 year ago" = "1 tahun lepas"; /* No comment provided by engineer. */ "1 month ago" = "1 bulan lepas"; /* No comment provided by engineer. */ "1 week ago" = "1 minggu lepas"; /* No comment provided by engineer. */ "1 day ago" = "1 hari lepas"; /* No comment provided by engineer. */ "1 hour ago" = "1 jam lepas"; /* No comment provided by engineer. */ "1 minute ago" = "1 minit lepas"; /* No comment provided by engineer. */ "1 second ago" = "1 saat lepas"; /* No comment provided by engineer. */ "This morning" = "Pagi ini"; /* No comment provided by engineer. */ "This afternoon" = "Petang ini"; /* No comment provided by engineer. */ "Today" = "Hari ini"; /* No comment provided by engineer. */ "This week" = "Minggu ini"; /* No comment provided by engineer. */ "This month" = "Bulan ini"; /* No comment provided by engineer. */ "This year" = "Tahun ini"; /* Short format for */ "%dy" = "%dy"; // year "%dM" = "%dM"; // month "%dw" = "%dw"; // week "%dd" = "%dd"; // day "%dh" = "%dh"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/nb.lproj/DateTools.strings ================================================ /* RULES: Assume value for (seconds, hours, minutes, days, weeks, months or years) is XXXY, Y is last digit, XY is last two digits; */ /* Y ==0 OR Y > 4 OR XY == 11; */ "%d days ago" = "%d dager siden"; /* If Y != 1 AND Y < 5; */ "%d _days ago" = "%d dager siden"; /* If Y == 1; */ "%d __days ago" = "%d dag siden"; /* Y ==0 OR Y > 4 OR XY == 11; */ "%d hours ago" = "%d timer siden"; /* If Y != 1 AND Y < 5; */ "%d _hours ago" = "%d timer siden"; /* If Y == 1; */ "%d __hours ago" = "%d time siden"; /* Y ==0 OR Y > 4 OR XY == 11; */ "%d minutes ago" = "%d minutter siden"; /* If Y != 1 AND Y < 5; */ "%d _minutes ago" = "%d minutter siden"; /* If Y == 1; */ "%d __minutes ago" = "%d minutt siden"; /* Y ==0 OR Y > 4 OR XY == 11; */ "%d months ago" = "%d måneder siden"; /* If Y != 1 AND Y < 5; */ "%d _months ago" = "%d måneder siden"; /* If Y == 1; */ "%d __months ago" = "%d måned siden"; /* Y ==0 OR Y > 4 OR XY == 11; */ "%d seconds ago" = "%d sekunder siden"; /* If Y != 1 AND Y < 5; */ "%d _seconds ago" = "%d sekunder siden"; /* If Y == 1; */ "%d __seconds ago" = "%d sekund siden"; /* Y ==0 OR Y > 4 OR XY == 11; */ "%d weeks ago" = "%d uker siden"; /* If Y != 1 AND Y < 5; */ "%d _weeks ago" = "%d uker siden"; /* If Y == 1; */ "%d __weeks ago" = "%d uke siden"; /* Y ==0 OR Y > 4 OR XY == 11; */ "%d years ago" = "%d år siden"; /* If Y != 1 AND Y < 5; */ "%d _years ago" = "%d år siden"; /* If Y == 1; */ "%d __years ago" = "%d år siden"; /* No comment provided by engineer. */ "A minute ago" = "Et minutt siden"; /* No comment provided by engineer. */ "An hour ago" = "En time siden"; /* No comment provided by engineer. */ "Just now" = "Nå"; /* No comment provided by engineer. */ "Last month" = "For en måned siden"; /* No comment provided by engineer. */ "Last week" = "For en uke siden"; /* No comment provided by engineer. */ "Last year" = "For et år siden"; /* No comment provided by engineer. */ "Yesterday" = "I går"; /* No comment provided by engineer. */ "1 year ago" = "1 år siden"; /* No comment provided by engineer. */ "1 month ago" = "1 måned siden"; /* No comment provided by engineer. */ "1 week ago" = "1 uke siden"; /* No comment provided by engineer. */ "1 day ago" = "1 dag siden"; /* No comment provided by engineer. */ "This morning" = "Denne morgenen"; /* No comment provided by engineer. */ "This afternoon" = "I ettermiddag"; /* No comment provided by engineer. */ "Today" = "I dag"; /* No comment provided by engineer. */ "This week" = "Denne uken"; /* No comment provided by engineer. */ "This month" = "Denne måneden"; /* No comment provided by engineer. */ "This year" = "Dette året"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/nl.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dagen geleden"; /* No comment provided by engineer. */ "%d hours ago" = "%d uur geleden"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minuten geleden"; /* No comment provided by engineer. */ "%d months ago" = "%d maanden geleden"; /* No comment provided by engineer. */ "%d seconds ago" = "%d seconden geleden"; /* No comment provided by engineer. */ "%d weeks ago" = "%d weken geleden"; /* No comment provided by engineer. */ "%d years ago" = "%d jaar geleden"; /* No comment provided by engineer. */ "A minute ago" = "Een minuut geleden"; /* No comment provided by engineer. */ "An hour ago" = "Een uur geleden"; /* No comment provided by engineer. */ "Just now" = "Zojuist"; /* No comment provided by engineer. */ "Last month" = "Vorige maand"; /* No comment provided by engineer. */ "Last week" = "Vorige week"; /* No comment provided by engineer. */ "Last year" = "Vorig jaar"; /* No comment provided by engineer. */ "Yesterday" = "Gisteren"; /* No comment provided by engineer. */ "1 year ago" = "1 jaar geleden"; /* No comment provided by engineer. */ "1 month ago" = "1 maand geleden"; /* No comment provided by engineer. */ "1 week ago" = "1 week geleden"; /* No comment provided by engineer. */ "1 day ago" = "1 dag geleden"; /* No comment provided by engineer. */ "This morning" = "Vanmorgen"; /* No comment provided by engineer. */ "This afternoon" = "Vanmiddag"; /* No comment provided by engineer. */ "Today" = "Vandaag"; /* No comment provided by engineer. */ "This week" = "Deze week"; /* No comment provided by engineer. */ "This month" = "Deze maand"; /* No comment provided by engineer. */ "This year" = "Dit jaar"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/pl.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dni temu"; /* No comment provided by engineer. */ "%d hours ago" = "%d godzin(y) temu"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minut(y) temu"; /* No comment provided by engineer. */ "%d months ago" = "%d miesiące/-y temu"; /* No comment provided by engineer. */ "%d seconds ago" = "%d sekund(y) temu"; /* No comment provided by engineer. */ "%d weeks ago" = "%d tygodni(e) temu"; /* No comment provided by engineer. */ "%d years ago" = "%d lat(a) temu"; /* No comment provided by engineer. */ "A minute ago" = "Minutę temu"; /* No comment provided by engineer. */ "An hour ago" = "Godzinę temu"; /* No comment provided by engineer. */ "Just now" = "W tej chwili"; /* No comment provided by engineer. */ "Last month" = "W zeszłym miesiącu"; /* No comment provided by engineer. */ "Last week" = "W zeszłym tygodniu"; /* No comment provided by engineer. */ "Last year" = "W zeszłym roku"; /* No comment provided by engineer. */ "Yesterday" = "Wczoraj"; /* No comment provided by engineer. */ "1 year ago" = "1 rok temu"; /* No comment provided by engineer. */ "1 month ago" = "1 miesiąc temu"; /* No comment provided by engineer. */ "1 week ago" = "1 tydzień temu"; /* No comment provided by engineer. */ "1 day ago" = "1 dzień temu"; /* No comment provided by engineer. */ "This morning" = "Dziś rano"; /* No comment provided by engineer. */ "This afternoon" = "Dziś po południu"; /* No comment provided by engineer. */ "Today" = "Dzisiaj"; /* No comment provided by engineer. */ "This week" = "W tym tygodniu"; /* No comment provided by engineer. */ "This month" = "W tym miesiącu"; /* No comment provided by engineer. */ "This year" = "W tym roku"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/pt-PT.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dias atrás"; /* No comment provided by engineer. */ "%d hours ago" = "%d horas atrás"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minutos atrás"; /* No comment provided by engineer. */ "%d months ago" = "%d meses atrás"; /* No comment provided by engineer. */ "%d seconds ago" = "%d segundos atrás"; /* No comment provided by engineer. */ "%d weeks ago" = "%d semanas atrás"; /* No comment provided by engineer. */ "%d years ago" = "%d anos atrás"; /* No comment provided by engineer. */ "A minute ago" = "Um minuto atrás"; /* No comment provided by engineer. */ "An hour ago" = "Uma hora atrás"; /* No comment provided by engineer. */ "Just now" = "Agora mesmo"; /* No comment provided by engineer. */ "Last month" = "Mês passado"; /* No comment provided by engineer. */ "Last week" = "Semana passada"; /* No comment provided by engineer. */ "Last year" = "Ano passado"; /* No comment provided by engineer. */ "Yesterday" = "Ontem"; /* No comment provided by engineer. */ "1 year ago" = "1 ano passado"; /* No comment provided by engineer. */ "1 month ago" = "1 mês atrás"; /* No comment provided by engineer. */ "1 week ago" = "1 semana atrás"; /* No comment provided by engineer. */ "1 day ago" = "1 dia atrás"; /* No comment provided by engineer. */ "This morning" = "Esta manhã"; /* No comment provided by engineer. */ "This afternoon" = "Esta tarde"; /* No comment provided by engineer. */ "Today" = "Hoje"; /* No comment provided by engineer. */ "This week" = "Esta semana"; /* No comment provided by engineer. */ "This month" = "Este mês"; /* No comment provided by engineer. */ "This year" = "Este ano"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/pt.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d dias atrás"; /* No comment provided by engineer. */ "%d hours ago" = "%d horas atrás"; /* No comment provided by engineer. */ "%d minutes ago" = "%d minutos atrás"; /* No comment provided by engineer. */ "%d months ago" = "%d meses atrás"; /* No comment provided by engineer. */ "%d seconds ago" = "%d segundos atrás"; /* No comment provided by engineer. */ "%d weeks ago" = "%d semanas atrás"; /* No comment provided by engineer. */ "%d years ago" = "%d anos atrás"; /* No comment provided by engineer. */ "A minute ago" = "Há um minuto"; /* No comment provided by engineer. */ "An hour ago" = "Há uma hora"; /* No comment provided by engineer. */ "Just now" = "Agora"; /* No comment provided by engineer. */ "Last month" = "Mês passado"; /* No comment provided by engineer. */ "Last week" = "Semana passada"; /* No comment provided by engineer. */ "Last year" = "Ano passado"; /* No comment provided by engineer. */ "Yesterday" = "Ontem"; /* No comment provided by engineer. */ "1 year ago" = "1 ano atrás"; /* No comment provided by engineer. */ "1 month ago" = "1 mês atrás"; /* No comment provided by engineer. */ "1 week ago" = "1 semana atrás"; /* No comment provided by engineer. */ "1 day ago" = "1 dia atrás"; /* No comment provided by engineer. */ "This morning" = "Esta manhã"; /* No comment provided by engineer. */ "This afternoon" = "Esta tarde"; /* No comment provided by engineer. */ "Today" = "Hoje"; /* No comment provided by engineer. */ "This week" = "Esta semana"; /* No comment provided by engineer. */ "This month" = "Este mês"; /* No comment provided by engineer. */ "This year" = "Este ano"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/ro.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "În urmă cu %d zile"; /* No comment provided by engineer. */ "%d hours ago" = "În urmă cu %d ore"; /* No comment provided by engineer. */ "%d minutes ago" = "În urmă cu %d minute"; /* No comment provided by engineer. */ "%d months ago" = "În urmă cu %d luni"; /* No comment provided by engineer. */ "%d seconds ago" = "În urmă cu %d secunde"; /* No comment provided by engineer. */ "%d weeks ago" = "În urmă cu %d săptămâni"; /* No comment provided by engineer. */ "%d years ago" = "În urmă cu %d ani"; /* No comment provided by engineer. */ "A minute ago" = "În urmă cu 1 minut"; /* No comment provided by engineer. */ "An hour ago" = "În urmă cu 1 oră"; /* No comment provided by engineer. */ "Just now" = "Acum câteva momente"; /* No comment provided by engineer. */ "Last month" = "Luna trecută"; /* No comment provided by engineer. */ "Last week" = "Săptămâna trecută"; /* No comment provided by engineer. */ "Last year" = "Anul trecut"; /* No comment provided by engineer. */ "Yesterday" = "Ieri"; /* No comment provided by engineer. */ "1 year ago" = "În urmă cu 1 an"; /* No comment provided by engineer. */ "1 month ago" = "În urmă cu 1 lună"; /* No comment provided by engineer. */ "1 week ago" = "În urmă cu 1 săptămână"; /* No comment provided by engineer. */ "1 day ago" = "În urmă cu 1 zi"; /* No comment provided by engineer. */ "This morning" = "Azi dimineață"; /* No comment provided by engineer. */ "This afternoon" = "În această seară"; /* No comment provided by engineer. */ "Today" = "Astăzi"; /* No comment provided by engineer. */ "This week" = "Săptămâna aceasta"; /* No comment provided by engineer. */ "This month" = "Luna aceasta"; /* No comment provided by engineer. */ "This year" = "Anul acesta"; /* Short format for */ "%dy" = "%da"; // year (an) "%dM" = "%dl"; // month (luna) "%dw" = "%dS"; // week (saptamana) "%dd" = "%dz"; // day (ziua) "%dh" = "%do"; // hour (ora) "%dm" = "%dm"; // minute (minut) "%ds" = "%ds"; // second (secunda) ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/ru.lproj/DateTools.strings ================================================ /* RULES: Assume value for (seconds, hours, minutes, days, weeks, months or years) is XXXY, Y is last digit, XY is last two digits; */ /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d days ago" = "%d дней назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _days ago" = "%d дня назад"; /* Y == 1 AND XY != 11; */ "%d __days ago" = "%d день назад"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d hours ago" = "%d часов назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _hours ago" = "%d часа назад"; /* Y == 1 AND XY != 11; */ "%d __hours ago" = "%d час назад"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d minutes ago" = "%d минут назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _minutes ago" = "%d минуты назад"; /* Y == 1 AND XY != 11; */ "%d __minutes ago" = "%d минуту назад"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d months ago" = "%d месяцев назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _months ago" = "%d месяца назад"; /* Y == 1 AND XY != 11; */ "%d __months ago" = "%d месяц назад"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d seconds ago" = "%d секунд назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _seconds ago" = "%d секунды назад"; /* Y == 1 AND XY != 11; */ "%d __seconds ago" = "%d секунду назад"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d weeks ago" = "%d недель назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _weeks ago" = "%d недели назад"; /* Y == 1 AND XY != 11; */ "%d __weeks ago" = "%d неделю назад"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d years ago" = "%d лет назад"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _years ago" = "%d года назад"; /* Y == 1 AND XY != 11; */ "%d __years ago" = "%d год назад"; /* No comment provided by engineer. */ "A minute ago" = "Минуту назад"; /* No comment provided by engineer. */ "An hour ago" = "Час назад"; /* No comment provided by engineer. */ "Just now" = "Только что"; /* No comment provided by engineer. */ "Last month" = "Месяц назад"; /* No comment provided by engineer. */ "Last week" = "Неделю назад"; /* No comment provided by engineer. */ "Last year" = "Год назад"; /* No comment provided by engineer. */ "Yesterday" = "Вчера"; /* No comment provided by engineer. */ "1 year ago" = "1 год назад"; /* No comment provided by engineer. */ "1 month ago" = "1 месяц назад"; /* No comment provided by engineer. */ "1 week ago" = "1 неделю назад"; /* No comment provided by engineer. */ "1 day ago" = "1 день назад"; /* No comment provided by engineer. */ "This morning" = "Этим утром"; /* No comment provided by engineer. */ "This afternoon" = "Этим днём"; /* No comment provided by engineer. */ "Today" = "Сегодня"; /* No comment provided by engineer. */ "This week" = "На этой неделе"; /* No comment provided by engineer. */ "This month" = "В этом месяце"; /* No comment provided by engineer. */ "This year" = "В этом году"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/sl.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "pred %d dnevi"; /* No comment provided by engineer. */ "%d hours ago" = "pred %d urami"; /* No comment provided by engineer. */ "%d minutes ago" = "pred %d minutami"; /* No comment provided by engineer. */ "%d months ago" = "pred %d meseci"; /* No comment provided by engineer. */ "%d seconds ago" = "pred %d sekundami"; /* No comment provided by engineer. */ "%d weeks ago" = "pred %d tedni"; /* No comment provided by engineer. */ "%d years ago" = "pred %d leti"; /* No comment provided by engineer. */ "A minute ago" = "pred eno minuto"; /* No comment provided by engineer. */ "An hour ago" = "pred eno uro"; /* No comment provided by engineer. */ "Just now" = "ravnokar"; /* No comment provided by engineer. */ "Last month" = "prejšnji mesec"; /* No comment provided by engineer. */ "Last week" = "prejšnji teden"; /* No comment provided by engineer. */ "Last year" = "prejšnje leto"; /* No comment provided by engineer. */ "Yesterday" = "včeraj"; /* No comment provided by engineer. */ "1 year ago" = "pred 1 letom"; /* No comment provided by engineer. */ "1 month ago" = "pred 1 mesecem"; /* No comment provided by engineer. */ "1 week ago" = "pred 1 tednom"; /* No comment provided by engineer. */ "1 day ago" = "pred 1 dnevom"; /* No comment provided by engineer. */ "1 hour ago" = "pred 1 uro"; /* No comment provided by engineer. */ "1 minute ago" = "pred 1 minuto"; /* No comment provided by engineer. */ "1 second ago" = "pred 1 sekundo"; /* No comment provided by engineer. */ "This morning" = "zjutraj"; /* No comment provided by engineer. */ "This afternoon" = "zvečer"; /* No comment provided by engineer. */ "Today" = "danes"; /* No comment provided by engineer. */ "This week" = "ta teden"; /* No comment provided by engineer. */ "This month" = "ta mesec"; /* No comment provided by engineer. */ "This year" = "to leto"; /* Short format for */ "%dy" = "%dl"; // year "%dM" = "%dM"; // month "%dw" = "%dt"; // week "%dd" = "%dd"; // day "%dh" = "%du"; // hour "%dm" = "%dm"; // minute "%ds" = "%ds"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/tr.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d gün önce"; /* No comment provided by engineer. */ "%d hours ago" = "%d saat önce"; /* No comment provided by engineer. */ "%d minutes ago" = "%d dakika önce"; /* No comment provided by engineer. */ "%d months ago" = "%d ay önce"; /* No comment provided by engineer. */ "%d seconds ago" = "%d saniye önce"; /* No comment provided by engineer. */ "%d weeks ago" = "%d hafta önce"; /* No comment provided by engineer. */ "%d years ago" = "%d yıl önce"; /* No comment provided by engineer. */ "A minute ago" = "Bir dakika önce"; /* No comment provided by engineer. */ "An hour ago" = "Bir saat önce"; /* No comment provided by engineer. */ "Just now" = "Şimdi"; /* No comment provided by engineer. */ "Last month" = "Geçen ay"; /* No comment provided by engineer. */ "Last week" = "Geçen hafta"; /* No comment provided by engineer. */ "Last year" = "Geçen yıl"; /* No comment provided by engineer. */ "Yesterday" = "Dün"; /* No comment provided by engineer. */ "1 year ago" = "1 yıl önce"; /* No comment provided by engineer. */ "1 month ago" = "1 ay önce"; /* No comment provided by engineer. */ "1 week ago" = "1 hafta önce"; /* No comment provided by engineer. */ "1 day ago" = "1 gün önce"; /* No comment provided by engineer. */ "This morning" = "Bu sabah"; /* No comment provided by engineer. */ "This afternoon" = "Öğleden sonra"; /* No comment provided by engineer. */ "Today" = "Bugün"; /* No comment provided by engineer. */ "This week" = "Bu hafta"; /* No comment provided by engineer. */ "This month" = "Bu ay"; /* No comment provided by engineer. */ "This year" = "Bu yıl"; /* Short format for */ "%dy" = "%dy"; // year "%dM" = "%day"; // month "%dw" = "%dh"; // week "%dd" = "%dg"; // day "%dh" = "%dsa"; // hour "%dm" = "%ddk"; // minute "%ds" = "%dsn"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/uk.lproj/DateTools.strings ================================================ /* RULES: Assume value for (seconds, hours, minutes, days, weeks, months or years) is XXXY, Y is last digit, XY is last two digits; */ /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d days ago" = "%d днів тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _days ago" = "%d дні тому"; /* Y == 1 AND XY != 11; */ "%d __days ago" = "%d день тому"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d hours ago" = "%d годин тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _hours ago" = "%d години тому"; /* Y == 1 AND XY != 11; */ "%d __hours ago" = "%d годину тому"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d minutes ago" = "%d хвилин тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _minutes ago" = "%d хвилини тому"; /* Y == 1 AND XY != 11; */ "%d __minutes ago" = "%d хвилину тому"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d months ago" = "%d місяців тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _months ago" = "%d місяці тому"; /* Y == 1 AND XY != 11; */ "%d __months ago" = "%d місяць тому"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d seconds ago" = "%d секунд тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _seconds ago" = "%d секунди тому"; /* Y == 1 AND XY != 11; */ "%d __seconds ago" = "%d секунду тому"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d weeks ago" = "%d тижнів тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _weeks ago" = "%d тижні тому"; /* Y == 1 AND XY != 11; */ "%d __weeks ago" = "%d тиждень тому"; /* Y == 0 OR Y > 4 OR (XY > 10 AND XY < 15); */ "%d years ago" = "%d років тому"; /* Y > 1 AND Y < 5 AND (XY < 10 OR XY > 20); */ "%d _years ago" = "%d роки тому"; /* Y == 1 AND XY != 11; */ "%d __years ago" = "%d рік тому"; /* No comment provided by engineer. */ "A minute ago" = "Хвилину тому"; /* No comment provided by engineer. */ "An hour ago" = "Годину тому"; /* No comment provided by engineer. */ "Just now" = "Щойно"; /* No comment provided by engineer. */ "Last month" = "Місяць тому"; /* No comment provided by engineer. */ "Last week" = "Тиждень тому"; /* No comment provided by engineer. */ "Last year" = "Рік тому"; /* No comment provided by engineer. */ "Yesterday" = "Вчора"; /* No comment provided by engineer. */ "1 year ago" = "1 рік тому"; /* No comment provided by engineer. */ "1 month ago" = "1 місяць тому"; /* No comment provided by engineer. */ "1 week ago" = "1 тиждень тому"; /* No comment provided by engineer. */ "1 day ago" = "1 день тому"; /* No comment provided by engineer. */ "This morning" = "Цього ранку"; /* No comment provided by engineer. */ "This afternoon" = "Сьогодні вдень"; /* No comment provided by engineer. */ "Today" = "Сьогодні"; /* No comment provided by engineer. */ "This week" = "Цього тижня"; /* No comment provided by engineer. */ "This month" = "Цього місяця"; /* No comment provided by engineer. */ "This year" = "Цього року"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/vi.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d ngày trước"; /* No comment provided by engineer. */ "%d hours ago" = "%d giờ trước"; /* No comment provided by engineer. */ "%d minutes ago" = "%d phút trước"; /* No comment provided by engineer. */ "%d months ago" = "%d tháng trước"; /* No comment provided by engineer. */ "%d seconds ago" = "%d giây trước"; /* No comment provided by engineer. */ "%d weeks ago" = "%d tuần trước"; /* No comment provided by engineer. */ "%d years ago" = "%d năm trước"; /* No comment provided by engineer. */ "A minute ago" = "Một phút trước"; /* No comment provided by engineer. */ "An hour ago" = "Một giờ trước"; /* No comment provided by engineer. */ "Just now" = "Vừa mới đây"; /* No comment provided by engineer. */ "Last month" = "Tháng trước"; /* No comment provided by engineer. */ "Last week" = "Tuần trước"; /* No comment provided by engineer. */ "Last year" = "Năm vừa rồi"; /* No comment provided by engineer. */ "Yesterday" = "Hôm qua"; /* No comment provided by engineer. */ "1 year ago" = "1 năm trước"; /* No comment provided by engineer. */ "1 month ago" = "1 tháng trước"; /* No comment provided by engineer. */ "1 week ago" = "1 tuần trước"; /* No comment provided by engineer. */ "1 day ago" = "1 ngày trước"; /* No comment provided by engineer. */ "This morning" = "Sáng nay"; /* No comment provided by engineer. */ "This afternoon" = "Trưa nay"; /* No comment provided by engineer. */ "Today" = "Hôm nay"; /* No comment provided by engineer. */ "This week" = "Tuần này"; /* No comment provided by engineer. */ "This month" = "Tháng này"; /* No comment provided by engineer. */ "This year" = "Năm nay"; ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/zh-Hans.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d天前"; /* No comment provided by engineer. */ "%d hours ago" = "%d小时前"; /* No comment provided by engineer. */ "%d minutes ago" = "%d分钟前"; /* No comment provided by engineer. */ "%d months ago" = "%d个月前"; /* No comment provided by engineer. */ "%d seconds ago" = "%d秒钟前"; /* No comment provided by engineer. */ "%d weeks ago" = "%d星期前"; /* No comment provided by engineer. */ "%d years ago" = "%d年前"; /* No comment provided by engineer. */ "A minute ago" = "1分钟前"; /* No comment provided by engineer. */ "An hour ago" = "1小时前"; /* No comment provided by engineer. */ "Just now" = "刚刚"; /* No comment provided by engineer. */ "Last month" = "上个月"; /* No comment provided by engineer. */ "Last week" = "上星期"; /* No comment provided by engineer. */ "Last year" = "去年"; /* No comment provided by engineer. */ "Yesterday" = "昨天"; /* You can add a space between the number and the characters. */ "1 year ago" = "1年前"; /* You can add a space between the number and the characters. */ "1 month ago" = "1个月前"; /* You can add a space between the number and the characters. */ "1 week ago" = "1星期前"; /* You can add a space between the number and the characters. */ "1 day ago" = "1天前"; /* No comment provided by engineer. */ "This morning" = "今天上午"; /* No comment provided by engineer. */ "This afternoon" = "今天下午"; /* No comment provided by engineer. */ "Today" = "今天"; /* No comment provided by engineer. */ "This week" = "本周"; /* No comment provided by engineer. */ "This month" = "本月"; /* No comment provided by engineer. */ "This year" = "今年"; /* Short format for */ "%dy" = "%d年"; // year "%dM" = "%d月"; // month "%dw" = "%d周"; // week "%dd" = "%d天"; // day "%dh" = "%d小时"; // hour "%dm" = "%d分"; // minute "%ds" = "%d秒"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.bundle/zh-Hant.lproj/DateTools.strings ================================================ /* No comment provided by engineer. */ "%d days ago" = "%d天前"; /* No comment provided by engineer. */ "%d hours ago" = "%d小時前"; /* No comment provided by engineer. */ "%d minutes ago" = "%d分鐘前"; /* No comment provided by engineer. */ "%d months ago" = "%d個月前"; /* No comment provided by engineer. */ "%d seconds ago" = "%d秒鐘前"; /* No comment provided by engineer. */ "%d weeks ago" = "%d星期前"; /* No comment provided by engineer. */ "%d years ago" = "%d年前"; /* No comment provided by engineer. */ "A minute ago" = "1分鐘前"; /* No comment provided by engineer. */ "An hour ago" = "1小時前"; /* No comment provided by engineer. */ "Just now" = "剛剛"; /* No comment provided by engineer. */ "Last month" = "上個月"; /* No comment provided by engineer. */ "Last week" = "上星期"; /* No comment provided by engineer. */ "Last year" = "去年"; /* No comment provided by engineer. */ "Yesterday" = "昨天"; /* No comment provided by engineer. */ "1 year ago" = "1年前"; /* No comment provided by engineer. */ "1 month ago" = "1個月前"; /* No comment provided by engineer. */ "1 week ago" = "1星期前"; /* No comment provided by engineer. */ "1 day ago" = "1天前"; /* No comment provided by engineer. */ "This morning" = "今天上午"; /* No comment provided by engineer. */ "This afternoon" = "今天下午"; /* No comment provided by engineer. */ "Today" = "今天"; /* No comment provided by engineer. */ "This week" = "本周"; /* No comment provided by engineer. */ "This month" = "本月"; /* No comment provided by engineer. */ "This year" = "今年"; /* Short format for */ "%dy" = "%d年"; // year "%dM" = "%d月"; // month "%dw" = "%d週"; // week "%dd" = "%d天"; // day "%dh" = "%d小時"; // hour "%dm" = "%d分"; // minute "%ds" = "%d秒"; // second ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/DateTools.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "DTConstants.h" #import "DTError.h" #import "NSDate+DateTools.h" #import "DTTimePeriod.h" #import "DTTimePeriodGroup.h" #import "DTTimePeriodCollection.h" #import "DTTimePeriodChain.h" ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/NSDate+DateTools.h ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef DateToolsLocalizedStrings #define DateToolsLocalizedStrings(key) \ NSLocalizedStringFromTableInBundle(key, @"DateTools", [NSBundle bundleWithPath:[[[NSBundle bundleForClass:[DTError class]] resourcePath] stringByAppendingPathComponent:@"DateTools.bundle"]], nil) #endif #import #import "DTConstants.h" @interface NSDate (DateTools) #pragma mark - Time Ago + (NSString*)timeAgoSinceDate:(NSDate*)date; + (NSString*)shortTimeAgoSinceDate:(NSDate*)date; - (NSString*)timeAgoSinceNow; - (NSString *)shortTimeAgoSinceNow; - (NSString *)timeAgoSinceDate:(NSDate *)date; - (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates; - (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates numericTimes:(BOOL)useNumericTimes; - (NSString *)shortTimeAgoSinceDate:(NSDate *)date; #pragma mark - Date Components Without Calendar - (NSInteger)era; - (NSInteger)year; - (NSInteger)month; - (NSInteger)day; - (NSInteger)hour; - (NSInteger)minute; - (NSInteger)second; - (NSInteger)weekday; - (NSInteger)weekdayOrdinal; - (NSInteger)quarter; - (NSInteger)weekOfMonth; - (NSInteger)weekOfYear; - (NSInteger)yearForWeekOfYear; - (NSInteger)daysInMonth; - (NSInteger)dayOfYear; -(NSInteger)daysInYear; -(BOOL)isInLeapYear; - (BOOL)isToday; - (BOOL)isTomorrow; -(BOOL)isYesterday; - (BOOL)isWeekend; -(BOOL)isSameDay:(NSDate *)date; + (BOOL)isSameDay:(NSDate *)date asDate:(NSDate *)compareDate; #pragma mark - Date Components With Calendar - (NSInteger)eraWithCalendar:(NSCalendar *)calendar; - (NSInteger)yearWithCalendar:(NSCalendar *)calendar; - (NSInteger)monthWithCalendar:(NSCalendar *)calendar; - (NSInteger)dayWithCalendar:(NSCalendar *)calendar; - (NSInteger)hourWithCalendar:(NSCalendar *)calendar; - (NSInteger)minuteWithCalendar:(NSCalendar *)calendar; - (NSInteger)secondWithCalendar:(NSCalendar *)calendar; - (NSInteger)weekdayWithCalendar:(NSCalendar *)calendar; - (NSInteger)weekdayOrdinalWithCalendar:(NSCalendar *)calendar; - (NSInteger)quarterWithCalendar:(NSCalendar *)calendar; - (NSInteger)weekOfMonthWithCalendar:(NSCalendar *)calendar; - (NSInteger)weekOfYearWithCalendar:(NSCalendar *)calendar; - (NSInteger)yearForWeekOfYearWithCalendar:(NSCalendar *)calendar; #pragma mark - Date Creating + (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day; + (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second; + (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString; + (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString timeZone:(NSTimeZone *)timeZone; #pragma mark - Date Editing #pragma mark Date By Adding - (NSDate *)dateByAddingYears:(NSInteger)years; - (NSDate *)dateByAddingMonths:(NSInteger)months; - (NSDate *)dateByAddingWeeks:(NSInteger)weeks; - (NSDate *)dateByAddingDays:(NSInteger)days; - (NSDate *)dateByAddingHours:(NSInteger)hours; - (NSDate *)dateByAddingMinutes:(NSInteger)minutes; - (NSDate *)dateByAddingSeconds:(NSInteger)seconds; #pragma mark Date By Subtracting - (NSDate *)dateBySubtractingYears:(NSInteger)years; - (NSDate *)dateBySubtractingMonths:(NSInteger)months; - (NSDate *)dateBySubtractingWeeks:(NSInteger)weeks; - (NSDate *)dateBySubtractingDays:(NSInteger)days; - (NSDate *)dateBySubtractingHours:(NSInteger)hours; - (NSDate *)dateBySubtractingMinutes:(NSInteger)minutes; - (NSDate *)dateBySubtractingSeconds:(NSInteger)seconds; #pragma mark - Date Comparison #pragma mark Time From -(NSInteger)yearsFrom:(NSDate *)date; -(NSInteger)monthsFrom:(NSDate *)date; -(NSInteger)weeksFrom:(NSDate *)date; -(NSInteger)daysFrom:(NSDate *)date; -(double)hoursFrom:(NSDate *)date; -(double)minutesFrom:(NSDate *)date; -(double)secondsFrom:(NSDate *)date; #pragma mark Time From With Calendar -(NSInteger)yearsFrom:(NSDate *)date calendar:(NSCalendar *)calendar; -(NSInteger)monthsFrom:(NSDate *)date calendar:(NSCalendar *)calendar; -(NSInteger)weeksFrom:(NSDate *)date calendar:(NSCalendar *)calendar; -(NSInteger)daysFrom:(NSDate *)date calendar:(NSCalendar *)calendar; #pragma mark Time Until -(NSInteger)yearsUntil; -(NSInteger)monthsUntil; -(NSInteger)weeksUntil; -(NSInteger)daysUntil; -(double)hoursUntil; -(double)minutesUntil; -(double)secondsUntil; #pragma mark Time Ago -(NSInteger)yearsAgo; -(NSInteger)monthsAgo; -(NSInteger)weeksAgo; -(NSInteger)daysAgo; -(double)hoursAgo; -(double)minutesAgo; -(double)secondsAgo; #pragma mark Earlier Than -(NSInteger)yearsEarlierThan:(NSDate *)date; -(NSInteger)monthsEarlierThan:(NSDate *)date; -(NSInteger)weeksEarlierThan:(NSDate *)date; -(NSInteger)daysEarlierThan:(NSDate *)date; -(double)hoursEarlierThan:(NSDate *)date; -(double)minutesEarlierThan:(NSDate *)date; -(double)secondsEarlierThan:(NSDate *)date; #pragma mark Later Than -(NSInteger)yearsLaterThan:(NSDate *)date; -(NSInteger)monthsLaterThan:(NSDate *)date; -(NSInteger)weeksLaterThan:(NSDate *)date; -(NSInteger)daysLaterThan:(NSDate *)date; -(double)hoursLaterThan:(NSDate *)date; -(double)minutesLaterThan:(NSDate *)date; -(double)secondsLaterThan:(NSDate *)date; #pragma mark Comparators -(BOOL)isEarlierThan:(NSDate *)date; -(BOOL)isLaterThan:(NSDate *)date; -(BOOL)isEarlierThanOrEqualTo:(NSDate *)date; -(BOOL)isLaterThanOrEqualTo:(NSDate *)date; #pragma mark - Formatted Dates #pragma mark Formatted With Style -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style; -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone; -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style locale:(NSLocale *)locale; -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale; #pragma mark Formatted With Format -(NSString *)formattedDateWithFormat:(NSString *)format; -(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone; -(NSString *)formattedDateWithFormat:(NSString *)format locale:(NSLocale *)locale; -(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale; #pragma mark - Helpers +(NSString *)defaultCalendarIdentifier; + (void)setDefaultCalendarIdentifier:(NSString *)identifier; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools/NSDate+DateTools.m ================================================ // Copyright (C) 2014 by Matthew York // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "NSDate+DateTools.h" typedef NS_ENUM(NSUInteger, DTDateComponent){ DTDateComponentEra, DTDateComponentYear, DTDateComponentMonth, DTDateComponentDay, DTDateComponentHour, DTDateComponentMinute, DTDateComponentSecond, DTDateComponentWeekday, DTDateComponentWeekdayOrdinal, DTDateComponentQuarter, DTDateComponentWeekOfMonth, DTDateComponentWeekOfYear, DTDateComponentYearForWeekOfYear, DTDateComponentDayOfYear }; typedef NS_ENUM(NSUInteger, DateAgoFormat){ DateAgoLong, DateAgoLongUsingNumericDatesAndTimes, DateAgoLongUsingNumericDates, DateAgoLongUsingNumericTimes, DateAgoShort }; typedef NS_ENUM(NSUInteger, DateAgoValues){ YearsAgo, MonthsAgo, WeeksAgo, DaysAgo, HoursAgo, MinutesAgo, SecondsAgo }; static const unsigned int allCalendarUnitFlags = NSCalendarUnitYear | NSCalendarUnitQuarter | NSCalendarUnitMonth | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekOfMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitEra | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal | NSCalendarUnitWeekOfYear; static NSString *defaultCalendarIdentifier = nil; static NSCalendar *implicitCalendar = nil; @implementation NSDate (DateTools) + (void)load { [self setDefaultCalendarIdentifier:NSCalendarIdentifierGregorian]; } #pragma mark - Time Ago /** * Takes in a date and returns a string with the most convenient unit of time representing * how far in the past that date is from now. * * @param NSDate - Date to be measured from now * * @return NSString - Formatted return string */ + (NSString*)timeAgoSinceDate:(NSDate*)date{ return [date timeAgoSinceDate:[NSDate date]]; } /** * Takes in a date and returns a shortened string with the most convenient unit of time representing * how far in the past that date is from now. * * @param NSDate - Date to be measured from now * * @return NSString - Formatted return string */ + (NSString*)shortTimeAgoSinceDate:(NSDate*)date{ return [date shortTimeAgoSinceDate:[NSDate date]]; } /** * Returns a string with the most convenient unit of time representing * how far in the past that date is from now. * * @return NSString - Formatted return string */ - (NSString*)timeAgoSinceNow{ return [self timeAgoSinceDate:[NSDate date]]; } /** * Returns a shortened string with the most convenient unit of time representing * how far in the past that date is from now. * * @return NSString - Formatted return string */ - (NSString *)shortTimeAgoSinceNow{ return [self shortTimeAgoSinceDate:[NSDate date]]; } - (NSString *)timeAgoSinceDate:(NSDate *)date{ return [self timeAgoSinceDate:date numericDates:NO]; } - (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates{ return [self timeAgoSinceDate:date numericDates:useNumericDates numericTimes:NO]; } - (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates numericTimes:(BOOL)useNumericTimes{ if (useNumericDates && useNumericTimes) { return [self timeAgoSinceDate:date format:DateAgoLongUsingNumericDatesAndTimes]; } else if (useNumericDates) { return [self timeAgoSinceDate:date format:DateAgoLongUsingNumericDates]; } else if (useNumericTimes) { return [self timeAgoSinceDate:date format:DateAgoLongUsingNumericDates]; } else { return [self timeAgoSinceDate:date format:DateAgoLong]; } } - (NSString *)shortTimeAgoSinceDate:(NSDate *)date{ return [self timeAgoSinceDate:date format:DateAgoShort]; } - (NSString *)timeAgoSinceDate:(NSDate *)date format:(DateAgoFormat)format { NSCalendar *calendar = [NSCalendar currentCalendar]; NSDate *earliest = [self earlierDate:date]; NSDate *latest = (earliest == self) ? date : self; // if timeAgo < 24h => compare DateTime else compare Date only NSUInteger upToHours = NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour; NSDateComponents *difference = [calendar components:upToHours fromDate:earliest toDate:latest options:0]; if (difference.hour < 24) { if (difference.hour >= 1) { return [self localizedStringFor:format valueType:HoursAgo value:difference.hour]; } else if (difference.minute >= 1) { return [self localizedStringFor:format valueType:MinutesAgo value:difference.minute]; } else { return [self localizedStringFor:format valueType:SecondsAgo value:difference.second]; } } else { NSUInteger bigUnits = NSCalendarUnitTimeZone | NSCalendarUnitDay | NSCalendarUnitWeekOfYear | NSCalendarUnitMonth | NSCalendarUnitYear; NSDateComponents *components = [calendar components:bigUnits fromDate:earliest]; earliest = [calendar dateFromComponents:components]; components = [calendar components:bigUnits fromDate:latest]; latest = [calendar dateFromComponents:components]; difference = [calendar components:bigUnits fromDate:earliest toDate:latest options:0]; if (difference.year >= 1) { return [self localizedStringFor:format valueType:YearsAgo value:difference.year]; } else if (difference.month >= 1) { return [self localizedStringFor:format valueType:MonthsAgo value:difference.month]; } else if (difference.weekOfYear >= 1) { return [self localizedStringFor:format valueType:WeeksAgo value:difference.weekOfYear]; } else { return [self localizedStringFor:format valueType:DaysAgo value:difference.day]; } } } - (NSString *)localizedStringFor:(DateAgoFormat)format valueType:(DateAgoValues)valueType value:(NSInteger)value { BOOL isShort = format == DateAgoShort; BOOL isNumericDate = format == DateAgoLongUsingNumericDates || format == DateAgoLongUsingNumericDatesAndTimes; BOOL isNumericTime = format == DateAgoLongUsingNumericTimes || format == DateAgoLongUsingNumericDatesAndTimes; switch (valueType) { case YearsAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@y" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@years ago" withValue:value]; } else if (isNumericDate) { return DateToolsLocalizedStrings(@"1 year ago"); } else { return DateToolsLocalizedStrings(@"Last year"); } case MonthsAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@M" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@months ago" withValue:value]; } else if (isNumericDate) { return DateToolsLocalizedStrings(@"1 month ago"); } else { return DateToolsLocalizedStrings(@"Last month"); } case WeeksAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@w" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@weeks ago" withValue:value]; } else if (isNumericDate) { return DateToolsLocalizedStrings(@"1 week ago"); } else { return DateToolsLocalizedStrings(@"Last week"); } case DaysAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@d" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@days ago" withValue:value]; } else if (isNumericDate) { return DateToolsLocalizedStrings(@"1 day ago"); } else { return DateToolsLocalizedStrings(@"Yesterday"); } case HoursAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@h" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@hours ago" withValue:value]; } else if (isNumericTime) { return DateToolsLocalizedStrings(@"1 hour ago"); } else { return DateToolsLocalizedStrings(@"An hour ago"); } case MinutesAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@m" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@minutes ago" withValue:value]; } else if (isNumericTime) { return DateToolsLocalizedStrings(@"1 minute ago"); } else { return DateToolsLocalizedStrings(@"A minute ago"); } case SecondsAgo: if (isShort) { return [self logicLocalizedStringFromFormat:@"%%d%@s" withValue:value]; } else if (value >= 2) { return [self logicLocalizedStringFromFormat:@"%%d %@seconds ago" withValue:value]; } else if (isNumericTime) { return DateToolsLocalizedStrings(@"1 second ago"); } else { return DateToolsLocalizedStrings(@"Just now"); } } return nil; } - (NSString *) logicLocalizedStringFromFormat:(NSString *)format withValue:(NSInteger)value{ NSString * localeFormat = [NSString stringWithFormat:format, [self getLocaleFormatUnderscoresWithValue:value]]; return [NSString stringWithFormat:DateToolsLocalizedStrings(localeFormat), value]; } - (NSString *)getLocaleFormatUnderscoresWithValue:(double)value{ NSString *localeCode = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]; // Russian (ru) and Ukrainian (uk) if([localeCode isEqualToString:@"ru"] || [localeCode isEqualToString:@"uk"]) { int XY = (int)floor(value) % 100; int Y = (int)floor(value) % 10; if(Y == 0 || Y > 4 || (XY > 10 && XY < 15)) { return @""; } if(Y > 1 && Y < 5 && (XY < 10 || XY > 20)) { return @"_"; } if(Y == 1 && XY != 11) { return @"__"; } } // Add more languages here, which are have specific translation rules... return @""; } #pragma mark - Date Components Without Calendar /** * Returns the era of the receiver. (0 for BC, 1 for AD for Gregorian) * * @return NSInteger */ - (NSInteger)era{ return [self componentForDate:self type:DTDateComponentEra calendar:nil]; } /** * Returns the year of the receiver. * * @return NSInteger */ - (NSInteger)year{ return [self componentForDate:self type:DTDateComponentYear calendar:nil]; } /** * Returns the month of the year of the receiver. * * @return NSInteger */ - (NSInteger)month{ return [self componentForDate:self type:DTDateComponentMonth calendar:nil]; } /** * Returns the day of the month of the receiver. * * @return NSInteger */ - (NSInteger)day{ return [self componentForDate:self type:DTDateComponentDay calendar:nil]; } /** * Returns the hour of the day of the receiver. (0-24) * * @return NSInteger */ - (NSInteger)hour{ return [self componentForDate:self type:DTDateComponentHour calendar:nil]; } /** * Returns the minute of the receiver. (0-59) * * @return NSInteger */ - (NSInteger)minute{ return [self componentForDate:self type:DTDateComponentMinute calendar:nil]; } /** * Returns the second of the receiver. (0-59) * * @return NSInteger */ - (NSInteger)second{ return [self componentForDate:self type:DTDateComponentSecond calendar:nil]; } /** * Returns the day of the week of the receiver. * * @return NSInteger */ - (NSInteger)weekday{ return [self componentForDate:self type:DTDateComponentWeekday calendar:nil]; } /** * Returns the ordinal for the day of the week of the receiver. * * @return NSInteger */ - (NSInteger)weekdayOrdinal{ return [self componentForDate:self type:DTDateComponentWeekdayOrdinal calendar:nil]; } /** * Returns the quarter of the receiver. * * @return NSInteger */ - (NSInteger)quarter{ return [self componentForDate:self type:DTDateComponentQuarter calendar:nil]; } /** * Returns the week of the month of the receiver. * * @return NSInteger */ - (NSInteger)weekOfMonth{ return [self componentForDate:self type:DTDateComponentWeekOfMonth calendar:nil]; } /** * Returns the week of the year of the receiver. * * @return NSInteger */ - (NSInteger)weekOfYear{ return [self componentForDate:self type:DTDateComponentWeekOfYear calendar:nil]; } /** * I honestly don't know much about this value... * * @return NSInteger */ - (NSInteger)yearForWeekOfYear{ return [self componentForDate:self type:DTDateComponentYearForWeekOfYear calendar:nil]; } /** * Returns how many days are in the month of the receiver. * * @return NSInteger */ - (NSInteger)daysInMonth{ NSCalendar *calendar = [NSCalendar currentCalendar]; NSRange days = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self]; return days.length; } /** * Returns the day of the year of the receiver. (1-365 or 1-366 for leap year) * * @return NSInteger */ - (NSInteger)dayOfYear{ return [self componentForDate:self type:DTDateComponentDayOfYear calendar:nil]; } /** * Returns how many days are in the year of the receiver. * * @return NSInteger */ -(NSInteger)daysInYear{ if (self.isInLeapYear) { return 366; } return 365; } /** * Returns whether the receiver falls in a leap year. * * @return NSInteger */ -(BOOL)isInLeapYear{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *dateComponents = [calendar components:allCalendarUnitFlags fromDate:self]; if (dateComponents.year%400 == 0){ return YES; } else if (dateComponents.year%100 == 0){ return NO; } else if (dateComponents.year%4 == 0){ return YES; } return NO; } - (BOOL)isToday { NSCalendar *cal = [NSCalendar currentCalendar]; NSDateComponents *components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:[NSDate date]]; NSDate *today = [cal dateFromComponents:components]; components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:self]; NSDate *otherDate = [cal dateFromComponents:components]; return [today isEqualToDate:otherDate]; } - (BOOL)isTomorrow { NSCalendar *cal = [NSCalendar currentCalendar]; NSDateComponents *components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:[[NSDate date] dateByAddingDays:1]]; NSDate *tomorrow = [cal dateFromComponents:components]; components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:self]; NSDate *otherDate = [cal dateFromComponents:components]; return [tomorrow isEqualToDate:otherDate]; } -(BOOL)isYesterday{ NSCalendar *cal = [NSCalendar currentCalendar]; NSDateComponents *components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:[[NSDate date] dateBySubtractingDays:1]]; NSDate *tomorrow = [cal dateFromComponents:components]; components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:self]; NSDate *otherDate = [cal dateFromComponents:components]; return [tomorrow isEqualToDate:otherDate]; } - (BOOL)isWeekend { NSCalendar *calendar = [NSCalendar currentCalendar]; NSRange weekdayRange = [calendar maximumRangeOfUnit:NSCalendarUnitWeekday]; NSDateComponents *components = [calendar components:NSCalendarUnitWeekday fromDate:self]; NSUInteger weekdayOfSomeDate = [components weekday]; BOOL result = NO; if (weekdayOfSomeDate == weekdayRange.location || weekdayOfSomeDate == weekdayRange.length) result = YES; return result; } /** * Returns whether two dates fall on the same day. * * @param date NSDate - Date to compare with sender * @return BOOL - YES if both paramter dates fall on the same day, NO otherwise */ -(BOOL)isSameDay:(NSDate *)date { return [NSDate isSameDay:self asDate:date]; } /** * Returns whether two dates fall on the same day. * * @param date NSDate - First date to compare * @param compareDate NSDate - Second date to compare * @return BOOL - YES if both paramter dates fall on the same day, NO otherwise */ + (BOOL)isSameDay:(NSDate *)date asDate:(NSDate *)compareDate { NSCalendar *cal = [NSCalendar currentCalendar]; NSDateComponents *components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:date]; NSDate *dateOne = [cal dateFromComponents:components]; components = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:compareDate]; NSDate *dateTwo = [cal dateFromComponents:components]; return [dateOne isEqualToDate:dateTwo]; } #pragma mark - Date Components With Calendar /** * Returns the era of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the era (0 for BC, 1 for AD for Gregorian) */ - (NSInteger)eraWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentEra calendar:calendar]; } /** * Returns the year of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the year as an integer */ - (NSInteger)yearWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentYear calendar:calendar]; } /** * Returns the month of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the month as an integer */ - (NSInteger)monthWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentMonth calendar:calendar]; } /** * Returns the day of the month of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the day of the month as an integer */ - (NSInteger)dayWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentDay calendar:calendar]; } /** * Returns the hour of the day of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the hour of the day as an integer */ - (NSInteger)hourWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentHour calendar:calendar]; } /** * Returns the minute of the hour of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the minute of the hour as an integer */ - (NSInteger)minuteWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentMinute calendar:calendar]; } /** * Returns the second of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the second as an integer */ - (NSInteger)secondWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentSecond calendar:calendar]; } /** * Returns the weekday of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the weekday as an integer */ - (NSInteger)weekdayWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentWeekday calendar:calendar]; } /** * Returns the weekday ordinal of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the weekday ordinal as an integer */ - (NSInteger)weekdayOrdinalWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentWeekdayOrdinal calendar:calendar]; } /** * Returns the quarter of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the quarter as an integer */ - (NSInteger)quarterWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentQuarter calendar:calendar]; } /** * Returns the week of the month of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the week of the month as an integer */ - (NSInteger)weekOfMonthWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentWeekOfMonth calendar:calendar]; } /** * Returns the week of the year of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the week of the year as an integer */ - (NSInteger)weekOfYearWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentWeekOfYear calendar:calendar]; } /** * Returns the year for week of the year (???) of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the year for week of the year as an integer */ - (NSInteger)yearForWeekOfYearWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentYearForWeekOfYear calendar:calendar]; } /** * Returns the day of the year of the receiver from a given calendar * * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - represents the day of the year as an integer */ - (NSInteger)dayOfYearWithCalendar:(NSCalendar *)calendar{ return [self componentForDate:self type:DTDateComponentDayOfYear calendar:calendar]; } /** * Takes in a date, calendar and desired date component and returns the desired NSInteger * representation for that component * * @param date NSDate - The date to be be mined for a desired component * @param component DTDateComponent - The desired component (i.e. year, day, week, etc) * @param calendar NSCalendar - The calendar to be used in the processing (Defaults to Gregorian) * * @return NSInteger */ -(NSInteger)componentForDate:(NSDate *)date type:(DTDateComponent)component calendar:(NSCalendar *)calendar{ if (!calendar) { calendar = [[self class] implicitCalendar]; } unsigned int unitFlags = 0; if (component == DTDateComponentYearForWeekOfYear) { unitFlags = NSCalendarUnitYear | NSCalendarUnitQuarter | NSCalendarUnitMonth | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekOfMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitEra | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal | NSCalendarUnitWeekOfYear | NSCalendarUnitYearForWeekOfYear; } else { unitFlags = allCalendarUnitFlags; } NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:date]; switch (component) { case DTDateComponentEra: return [dateComponents era]; case DTDateComponentYear: return [dateComponents year]; case DTDateComponentMonth: return [dateComponents month]; case DTDateComponentDay: return [dateComponents day]; case DTDateComponentHour: return [dateComponents hour]; case DTDateComponentMinute: return [dateComponents minute]; case DTDateComponentSecond: return [dateComponents second]; case DTDateComponentWeekday: return [dateComponents weekday]; case DTDateComponentWeekdayOrdinal: return [dateComponents weekdayOrdinal]; case DTDateComponentQuarter: return [dateComponents quarter]; case DTDateComponentWeekOfMonth: return [dateComponents weekOfMonth]; case DTDateComponentWeekOfYear: return [dateComponents weekOfYear]; case DTDateComponentYearForWeekOfYear: return [dateComponents yearForWeekOfYear]; case DTDateComponentDayOfYear: return [calendar ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:date]; default: break; } return 0; } #pragma mark - Date Creating + (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day { return [self dateWithYear:year month:month day:day hour:0 minute:0 second:0]; } + (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second { NSDate *nsDate = nil; NSDateComponents *components = [[NSDateComponents alloc] init]; components.year = year; components.month = month; components.day = day; components.hour = hour; components.minute = minute; components.second = second; nsDate = [[[self class] implicitCalendar] dateFromComponents:components]; return nsDate; } + (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString { return [self dateWithString:dateString formatString:formatString timeZone:[NSTimeZone systemTimeZone]]; } + (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString timeZone:(NSTimeZone *)timeZone { static NSDateFormatter *parser = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ parser = [[NSDateFormatter alloc] init]; }); parser.dateStyle = NSDateFormatterNoStyle; parser.timeStyle = NSDateFormatterNoStyle; parser.timeZone = timeZone; parser.dateFormat = formatString; return [parser dateFromString:dateString]; } #pragma mark - Date Editing #pragma mark Date By Adding /** * Returns a date representing the receivers date shifted later by the provided number of years. * * @param years NSInteger - Number of years to add * * @return NSDate - Date modified by the number of desired years */ - (NSDate *)dateByAddingYears:(NSInteger)years{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setYear:years]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted later by the provided number of months. * * @param years NSInteger - Number of months to add * * @return NSDate - Date modified by the number of desired months */ - (NSDate *)dateByAddingMonths:(NSInteger)months{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setMonth:months]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted later by the provided number of weeks. * * @param years NSInteger - Number of weeks to add * * @return NSDate - Date modified by the number of desired weeks */ - (NSDate *)dateByAddingWeeks:(NSInteger)weeks{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setWeekOfYear:weeks]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted later by the provided number of days. * * @param years NSInteger - Number of days to add * * @return NSDate - Date modified by the number of desired days */ - (NSDate *)dateByAddingDays:(NSInteger)days{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setDay:days]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted later by the provided number of hours. * * @param years NSInteger - Number of hours to add * * @return NSDate - Date modified by the number of desired hours */ - (NSDate *)dateByAddingHours:(NSInteger)hours{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setHour:hours]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted later by the provided number of minutes. * * @param years NSInteger - Number of minutes to add * * @return NSDate - Date modified by the number of desired minutes */ - (NSDate *)dateByAddingMinutes:(NSInteger)minutes{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setMinute:minutes]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted later by the provided number of seconds. * * @param years NSInteger - Number of seconds to add * * @return NSDate - Date modified by the number of desired seconds */ - (NSDate *)dateByAddingSeconds:(NSInteger)seconds{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setSecond:seconds]; return [calendar dateByAddingComponents:components toDate:self options:0]; } #pragma mark Date By Subtracting /** * Returns a date representing the receivers date shifted earlier by the provided number of years. * * @param years NSInteger - Number of years to subtract * * @return NSDate - Date modified by the number of desired years */ - (NSDate *)dateBySubtractingYears:(NSInteger)years{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setYear:-1*years]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted earlier by the provided number of months. * * @param years NSInteger - Number of months to subtract * * @return NSDate - Date modified by the number of desired months */ - (NSDate *)dateBySubtractingMonths:(NSInteger)months{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setMonth:-1*months]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted earlier by the provided number of weeks. * * @param years NSInteger - Number of weeks to subtract * * @return NSDate - Date modified by the number of desired weeks */ - (NSDate *)dateBySubtractingWeeks:(NSInteger)weeks{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setWeekOfYear:-1*weeks]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted earlier by the provided number of days. * * @param years NSInteger - Number of days to subtract * * @return NSDate - Date modified by the number of desired days */ - (NSDate *)dateBySubtractingDays:(NSInteger)days{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setDay:-1*days]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted earlier by the provided number of hours. * * @param years NSInteger - Number of hours to subtract * * @return NSDate - Date modified by the number of desired hours */ - (NSDate *)dateBySubtractingHours:(NSInteger)hours{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setHour:-1*hours]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted earlier by the provided number of minutes. * * @param years NSInteger - Number of minutes to subtract * * @return NSDate - Date modified by the number of desired minutes */ - (NSDate *)dateBySubtractingMinutes:(NSInteger)minutes{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setMinute:-1*minutes]; return [calendar dateByAddingComponents:components toDate:self options:0]; } /** * Returns a date representing the receivers date shifted earlier by the provided number of seconds. * * @param years NSInteger - Number of seconds to subtract * * @return NSDate - Date modified by the number of desired seconds */ - (NSDate *)dateBySubtractingSeconds:(NSInteger)seconds{ NSCalendar *calendar = [[self class] implicitCalendar]; NSDateComponents *components = [[NSDateComponents alloc] init]; [components setSecond:-1*seconds]; return [calendar dateByAddingComponents:components toDate:self options:0]; } #pragma mark - Date Comparison #pragma mark Time From /** * Returns an NSInteger representing the amount of time in years between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * Uses the default Gregorian calendar * * @param date NSDate - The provided date for comparison * * @return NSInteger - The NSInteger representation of the years between receiver and provided date */ -(NSInteger)yearsFrom:(NSDate *)date{ return [self yearsFrom:date calendar:nil]; } /** * Returns an NSInteger representing the amount of time in months between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * Uses the default Gregorian calendar * * @param date NSDate - The provided date for comparison * * @return NSInteger - The NSInteger representation of the years between receiver and provided date */ -(NSInteger)monthsFrom:(NSDate *)date{ return [self monthsFrom:date calendar:nil]; } /** * Returns an NSInteger representing the amount of time in weeks between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * Uses the default Gregorian calendar * * @param date NSDate - The provided date for comparison * * @return NSInteger - The double representation of the weeks between receiver and provided date */ -(NSInteger)weeksFrom:(NSDate *)date{ return [self weeksFrom:date calendar:nil]; } /** * Returns an NSInteger representing the amount of time in days between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * Uses the default Gregorian calendar * * @param date NSDate - The provided date for comparison * * @return NSInteger - The double representation of the days between receiver and provided date */ -(NSInteger)daysFrom:(NSDate *)date{ return [self daysFrom:date calendar:nil]; } /** * Returns an NSInteger representing the amount of time in hours between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * * @return double - The double representation of the hours between receiver and provided date */ -(double)hoursFrom:(NSDate *)date{ return ([self timeIntervalSinceDate:date])/SECONDS_IN_HOUR; } /** * Returns an NSInteger representing the amount of time in minutes between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * * @return double - The double representation of the minutes between receiver and provided date */ -(double)minutesFrom:(NSDate *)date{ return ([self timeIntervalSinceDate:date])/SECONDS_IN_MINUTE; } /** * Returns an NSInteger representing the amount of time in seconds between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * * @return double - The double representation of the seconds between receiver and provided date */ -(double)secondsFrom:(NSDate *)date{ return [self timeIntervalSinceDate:date]; } #pragma mark Time From With Calendar /** * Returns an NSInteger representing the amount of time in years between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - The double representation of the years between receiver and provided date */ -(NSInteger)yearsFrom:(NSDate *)date calendar:(NSCalendar *)calendar{ if (!calendar) { calendar = [[self class] implicitCalendar]; } NSDate *earliest = [self earlierDate:date]; NSDate *latest = (earliest == self) ? date : self; NSInteger multiplier = (earliest == self) ? -1 : 1; NSDateComponents *components = [calendar components:NSCalendarUnitYear fromDate:earliest toDate:latest options:0]; return multiplier*components.year; } /** * Returns an NSInteger representing the amount of time in months between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - The double representation of the months between receiver and provided date */ -(NSInteger)monthsFrom:(NSDate *)date calendar:(NSCalendar *)calendar{ if (!calendar) { calendar = [[self class] implicitCalendar]; } NSDate *earliest = [self earlierDate:date]; NSDate *latest = (earliest == self) ? date : self; NSInteger multiplier = (earliest == self) ? -1 : 1; NSDateComponents *components = [calendar components:allCalendarUnitFlags fromDate:earliest toDate:latest options:0]; return multiplier*(components.month + 12*components.year); } /** * Returns an NSInteger representing the amount of time in weeks between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - The double representation of the weeks between receiver and provided date */ -(NSInteger)weeksFrom:(NSDate *)date calendar:(NSCalendar *)calendar{ if (!calendar) { calendar = [[self class] implicitCalendar]; } NSDate *earliest = [self earlierDate:date]; NSDate *latest = (earliest == self) ? date : self; NSInteger multiplier = (earliest == self) ? -1 : 1; NSDateComponents *components = [calendar components:NSCalendarUnitWeekOfYear fromDate:earliest toDate:latest options:0]; return multiplier*components.weekOfYear; } /** * Returns an NSInteger representing the amount of time in days between the receiver and the provided date. * If the receiver is earlier than the provided date, the returned value will be negative. * * @param date NSDate - The provided date for comparison * @param calendar NSCalendar - The calendar to be used in the calculation * * @return NSInteger - The double representation of the days between receiver and provided date */ -(NSInteger)daysFrom:(NSDate *)date calendar:(NSCalendar *)calendar{ if (!calendar) { calendar = [[self class] implicitCalendar]; } NSDate *earliest = [self earlierDate:date]; NSDate *latest = (earliest == self) ? date : self; NSInteger multiplier = (earliest == self) ? -1 : 1; NSDateComponents *components = [calendar components:NSCalendarUnitDay fromDate:earliest toDate:latest options:0]; return multiplier*components.day; } #pragma mark Time Until /** * Returns the number of years until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return NSInteger representiation of years */ -(NSInteger)yearsUntil{ return [self yearsLaterThan:[NSDate date]]; } /** * Returns the number of months until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return NSInteger representiation of months */ -(NSInteger)monthsUntil{ return [self monthsLaterThan:[NSDate date]]; } /** * Returns the number of weeks until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return NSInteger representiation of weeks */ -(NSInteger)weeksUntil{ return [self weeksLaterThan:[NSDate date]]; } /** * Returns the number of days until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return NSInteger representiation of days */ -(NSInteger)daysUntil{ return [self daysLaterThan:[NSDate date]]; } /** * Returns the number of hours until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return double representiation of hours */ -(double)hoursUntil{ return [self hoursLaterThan:[NSDate date]]; } /** * Returns the number of minutes until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return double representiation of minutes */ -(double)minutesUntil{ return [self minutesLaterThan:[NSDate date]]; } /** * Returns the number of seconds until the receiver's date. Returns 0 if the receiver is the same or earlier than now. * * @return double representiation of seconds */ -(double)secondsUntil{ return [self secondsLaterThan:[NSDate date]]; } #pragma mark Time Ago /** * Returns the number of years the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return NSInteger representiation of years */ -(NSInteger)yearsAgo{ return [self yearsEarlierThan:[NSDate date]]; } /** * Returns the number of months the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return NSInteger representiation of months */ -(NSInteger)monthsAgo{ return [self monthsEarlierThan:[NSDate date]]; } /** * Returns the number of weeks the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return NSInteger representiation of weeks */ -(NSInteger)weeksAgo{ return [self weeksEarlierThan:[NSDate date]]; } /** * Returns the number of days the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return NSInteger representiation of days */ -(NSInteger)daysAgo{ return [self daysEarlierThan:[NSDate date]]; } /** * Returns the number of hours the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return double representiation of hours */ -(double)hoursAgo{ return [self hoursEarlierThan:[NSDate date]]; } /** * Returns the number of minutes the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return double representiation of minutes */ -(double)minutesAgo{ return [self minutesEarlierThan:[NSDate date]]; } /** * Returns the number of seconds the receiver's date is earlier than now. Returns 0 if the receiver is the same or later than now. * * @return double representiation of seconds */ -(double)secondsAgo{ return [self secondsEarlierThan:[NSDate date]]; } #pragma mark Earlier Than /** * Returns the number of years the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of years */ -(NSInteger)yearsEarlierThan:(NSDate *)date{ return ABS(MIN([self yearsFrom:date], 0)); } /** * Returns the number of months the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of months */ -(NSInteger)monthsEarlierThan:(NSDate *)date{ return ABS(MIN([self monthsFrom:date], 0)); } /** * Returns the number of weeks the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of weeks */ -(NSInteger)weeksEarlierThan:(NSDate *)date{ return ABS(MIN([self weeksFrom:date], 0)); } /** * Returns the number of days the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of days */ -(NSInteger)daysEarlierThan:(NSDate *)date{ return ABS(MIN([self daysFrom:date], 0)); } /** * Returns the number of hours the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return double representing the number of hours */ -(double)hoursEarlierThan:(NSDate *)date{ return ABS(MIN([self hoursFrom:date], 0)); } /** * Returns the number of minutes the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return double representing the number of minutes */ -(double)minutesEarlierThan:(NSDate *)date{ return ABS(MIN([self minutesFrom:date], 0)); } /** * Returns the number of seconds the receiver's date is earlier than the provided comparison date. * Returns 0 if the receiver's date is later than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return double representing the number of seconds */ -(double)secondsEarlierThan:(NSDate *)date{ return ABS(MIN([self secondsFrom:date], 0)); } #pragma mark Later Than /** * Returns the number of years the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of years */ -(NSInteger)yearsLaterThan:(NSDate *)date{ return MAX([self yearsFrom:date], 0); } /** * Returns the number of months the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of months */ -(NSInteger)monthsLaterThan:(NSDate *)date{ return MAX([self monthsFrom:date], 0); } /** * Returns the number of weeks the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of weeks */ -(NSInteger)weeksLaterThan:(NSDate *)date{ return MAX([self weeksFrom:date], 0); } /** * Returns the number of days the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return NSInteger representing the number of days */ -(NSInteger)daysLaterThan:(NSDate *)date{ return MAX([self daysFrom:date], 0); } /** * Returns the number of hours the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return double representing the number of hours */ -(double)hoursLaterThan:(NSDate *)date{ return MAX([self hoursFrom:date], 0); } /** * Returns the number of minutes the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return double representing the number of minutes */ -(double)minutesLaterThan:(NSDate *)date{ return MAX([self minutesFrom:date], 0); } /** * Returns the number of seconds the receiver's date is later than the provided comparison date. * Returns 0 if the receiver's date is earlier than or equal to the provided comparison date. * * @param date NSDate - Provided date for comparison * * @return double representing the number of seconds */ -(double)secondsLaterThan:(NSDate *)date{ return MAX([self secondsFrom:date], 0); } #pragma mark Comparators /** * Returns a YES if receiver is earlier than provided comparison date, otherwise returns NO * * @param date NSDate - Provided date for comparison * * @return BOOL representing comparison result */ -(BOOL)isEarlierThan:(NSDate *)date{ if (self.timeIntervalSince1970 < date.timeIntervalSince1970) { return YES; } return NO; } /** * Returns a YES if receiver is later than provided comparison date, otherwise returns NO * * @param date NSDate - Provided date for comparison * * @return BOOL representing comparison result */ -(BOOL)isLaterThan:(NSDate *)date{ if (self.timeIntervalSince1970 > date.timeIntervalSince1970) { return YES; } return NO; } /** * Returns a YES if receiver is earlier than or equal to the provided comparison date, otherwise returns NO * * @param date NSDate - Provided date for comparison * * @return BOOL representing comparison result */ -(BOOL)isEarlierThanOrEqualTo:(NSDate *)date{ if (self.timeIntervalSince1970 <= date.timeIntervalSince1970) { return YES; } return NO; } /** * Returns a YES if receiver is later than or equal to provided comparison date, otherwise returns NO * * @param date NSDate - Provided date for comparison * * @return BOOL representing comparison result */ -(BOOL)isLaterThanOrEqualTo:(NSDate *)date{ if (self.timeIntervalSince1970 >= date.timeIntervalSince1970) { return YES; } return NO; } #pragma mark - Formatted Dates #pragma mark Formatted With Style /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given style * * @param style NSDateFormatterStyle - Desired date formatting style * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style{ return [self formattedDateWithStyle:style timeZone:[NSTimeZone systemTimeZone] locale:[NSLocale autoupdatingCurrentLocale]]; } /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given style and time zone * * @param style NSDateFormatterStyle - Desired date formatting style * @param timeZone NSTimeZone - Desired time zone * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone{ return [self formattedDateWithStyle:style timeZone:timeZone locale:[NSLocale autoupdatingCurrentLocale]]; } /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given style and locale * * @param style NSDateFormatterStyle - Desired date formatting style * @param locale NSLocale - Desired locale * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style locale:(NSLocale *)locale{ return [self formattedDateWithStyle:style timeZone:[NSTimeZone systemTimeZone] locale:locale]; } /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given style, time zone and locale * * @param style NSDateFormatterStyle - Desired date formatting style * @param timeZone NSTimeZone - Desired time zone * @param locale NSLocale - Desired locale * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale{ static NSDateFormatter *formatter = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ formatter = [[NSDateFormatter alloc] init]; }); [formatter setDateStyle:style]; [formatter setTimeZone:timeZone]; [formatter setLocale:locale]; return [formatter stringFromDate:self]; } #pragma mark Formatted With Format /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given date format * * @param format NSString - String representing the desired date format * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithFormat:(NSString *)format{ return [self formattedDateWithFormat:format timeZone:[NSTimeZone systemTimeZone] locale:[NSLocale autoupdatingCurrentLocale]]; } /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given date format and time zone * * @param format NSString - String representing the desired date format * @param timeZone NSTimeZone - Desired time zone * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone{ return [self formattedDateWithFormat:format timeZone:timeZone locale:[NSLocale autoupdatingCurrentLocale]]; } /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given date format and locale * * @param format NSString - String representing the desired date format * @param locale NSLocale - Desired locale * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithFormat:(NSString *)format locale:(NSLocale *)locale{ return [self formattedDateWithFormat:format timeZone:[NSTimeZone systemTimeZone] locale:locale]; } /** * Convenience method that returns a formatted string representing the receiver's date formatted to a given date format, time zone and locale * * @param format NSString - String representing the desired date format * @param timeZone NSTimeZone - Desired time zone * @param locale NSLocale - Desired locale * * @return NSString representing the formatted date string */ -(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale{ static NSDateFormatter *formatter = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ formatter = [[NSDateFormatter alloc] init]; }); [formatter setDateFormat:format]; [formatter setTimeZone:timeZone]; [formatter setLocale:locale]; return [formatter stringFromDate:self]; } #pragma mark - Helpers /** * Class method that returns whether the given year is a leap year for the Gregorian Calendar * Returns YES if year is a leap year, otherwise returns NO * * @param year NSInteger - Year to evaluate * * @return BOOL evaluation of year */ +(BOOL)isLeapYear:(NSInteger)year{ if (year%400){ return YES; } else if (year%100){ return NO; } else if (year%4){ return YES; } return NO; } /** * Retrieves the default calendar identifier used for all non-calendar-specified operations * * @return NSString - NSCalendarIdentifier */ +(NSString *)defaultCalendarIdentifier { return defaultCalendarIdentifier; } /** * Sets the default calendar identifier used for all non-calendar-specified operations * * @param identifier NSString - NSCalendarIdentifier */ + (void)setDefaultCalendarIdentifier:(NSString *)identifier { defaultCalendarIdentifier = [identifier copy]; implicitCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:defaultCalendarIdentifier ?: NSCalendarIdentifierGregorian]; } /** * Retrieves a default NSCalendar instance, based on the value of defaultCalendarSetting * * @return NSCalendar The current implicit calendar */ + (NSCalendar *)implicitCalendar { return implicitCalendar; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/DateTools.podspec ================================================ Pod::Spec.new do |s| s.name = 'DateTools' s.version = '1.7.0' s.summary = 'Dates and time made easy in Objective-C' s.homepage = 'https://github.com/MatthewYork/DateTools' s.description = 'DateTools was written to streamline date and time handling in Objective-C.' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { "Matthew York" => "my3681@gmail.com" } s.source = { :git => "https://github.com/MatthewYork/DateTools.git", :tag => "v#{s.version.to_s}" } s.ios.platform = :ios, '7.0' s.osx.platform = :iox, '10.7' s.requires_arc = true s.source_files = 'DateTools' s.resources = 'DateTools/DateTools.bundle' end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateTools/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier com.molabo.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/AppDelegate.h ================================================ // // AppDelegate.h // DateToolsExample // // Created by Matthew York on 3/19/14. // // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) UITabBarController *tabBarController; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/AppDelegate.m ================================================ // // AppDelegate.m // DateToolsExample // // Created by Matthew York on 3/19/14. // // #import "AppDelegate.h" #import "Colours.h" #import "ExampleNavigationController.h" #import "DateToolsViewController.h" #import "TimePeriodsViewController.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; [self initializeTabBarController]; [self.window setRootViewController:self.tabBarController]; [self.window makeKeyAndVisible]; // Override point for customization after application launch. return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } -(void)initializeTabBarController{ ExampleNavigationController *dtVC = [[ExampleNavigationController alloc] initWithRootViewController:[[DateToolsViewController alloc] initWithNibName:@"DateToolsViewController" bundle:nil]]; ExampleNavigationController *tpVC = [[ExampleNavigationController alloc] initWithRootViewController:[[TimePeriodsViewController alloc] initWithNibName:@"TimePeriodsViewController" bundle:nil]]; //Initialize tab bar controller self.tabBarController = [[UITabBarController alloc] init]; //Style tab bar if ([self.tabBarController.tabBar respondsToSelector:@selector(setTranslucent:)]) { [self.tabBarController.tabBar setTranslucent:NO]; [self.tabBarController.tabBar setTintColor:[UIColor infoBlueColor]]; } else { [self.tabBarController.tabBar setBackgroundColor:[UIColor infoBlueColor]]; } //Add view controllers self.tabBarController.viewControllers = @[dtVC, tpVC]; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/Colours.h ================================================ // Copyright (C) 2013 by Benjamin Gordon // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "TargetConditionals.h" #include #pragma mark - Static String Keys static NSString * kColoursRGBA_R = @"RGBA-r"; static NSString * kColoursRGBA_G = @"RGBA-g"; static NSString * kColoursRGBA_B = @"RGBA-b"; static NSString * kColoursRGBA_A = @"RGBA-a"; static NSString * kColoursHSBA_H = @"HSBA-h"; static NSString * kColoursHSBA_S = @"HSBA-s"; static NSString * kColoursHSBA_B = @"HSBA-b"; static NSString * kColoursHSBA_A = @"HSBA-a"; static NSString * kColoursCIE_L = @"LABa-L"; static NSString * kColoursCIE_A = @"LABa-A"; static NSString * kColoursCIE_B = @"LABa-B"; static NSString * kColoursCIE_alpha = @"LABa-a"; static NSString * kColoursCMYK_C = @"CMYK-c"; static NSString * kColoursCMYK_M = @"CMYK-m"; static NSString * kColoursCMYK_Y = @"CMYK-y"; static NSString * kColoursCMYK_K = @"CMYK-k"; #pragma mark - Create correct iOS/OSX interface #if TARGET_OS_IPHONE #import @interface UIColor (Colours) #elif TARGET_OS_MAC #import @interface NSColor (Colours) #endif #pragma mark - Enums // Color Scheme Generation Enum typedef NS_ENUM(NSInteger, ColorScheme) { ColorSchemeAnalagous, ColorSchemeMonochromatic, ColorSchemeTriad, ColorSchemeComplementary }; // ColorFormulation Type typedef NS_ENUM(NSInteger, ColorFormulation) { ColorFormulationRGBA, ColorFormulationHSBA, ColorFormulationLAB, ColorFormulationCMYK }; // ColorDistance typedef NS_ENUM(NSInteger, ColorDistance) { ColorDistanceCIE76, ColorDistanceCIE94, ColorDistanceCIE2000, }; #pragma mark - Color from Hex/RGBA/HSBA/CIE_LAB/CMYK /** Creates a Color from a Hex representation string @param hexString Hex string that looks like @"#FF0000" or @"FF0000" @return Color */ + (instancetype)colorFromHexString:(NSString *)hexString; /** Creates a Color from an array of 4 NSNumbers (r,g,b,a) @param rgbaArray 4 NSNumbers for rgba between 0 - 1 @return Color */ + (instancetype)colorFromRGBAArray:(NSArray *)rgbaArray; /** Creates a Color from a dictionary of 4 NSNumbers Keys: kColoursRGBA_R, kColoursRGBA_G, kColoursRGBA_B, kColoursRGBA_A @param rgbaDictionary 4 NSNumbers for rgba between 0 - 1 @return Color */ + (instancetype)colorFromRGBADictionary:(NSDictionary *)rgbaDict; /** Creates a Color from an array of 4 NSNumbers (h,s,b,a) @param hsbaArray 4 NSNumbers for rgba between 0 - 1 @return Color */ + (instancetype)colorFromHSBAArray:(NSArray *)hsbaArray; /** Creates a Color from a dictionary of 4 NSNumbers Keys: kColoursHSBA_H, kColoursHSBA_S, kColoursHSBA_B, kColoursHSBA_A @param hsbaDictionary 4 NSNumbers for rgba between 0 - 1 @return Color */ + (instancetype)colorFromHSBADictionary:(NSDictionary *)hsbaDict; /** Creates a Color from an array of 4 NSNumbers (L,a,b,alpha) @param colors 4 NSNumbers for CIE_LAB between 0 - 1 @return Color */ + (instancetype)colorFromCIE_LabArray:(NSArray *)colors; /** Creates a Color from a dictionary of 4 NSNumbers Keys: kColoursCIE_L, kColoursCIE_A, kColoursCIE_B, kColoursCIE_alpha @param colors 4 NSNumbers for CIE_LAB between 0 - 1 @return Color */ + (instancetype)colorFromCIE_LabDictionary:(NSDictionary *)colors; /** Creates a Color from an array of 4 NSNumbers (C,M,Y,K) @param colors 4 NSNumbers for CMYK between 0 - 1 @return Color */ + (instancetype)colorFromCMYKArray:(NSArray *)cmyk; /** Creates a Color from a dictionary of 4 NSNumbers Keys: kColoursCMYK_C, kColoursCMYK_M, kColoursCMYK_Y, kColoursCMYK_K @param colors 4 NSNumbers for CMYK between 0 - 1 @return Color */ + (instancetype)colorFromCMYKDictionary:(NSDictionary *)cmyk; #pragma mark - Hex/RGBA/HSBA/CIE_LAB/CMYK from Color /** Creates a Hex representation from a Color @return NSString */ - (NSString *)hexString; /** Creates an array of 4 NSNumbers representing the float values of r, g, b, a in that order. @return NSArray */ - (NSArray *)rgbaArray; /** Creates an array of 4 NSNumbers representing the float values of h, s, b, a in that order. @return NSArray */ - (NSArray *)hsbaArray; /** Creates a dictionary of 4 NSNumbers representing float values with keys: kColoursRGBA_R, kColoursRGBA_G, kColoursRGBA_B, kColoursRGBA_A @return NSDictionary */ - (NSDictionary *)rgbaDictionary; /** Creates a dictionary of 4 NSNumbers representing float values with keys: kColoursHSBA_H, kColoursHSBA_S, kColoursHSBA_B, kColoursHSBA_A @return NSDictionary */ - (NSDictionary *)hsbaDictionary; /** * Creates an array of 4 NSNumbers representing the float values of L*, a, b, alpha in that order. * * @return NSArray */ - (NSArray *)CIE_LabArray; /** * Creates a dictionary of 4 NSNumbers representing the float values with keys: kColoursCIE_L, kColoursCIE_A, kColoursCIE_B, kColoursCIE_alpha * * @return NSDictionary */ - (NSDictionary *)CIE_LabDictionary; /** * Creates an array of 4 NSNumbers representing the float values of C, M, Y, K in that order. * * @return NSArray */ - (NSArray *)cmykArray; /** * Creates a dictionary of 4 NSNumbers representing the float values with keys: kColoursCMYK_C, kColoursCMYK_M, kColoursCMYK_Y, kColoursCMYK_K * * @return NSDictionary */ - (NSDictionary *)cmykDictionary; #pragma mark - Color Components /** * Creates an NSDictionary with RGBA and HSBA color components inside. * * @return NSDictionary */ - (NSDictionary *)colorComponents; /** * Returns the red value from an RGBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)red; /** * Returns the green value from an RGBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)green; /** * Returns the blue value from an RGBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)blue; /** * Returns the hue value from an HSBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)hue; /** * Returns the saturation value from an HSBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)saturation; /** * Returns the brightness value from an HSBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)brightness; /** * Returns the alpha value from an RGBA formulation of the UIColor. * * @return CGFloat */ - (CGFloat)alpha; /** * Returns the lightness value from a CIELAB formulation of the UIColor. * * @return CGFloat */ - (CGFloat)CIE_Lightness; /** * Returns the a value from a CIELAB formulation of the UIColor. * * @return CGFloat */ - (CGFloat)CIE_a; /** * Returns the b value from a CIELAB formulation of the UIColor. * * @return CGFloat */ - (CGFloat)CIE_b; /** * Returns the cyan value from a CMYK formulation of the UIColor. * * @return CGFloat */ - (CGFloat)cyan; /** * Returns the magenta value from a CMYK formulation of the UIColor. * * @return CGFloat */ - (CGFloat)magenta; /** * Returns the yellow value from a CMYK formulation of the UIColor. * * @return CGFloat */ - (CGFloat)yellow; /** * Returns the black (K) value from a CMYK formulation of the UIColor. * * @return CGFloat */ - (CGFloat)keyBlack; #pragma mark - 4 Color Scheme from Color /** Creates an NSArray of 4 Colors that complement the Color. @param type ColorSchemeAnalagous, ColorSchemeMonochromatic, ColorSchemeTriad, ColorSchemeComplementary @return NSArray */ - (NSArray *)colorSchemeOfType:(ColorScheme)type; #pragma mark - Contrasting Color from Color /** Creates either [Color whiteColor] or [Color blackColor] depending on if the color this method is run on is dark or light. @return Color */ - (instancetype)blackOrWhiteContrastingColor; #pragma mark - Complementary Color /** Creates a complementary color - a color directly opposite it on the color wheel. @return Color */ - (instancetype)complementaryColor; #pragma mark - Distance between Colors /** * Returns a float of the distance between 2 colors. Defaults to the * CIE94 specification found here: http://en.wikipedia.org/wiki/Color_difference * * @param color Color to check self with. * * @return CGFloat */ - (CGFloat)distanceFromColor:(id)color; /** * Returns a float of the distance between 2 colors, using one of * * * @param color Color to check against * @param distanceType Formula to calculate with * * @return CGFloat */ - (CGFloat)distanceFromColor:(id)color type:(ColorDistance)distanceType; #pragma mark - Colors // System Colors + (instancetype)infoBlueColor; + (instancetype)successColor; + (instancetype)warningColor; + (instancetype)dangerColor; // Whites + (instancetype)antiqueWhiteColor; + (instancetype)oldLaceColor; + (instancetype)ivoryColor; + (instancetype)seashellColor; + (instancetype)ghostWhiteColor; + (instancetype)snowColor; + (instancetype)linenColor; // Grays + (instancetype)black25PercentColor; + (instancetype)black50PercentColor; + (instancetype)black75PercentColor; + (instancetype)warmGrayColor; + (instancetype)coolGrayColor; + (instancetype)charcoalColor; // Blues + (instancetype)tealColor; + (instancetype)steelBlueColor; + (instancetype)robinEggColor; + (instancetype)pastelBlueColor; + (instancetype)turquoiseColor; + (instancetype)skyBlueColor; + (instancetype)indigoColor; + (instancetype)denimColor; + (instancetype)blueberryColor; + (instancetype)cornflowerColor; + (instancetype)babyBlueColor; + (instancetype)midnightBlueColor; + (instancetype)fadedBlueColor; + (instancetype)icebergColor; + (instancetype)waveColor; // Greens + (instancetype)emeraldColor; + (instancetype)grassColor; + (instancetype)pastelGreenColor; + (instancetype)seafoamColor; + (instancetype)paleGreenColor; + (instancetype)cactusGreenColor; + (instancetype)chartreuseColor; + (instancetype)hollyGreenColor; + (instancetype)oliveColor; + (instancetype)oliveDrabColor; + (instancetype)moneyGreenColor; + (instancetype)honeydewColor; + (instancetype)limeColor; + (instancetype)cardTableColor; // Reds + (instancetype)salmonColor; + (instancetype)brickRedColor; + (instancetype)easterPinkColor; + (instancetype)grapefruitColor; + (instancetype)pinkColor; + (instancetype)indianRedColor; + (instancetype)strawberryColor; + (instancetype)coralColor; + (instancetype)maroonColor; + (instancetype)watermelonColor; + (instancetype)tomatoColor; + (instancetype)pinkLipstickColor; + (instancetype)paleRoseColor; + (instancetype)crimsonColor; // Purples + (instancetype)eggplantColor; + (instancetype)pastelPurpleColor; + (instancetype)palePurpleColor; + (instancetype)coolPurpleColor; + (instancetype)violetColor; + (instancetype)plumColor; + (instancetype)lavenderColor; + (instancetype)raspberryColor; + (instancetype)fuschiaColor; + (instancetype)grapeColor; + (instancetype)periwinkleColor; + (instancetype)orchidColor; // Yellows + (instancetype)goldenrodColor; + (instancetype)yellowGreenColor; + (instancetype)bananaColor; + (instancetype)mustardColor; + (instancetype)buttermilkColor; + (instancetype)goldColor; + (instancetype)creamColor; + (instancetype)lightCreamColor; + (instancetype)wheatColor; + (instancetype)beigeColor; // Oranges + (instancetype)peachColor; + (instancetype)burntOrangeColor; + (instancetype)pastelOrangeColor; + (instancetype)cantaloupeColor; + (instancetype)carrotColor; + (instancetype)mandarinColor; // Browns + (instancetype)chiliPowderColor; + (instancetype)burntSiennaColor; + (instancetype)chocolateColor; + (instancetype)coffeeColor; + (instancetype)cinnamonColor; + (instancetype)almondColor; + (instancetype)eggshellColor; + (instancetype)sandColor; + (instancetype)mudColor; + (instancetype)siennaColor; + (instancetype)dustColor; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/Colours.m ================================================ // Copyright (C) 2013 by Benjamin Gordon // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and // associated documentation files (the "Software"), to // deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall // be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "Colours.h" // Swizzle #import #pragma mark - Create correct iOS/OSX implementation #if TARGET_OS_IPHONE #import @implementation UIColor (Colours) #elif TARGET_OS_MAC #import @implementation NSColor (Colours) #endif #pragma mark - Color from Hex + (instancetype)colorFromHexString:(NSString *)hexString { unsigned rgbValue = 0; hexString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]; NSScanner *scanner = [NSScanner scannerWithString:hexString]; [scanner scanHexInt:&rgbValue]; return [[self class] colorWithR:((rgbValue & 0xFF0000) >> 16) G:((rgbValue & 0xFF00) >> 8) B:(rgbValue & 0xFF) A:1.0]; } #pragma mark - Hex from Color - (NSString *)hexString { NSArray *colorArray = [self rgbaArray]; int r = [colorArray[0] floatValue] * 255; int g = [colorArray[1] floatValue] * 255; int b = [colorArray[2] floatValue] * 255; NSString *red = [NSString stringWithFormat:@"%02x", r]; NSString *green = [NSString stringWithFormat:@"%02x", g]; NSString *blue = [NSString stringWithFormat:@"%02x", b]; return [NSString stringWithFormat:@"#%@%@%@", red, green, blue]; } #pragma mark - Color from RGBA + (instancetype)colorFromRGBAArray:(NSArray *)rgbaArray { if (rgbaArray.count < 4) { return [[self class] clearColor]; } return [[self class] colorWithRed:[rgbaArray[0] floatValue] green:[rgbaArray[1] floatValue] blue:[rgbaArray[2] floatValue] alpha:[rgbaArray[3] floatValue]]; } + (instancetype)colorFromRGBADictionary:(NSDictionary *)rgbaDict { if (rgbaDict[kColoursRGBA_R] && rgbaDict[kColoursRGBA_G] && rgbaDict[kColoursRGBA_B] && rgbaDict[kColoursRGBA_A]) { return [[self class] colorWithRed:[rgbaDict[kColoursRGBA_R] floatValue] green:[rgbaDict[kColoursRGBA_G] floatValue] blue:[rgbaDict[kColoursRGBA_B] floatValue] alpha:[rgbaDict[kColoursRGBA_A] floatValue]]; } return [[self class] clearColor]; } #pragma mark - RGBA from Color - (NSArray *)rgbaArray { CGFloat r=0,g=0,b=0,a=0; if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) { [self getRed:&r green:&g blue:&b alpha:&a]; } else { const CGFloat *components = CGColorGetComponents(self.CGColor); r = components[0]; g = components[1]; b = components[2]; a = components[3]; } return @[@(r), @(g), @(b), @(a)]; } - (NSDictionary *)rgbaDictionary { CGFloat r=0,g=0,b=0,a=0; if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) { [self getRed:&r green:&g blue:&b alpha:&a]; } else { const CGFloat *components = CGColorGetComponents(self.CGColor); r = components[0]; g = components[1]; b = components[2]; a = components[3]; } return @{kColoursRGBA_R:@(r), kColoursRGBA_G:@(g), kColoursRGBA_B:@(b), kColoursRGBA_A:@(a)}; } #pragma mark - HSBA from Color - (NSArray *)hsbaArray { // Takes a [self class] and returns Hue,Saturation,Brightness,Alpha values in NSNumber form CGFloat h=0,s=0,b=0,a=0; if ([self respondsToSelector:@selector(getHue:saturation:brightness:alpha:)]) { [self getHue:&h saturation:&s brightness:&b alpha:&a]; } return @[@(h), @(s), @(b), @(a)]; } - (NSDictionary *)hsbaDictionary { CGFloat h=0,s=0,b=0,a=0; if ([self respondsToSelector:@selector(getHue:saturation:brightness:alpha:)]) { [self getHue:&h saturation:&s brightness:&b alpha:&a]; } return @{kColoursHSBA_H:@(h), kColoursHSBA_S:@(s), kColoursHSBA_B:@(b), kColoursHSBA_A:@(a)}; } #pragma mark - Color from HSBA + (instancetype)colorFromHSBAArray:(NSArray *)hsbaArray { if (hsbaArray.count < 4) { return [[self class] clearColor]; } return [[self class] colorWithHue:[hsbaArray[0] doubleValue] saturation:[hsbaArray[1] doubleValue] brightness:[hsbaArray[2] doubleValue] alpha:[hsbaArray[3] doubleValue]]; } + (instancetype)colorFromHSBADictionary:(NSDictionary *)hsbaDict { if (hsbaDict[kColoursHSBA_H] && hsbaDict[kColoursHSBA_S] && hsbaDict[kColoursHSBA_B] && hsbaDict[kColoursHSBA_A]) { return [[self class] colorWithHue:[hsbaDict[kColoursHSBA_H] doubleValue] saturation:[hsbaDict[kColoursHSBA_S] doubleValue] brightness:[hsbaDict[kColoursHSBA_B] doubleValue] alpha:[hsbaDict[kColoursHSBA_A] doubleValue]]; } return [[self class] clearColor]; } #pragma mark - LAB from Color - (NSArray *)CIE_LabArray { // Convert Color to XYZ format first NSArray *rgba = [self rgbaArray]; CGFloat R = [rgba[0] floatValue]; CGFloat G = [rgba[1] floatValue]; CGFloat B = [rgba[2] floatValue]; // Create deltaR block void (^deltaRGB)(CGFloat *R); deltaRGB = ^(CGFloat *R) { *R = (*R > 0.04045) ? pow((*R + 0.055)/1.055, 2.40) : (*R/12.92); }; deltaRGB(&R); deltaRGB(&G); deltaRGB(&B); CGFloat X = R*41.24 + G*35.76 + B*18.05; CGFloat Y = R*21.26 + G*71.52 + B*7.22; CGFloat Z = R*1.93 + G*11.92 + B*95.05; // Convert XYZ to L*a*b* X = X/95.047; Y = Y/100.000; Z = Z/108.883; // Create deltaF block void (^deltaF)(CGFloat *f); deltaF = ^(CGFloat *f){ *f = (*f > pow((6.0/29.0), 3.0)) ? pow(*f, 1.0/3.0) : (1/3)*pow((29.0/6.0), 2.0) * *f + 4/29.0; }; deltaF(&X); deltaF(&Y); deltaF(&Z); NSNumber *L = @(116*Y - 16); NSNumber *a = @(500 * (X - Y)); NSNumber *b = @(200 * (Y - Z)); return @[L, a, b, rgba[3]]; } - (NSDictionary *)CIE_LabDictionary { NSArray *colors = [self CIE_LabArray]; return @{kColoursCIE_L:colors[0], kColoursCIE_A:colors[1], kColoursCIE_B:colors[2], kColoursCIE_alpha:colors[3],}; } #pragma mark - Color from LAB + (instancetype)colorFromCIE_LabArray:(NSArray *)colors { if (!colors || colors.count < 4) { return [[self class] clearColor]; } // Convert LAB to XYZ CGFloat L = [colors[0] floatValue]; CGFloat A = [colors[1] floatValue]; CGFloat B = [colors[2] floatValue]; CGFloat Y = (L + 16.0)/116.0; CGFloat X = A/500 + Y; CGFloat Z = Y - B/200; void (^deltaXYZ)(CGFloat *); deltaXYZ = ^(CGFloat *k){ *k = (pow(*k, 3.0) > 0.008856) ? pow(*k, 3.0) : (*k - 4/29.0)/7.787; }; deltaXYZ(&X); deltaXYZ(&Y); deltaXYZ(&Z); X = X*.95047; Y = Y*1.00000; Z = Z*1.08883; // Convert XYZ to RGB CGFloat R = X*3.2406 + Y*-1.5372 + Z*-0.4986; CGFloat G = X*-0.9689 + Y*1.8758 + Z*0.0415; CGFloat _B = X*0.0557 + Y*-0.2040 + Z*1.0570; void (^deltaRGB)(CGFloat *); deltaRGB = ^(CGFloat *k){ *k = (*k > 0.0031308) ? 1.055 * (pow(*k, (1/2.4))) - 0.055 : *k * 12.92; }; deltaRGB(&R); deltaRGB(&G); deltaRGB(&_B); // return Color return [[self class] colorFromRGBAArray:@[@(R), @(G), @(_B), colors[3]]]; } + (instancetype)colorFromCIE_LabDictionary:(NSDictionary *)colors { if (!colors) { return [[self class] clearColor]; } if (colors[kColoursCIE_L] && colors[kColoursCIE_A] && colors[kColoursCIE_B] && colors[kColoursCIE_alpha]) { return [self colorFromCIE_LabArray:@[colors[kColoursCIE_L], colors[kColoursCIE_A], colors[kColoursCIE_B], colors[kColoursCIE_alpha]]]; } return [[self class] clearColor]; } #pragma mark - Color to CMYK - (NSArray *)cmykArray { // Convert RGB to CMY NSArray *rgb = [self rgbaArray]; CGFloat C = 1 - [rgb[0] floatValue]; CGFloat M = 1 - [rgb[1] floatValue]; CGFloat Y = 1 - [rgb[2] floatValue]; // Find K CGFloat K = MIN(1, MIN(C, MIN(Y, M))); if (K == 1) { C = 0; M = 0; Y = 0; } else { void (^newCMYK)(CGFloat *); newCMYK = ^(CGFloat *x){ *x = (*x - K)/(1 - K); }; newCMYK(&C); newCMYK(&M); newCMYK(&Y); } return @[@(C), @(M), @(Y), @(K)]; } - (NSDictionary *)cmykDictionary { NSArray *colors = [self cmykArray]; return @{kColoursCMYK_C:colors[0], kColoursCMYK_M:colors[1], kColoursCMYK_Y:colors[2], kColoursCMYK_K:colors[3]}; } #pragma mark - CMYK to Color + (instancetype)colorFromCMYKArray:(NSArray *)cmyk { if (!cmyk || cmyk.count < 4) { return [[self class] clearColor]; } // Find CMY values CGFloat C = [cmyk[0] floatValue]; CGFloat M = [cmyk[1] floatValue]; CGFloat Y = [cmyk[2] floatValue]; CGFloat K = [cmyk[3] floatValue]; void (^cmyTransform)(CGFloat *); cmyTransform = ^(CGFloat *x){ *x = *x * (1 - K) + K; }; cmyTransform(&C); cmyTransform(&M); cmyTransform(&Y); // Translate CMY to RGB CGFloat R = 1 - C; CGFloat G = 1 - M; CGFloat B = 1 - Y; // return the Color return [[self class] colorFromRGBAArray:@[@(R), @(G), @(B), @(1)]]; } + (instancetype)colorFromCMYKDictionary:(NSDictionary *)cmyk { if (!cmyk) { return [[self class] clearColor]; } if (cmyk[kColoursCMYK_C] && cmyk[kColoursCMYK_M] && cmyk[kColoursCMYK_Y] && cmyk[kColoursCMYK_K]) { return [[self class] colorFromCMYKArray:@[cmyk[kColoursCMYK_C], cmyk[kColoursCMYK_M], cmyk[kColoursCMYK_Y], cmyk[kColoursCMYK_K]]]; } return [[self class] clearColor]; } #pragma mark - Color Components - (NSDictionary *)colorComponents { NSMutableDictionary *components = [[self rgbaDictionary] mutableCopy]; [components addEntriesFromDictionary:[self hsbaDictionary]]; [components addEntriesFromDictionary:[self CIE_LabDictionary]]; return components; } - (CGFloat)red { return [[self rgbaArray][0] floatValue]; } - (CGFloat)green { return [[self rgbaArray][1] floatValue]; } - (CGFloat)blue { return [[self rgbaArray][2] floatValue]; } - (CGFloat)hue { return [[self hsbaArray][0] floatValue]; } - (CGFloat)saturation { return [[self hsbaArray][1] floatValue]; } - (CGFloat)brightness { return [[self hsbaArray][2] floatValue]; } - (CGFloat)alpha { return [[self rgbaArray][3] floatValue]; } - (CGFloat)CIE_Lightness { return [[self CIE_LabArray][0] floatValue]; } - (CGFloat)CIE_a { return [[self CIE_LabArray][1] floatValue]; } - (CGFloat)CIE_b { return [[self CIE_LabArray][2] floatValue]; } - (CGFloat)cyan { return [[self cmykArray][0] floatValue]; } - (CGFloat)magenta { return [[self cmykArray][1] floatValue]; } - (CGFloat)yellow { return [[self cmykArray][2] floatValue]; } - (CGFloat)keyBlack { return [[self cmykArray][3] floatValue]; } #pragma mark - Generate Color Scheme - (NSArray *)colorSchemeOfType:(ColorScheme)type { NSArray *hsbArray = [self hsbaArray]; float hue = [hsbArray[0] floatValue] * 360; float sat = [hsbArray[1] floatValue] * 100; float bright = [hsbArray[2] floatValue] * 100; float alpha = [hsbArray[3] floatValue]; switch (type) { case ColorSchemeAnalagous: return [[self class] analagousColorsFromHue:hue saturation:sat brightness:bright alpha:alpha]; case ColorSchemeMonochromatic: return [[self class] monochromaticColorsFromHue:hue saturation:sat brightness:bright alpha:alpha]; case ColorSchemeTriad: return [[self class] triadColorsFromHue:hue saturation:sat brightness:bright alpha:alpha]; case ColorSchemeComplementary: return [[self class] complementaryColorsFromHue:hue saturation:sat brightness:bright alpha:alpha]; default: return nil; } } #pragma mark - Color Scheme Generation - Helper methods + (NSArray *)analagousColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a { return @[[[self class] colorWithHue:[[self class] addDegrees:30 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-10)/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:15 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-5)/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:-15 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-5)/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:-30 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-10)/100 alpha:a]]; } + (NSArray *)monochromaticColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a { return @[[[self class] colorWithHue:h/360 saturation:(s/2)/100 brightness:(b/3)/100 alpha:a], [[self class] colorWithHue:h/360 saturation:s/100 brightness:(b/2)/100 alpha:a], [[self class] colorWithHue:h/360 saturation:(s/3)/100 brightness:(2*b/3)/100 alpha:a], [[self class] colorWithHue:h/360 saturation:s/100 brightness:(4*b/5)/100 alpha:a]]; } + (NSArray *)triadColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a { return @[[[self class] colorWithHue:[[self class] addDegrees:120 toDegree:h]/360 saturation:(7*s/6)/100 brightness:(b-5)/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:120 toDegree:h]/360 saturation:s/100 brightness:b/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:240 toDegree:h]/360 saturation:s/100 brightness:b/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:240 toDegree:h]/360 saturation:(7*s/6)/100 brightness:(b-5)/100 alpha:a]]; } + (NSArray *)complementaryColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a { return @[[[self class] colorWithHue:h/360 saturation:s/100 brightness:(4*b/5)/100 alpha:a], [[self class] colorWithHue:h/360 saturation:(5*s/7)/100 brightness:b/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:180 toDegree:h]/360 saturation:s/100 brightness:b/100 alpha:a], [[self class] colorWithHue:[[self class] addDegrees:180 toDegree:h]/360 saturation:(5*s/7)/100 brightness:b/100 alpha:a]]; } #pragma mark - Contrasting Color - (instancetype)blackOrWhiteContrastingColor { NSArray *rgbaArray = [self rgbaArray]; double a = 1 - ((0.299 * [rgbaArray[0] doubleValue]) + (0.587 * [rgbaArray[1] doubleValue]) + (0.114 * [rgbaArray[2] doubleValue])); return a < 0.5 ? [[self class] blackColor] : [[self class] whiteColor]; } #pragma mark - Complementary Color - (instancetype)complementaryColor { NSMutableDictionary *hsba = [[self hsbaDictionary] mutableCopy]; float newH = [[self class] addDegrees:180.0f toDegree:([hsba[kColoursHSBA_H] floatValue]*360)]; [hsba setObject:@(newH) forKey:kColoursHSBA_H]; return [[self class] colorFromHSBADictionary:hsba]; } #pragma mark - Distance between Colors - (CGFloat)distanceFromColor:(id)color { // Defaults to CIE94 return [self distanceFromColor:color type:ColorDistanceCIE94]; } - (CGFloat)distanceFromColor:(id)color type:(ColorDistance)distanceType { /** * * Detecting a difference in two colors is not as trivial as it sounds. * One's first instinct is to go for a difference in RGB values, leaving * you with a sum of the differences of each point. It looks great! Until * you actually start comparing colors. Why do these two reds have a different * distance than these two blues *in real life* vs computationally? * Human visual perception is next in the line of things between a color * and your brain. Some colors are just perceived to have larger variants inside * of their respective areas than others, so we need a way to model this * human variable to colors. Enter CIELAB. This color formulation is supposed to be * this model. So now we need to standardize a unit of distance between any two * colors that works independent of how humans visually perceive that distance. * Enter CIE76,94,2000. These are methods that use user-tested data and other * mathematically and statistically significant correlations to output this info. * You can read the wiki articles below to get a better understanding historically * of how we moved to newer and better color distance formulas, and what * their respective pros/cons are. * * References: * * http://en.wikipedia.org/wiki/Color_difference * http://en.wikipedia.org/wiki/Just_noticeable_difference * http://en.wikipedia.org/wiki/CIELAB * */ // Check if it's a color if (![color isKindOfClass:[self class]]) { // NSLog(@"Not a %@ object.", NSStringFromClass([self class])); return MAXFLOAT; } // Set Up Common Variables NSArray *lab1 = [self CIE_LabArray]; NSArray *lab2 = [color CIE_LabArray]; CGFloat L1 = [lab1[0] floatValue]; CGFloat A1 = [lab1[1] floatValue]; CGFloat B1 = [lab1[2] floatValue]; CGFloat L2 = [lab2[0] floatValue]; CGFloat A2 = [lab2[1] floatValue]; CGFloat B2 = [lab2[2] floatValue]; // CIE76 first if (distanceType == ColorDistanceCIE76) { CGFloat distance = sqrtf(pow((L1-L2), 2) + pow((A1-A2), 2) + pow((B1-B2), 2)); return distance; } // More Common Variables CGFloat kL = 1; CGFloat kC = 1; CGFloat kH = 1; CGFloat k1 = 0.045; CGFloat k2 = 0.015; CGFloat deltaL = L1 - L2; CGFloat C1 = sqrt((A1*A1) + (B1*B1)); CGFloat C2 = sqrt((A2*A2) + (B2*B2)); CGFloat deltaC = C1 - C2; CGFloat deltaH = sqrt(pow((A1-A2), 2.0) + pow((B1-B2), 2.0) - pow(deltaC, 2.0)); CGFloat sL = 1; CGFloat sC = 1 + k1*(sqrt((A1*A1) + (B1*B1))); CGFloat sH = 1 + k2*(sqrt((A1*A1) + (B1*B1))); // CIE94 if (distanceType == ColorDistanceCIE94) { return sqrt(pow((deltaL/(kL*sL)), 2.0) + pow((deltaC/(kC*sC)), 2.0) + pow((deltaH/(kH*sH)), 2.0)); } // CIE2000 // More variables CGFloat deltaLPrime = L2 - L1; CGFloat meanL = (L1 + L2)/2; CGFloat meanC = (C1 + C2)/2; CGFloat aPrime1 = A1 + A1/2*(1 - sqrt(pow(meanC, 7.0)/(pow(meanC, 7.0) + pow(25.0, 7.0)))); CGFloat aPrime2 = A2 + A2/2*(1 - sqrt(pow(meanC, 7.0)/(pow(meanC, 7.0) + pow(25.0, 7.0)))); CGFloat cPrime1 = sqrt((aPrime1*aPrime1) + (B1*B1)); CGFloat cPrime2 = sqrt((aPrime2*aPrime2) + (B2*B2)); CGFloat cMeanPrime = (cPrime1 + cPrime2)/2; CGFloat deltaCPrime = cPrime1 - cPrime2; CGFloat hPrime1 = atan2(B1, aPrime1); CGFloat hPrime2 = atan2(B2, aPrime2); hPrime1 = fmodf(hPrime1, [self radiansFromDegree:360]); hPrime2 = fmodf(hPrime2, [self radiansFromDegree:360]); CGFloat deltahPrime = 0; if (fabsf(hPrime1 - hPrime2) <= [self radiansFromDegree:180]) { deltahPrime = hPrime2 - hPrime1; } else { deltahPrime = (hPrime2 <= hPrime1) ? hPrime2 - hPrime1 + [self radiansFromDegree:360] : hPrime2 - hPrime1 - [self radiansFromDegree:360]; } CGFloat deltaHPrime = 2 * sqrt(cPrime1*cPrime2) * sin(deltahPrime/2); CGFloat meanHPrime = (fabsf(hPrime1 - hPrime2) <= [self radiansFromDegree:180]) ? (hPrime1 + hPrime2)/2 : (hPrime1 + hPrime2 + [self radiansFromDegree:360])/2; CGFloat T = 1 - 0.17*cos(meanHPrime - [self radiansFromDegree:30]) + 0.24*cos(2*meanHPrime)+0.32*cos(3*meanHPrime + [self radiansFromDegree:6]) - 0.20*cos(4*meanHPrime - [self radiansFromDegree:63]); sL = 1 + (0.015 * pow((meanL - 50), 2))/sqrt(20 + pow((meanL - 50), 2)); sC = 1 + 0.045*cMeanPrime; sH = 1 + 0.015*cMeanPrime*T; CGFloat Rt = -2 * sqrt(pow(cMeanPrime, 7)/(pow(cMeanPrime, 7) + pow(25.0, 7))) * sin([self radiansFromDegree:60]* exp(-1 * pow((meanHPrime - [self radiansFromDegree:275])/[self radiansFromDegree:25], 2))); // Finally return CIE2000 distance return sqrt(pow((deltaLPrime/(kL*sL)), 2) + pow((deltaCPrime/(kC*sC)), 2) + pow((deltaHPrime/(kH*sH)), 2) + Rt*(deltaC/(kC*sC))*(deltaHPrime/(kH*sH))); } #pragma mark - System Colors + (instancetype)infoBlueColor { return [[self class] colorWithR:47 G:112 B:225 A:1.0]; } + (instancetype)successColor { return [[self class] colorWithR:83 G:215 B:106 A:1.0]; } + (instancetype)warningColor { return [[self class] colorWithR:221 G:170 B:59 A:1.0]; } + (instancetype)dangerColor { return [[self class] colorWithR:229 G:0 B:15 A:1.0]; } #pragma mark - Whites + (instancetype)antiqueWhiteColor { return [[self class] colorWithR:250 G:235 B:215 A:1.0]; } + (instancetype)oldLaceColor { return [[self class] colorWithR:253 G:245 B:230 A:1.0]; } + (instancetype)ivoryColor { return [[self class] colorWithR:255 G:255 B:240 A:1.0]; } + (instancetype)seashellColor { return [[self class] colorWithR:255 G:245 B:238 A:1.0]; } + (instancetype)ghostWhiteColor { return [[self class] colorWithR:248 G:248 B:255 A:1.0]; } + (instancetype)snowColor { return [[self class] colorWithR:255 G:250 B:250 A:1.0]; } + (instancetype)linenColor { return [[self class] colorWithR:250 G:240 B:230 A:1.0]; } #pragma mark - Grays + (instancetype)black25PercentColor { return [[self class] colorWithWhite:0.25 alpha:1.0]; } + (instancetype)black50PercentColor { return [[self class] colorWithWhite:0.5 alpha:1.0]; } + (instancetype)black75PercentColor { return [[self class] colorWithWhite:0.75 alpha:1.0]; } + (instancetype)warmGrayColor { return [[self class] colorWithR:133 G:117 B:112 A:1.0]; } + (instancetype)coolGrayColor { return [[self class] colorWithR:118 G:122 B:133 A:1.0]; } + (instancetype)charcoalColor { return [[self class] colorWithR:34 G:34 B:34 A:1.0]; } #pragma mark - Blues + (instancetype)tealColor { return [[self class] colorWithR:28 G:160 B:170 A:1.0]; } + (instancetype)steelBlueColor { return [[self class] colorWithR:103 G:153 B:170 A:1.0]; } + (instancetype)robinEggColor { return [[self class] colorWithR:141 G:218 B:247 A:1.0]; } + (instancetype)pastelBlueColor { return [[self class] colorWithR:99 G:161 B:247 A:1.0]; } + (instancetype)turquoiseColor { return [[self class] colorWithR:112 G:219 B:219 A:1.0]; } + (instancetype)skyBlueColor { return [[self class] colorWithR:0 G:178 B:238 A:1.0]; } + (instancetype)indigoColor { return [[self class] colorWithR:13 G:79 B:139 A:1.0]; } + (instancetype)denimColor { return [[self class] colorWithR:67 G:114 B:170 A:1.0]; } + (instancetype)blueberryColor { return [[self class] colorWithR:89 G:113 B:173 A:1.0]; } + (instancetype)cornflowerColor { return [[self class] colorWithR:100 G:149 B:237 A:1.0]; } + (instancetype)babyBlueColor { return [[self class] colorWithR:190 G:220 B:230 A:1.0]; } + (instancetype)midnightBlueColor { return [[self class] colorWithR:13 G:26 B:35 A:1.0]; } + (instancetype)fadedBlueColor { return [[self class] colorWithR:23 G:137 B:155 A:1.0]; } + (instancetype)icebergColor { return [[self class] colorWithR:200 G:213 B:219 A:1.0]; } + (instancetype)waveColor { return [[self class] colorWithR:102 G:169 B:251 A:1.0]; } #pragma mark - Greens + (instancetype)emeraldColor { return [[self class] colorWithR:1 G:152 B:117 A:1.0]; } + (instancetype)grassColor { return [[self class] colorWithR:99 G:214 B:74 A:1.0]; } + (instancetype)pastelGreenColor { return [[self class] colorWithR:126 G:242 B:124 A:1.0]; } + (instancetype)seafoamColor { return [[self class] colorWithR:77 G:226 B:140 A:1.0]; } + (instancetype)paleGreenColor { return [[self class] colorWithR:176 G:226 B:172 A:1.0]; } + (instancetype)cactusGreenColor { return [[self class] colorWithR:99 G:111 B:87 A:1.0]; } + (instancetype)chartreuseColor { return [[self class] colorWithR:69 G:139 B:0 A:1.0]; } + (instancetype)hollyGreenColor { return [[self class] colorWithR:32 G:87 B:14 A:1.0]; } + (instancetype)oliveColor { return [[self class] colorWithR:91 G:114 B:34 A:1.0]; } + (instancetype)oliveDrabColor { return [[self class] colorWithR:107 G:142 B:35 A:1.0]; } + (instancetype)moneyGreenColor { return [[self class] colorWithR:134 G:198 B:124 A:1.0]; } + (instancetype)honeydewColor { return [[self class] colorWithR:216 G:255 B:231 A:1.0]; } + (instancetype)limeColor { return [[self class] colorWithR:56 G:237 B:56 A:1.0]; } + (instancetype)cardTableColor { return [[self class] colorWithR:87 G:121 B:107 A:1.0]; } #pragma mark - Reds + (instancetype)salmonColor { return [[self class] colorWithR:233 G:87 B:95 A:1.0]; } + (instancetype)brickRedColor { return [[self class] colorWithR:151 G:27 B:16 A:1.0]; } + (instancetype)easterPinkColor { return [[self class] colorWithR:241 G:167 B:162 A:1.0]; } + (instancetype)grapefruitColor { return [[self class] colorWithR:228 G:31 B:54 A:1.0]; } + (instancetype)pinkColor { return [[self class] colorWithR:255 G:95 B:154 A:1.0]; } + (instancetype)indianRedColor { return [[self class] colorWithR:205 G:92 B:92 A:1.0]; } + (instancetype)strawberryColor { return [[self class] colorWithR:190 G:38 B:37 A:1.0]; } + (instancetype)coralColor { return [[self class] colorWithR:240 G:128 B:128 A:1.0]; } + (instancetype)maroonColor { return [[self class] colorWithR:80 G:4 B:28 A:1.0]; } + (instancetype)watermelonColor { return [[self class] colorWithR:242 G:71 B:63 A:1.0]; } + (instancetype)tomatoColor { return [[self class] colorWithR:255 G:99 B:71 A:1.0]; } + (instancetype)pinkLipstickColor { return [[self class] colorWithR:255 G:105 B:180 A:1.0]; } + (instancetype)paleRoseColor { return [[self class] colorWithR:255 G:228 B:225 A:1.0]; } + (instancetype)crimsonColor { return [[self class] colorWithR:187 G:18 B:36 A:1.0]; } #pragma mark - Purples + (instancetype)eggplantColor { return [[self class] colorWithR:105 G:5 B:98 A:1.0]; } + (instancetype)pastelPurpleColor { return [[self class] colorWithR:207 G:100 B:235 A:1.0]; } + (instancetype)palePurpleColor { return [[self class] colorWithR:229 G:180 B:235 A:1.0]; } + (instancetype)coolPurpleColor { return [[self class] colorWithR:140 G:93 B:228 A:1.0]; } + (instancetype)violetColor { return [[self class] colorWithR:191 G:95 B:255 A:1.0]; } + (instancetype)plumColor { return [[self class] colorWithR:139 G:102 B:139 A:1.0]; } + (instancetype)lavenderColor { return [[self class] colorWithR:204 G:153 B:204 A:1.0]; } + (instancetype)raspberryColor { return [[self class] colorWithR:135 G:38 B:87 A:1.0]; } + (instancetype)fuschiaColor { return [[self class] colorWithR:255 G:20 B:147 A:1.0]; } + (instancetype)grapeColor { return [[self class] colorWithR:54 G:11 B:88 A:1.0]; } + (instancetype)periwinkleColor { return [[self class] colorWithR:135 G:159 B:237 A:1.0]; } + (instancetype)orchidColor { return [[self class] colorWithR:218 G:112 B:214 A:1.0]; } #pragma mark - Yellows + (instancetype)goldenrodColor { return [[self class] colorWithR:215 G:170 B:51 A:1.0]; } + (instancetype)yellowGreenColor { return [[self class] colorWithR:192 G:242 B:39 A:1.0]; } + (instancetype)bananaColor { return [[self class] colorWithR:229 G:227 B:58 A:1.0]; } + (instancetype)mustardColor { return [[self class] colorWithR:205 G:171 B:45 A:1.0]; } + (instancetype)buttermilkColor { return [[self class] colorWithR:254 G:241 B:181 A:1.0]; } + (instancetype)goldColor { return [[self class] colorWithR:139 G:117 B:18 A:1.0]; } + (instancetype)creamColor { return [[self class] colorWithR:240 G:226 B:187 A:1.0]; } + (instancetype)lightCreamColor { return [[self class] colorWithR:240 G:238 B:215 A:1.0]; } + (instancetype)wheatColor { return [[self class] colorWithR:240 G:238 B:215 A:1.0]; } + (instancetype)beigeColor { return [[self class] colorWithR:245 G:245 B:220 A:1.0]; } #pragma mark - Oranges + (instancetype)peachColor { return [[self class] colorWithR:242 G:187 B:97 A:1.0]; } + (instancetype)burntOrangeColor { return [[self class] colorWithR:184 G:102 B:37 A:1.0]; } + (instancetype)pastelOrangeColor { return [[self class] colorWithR:248 G:197 B:143 A:1.0]; } + (instancetype)cantaloupeColor { return [[self class] colorWithR:250 G:154 B:79 A:1.0]; } + (instancetype)carrotColor { return [[self class] colorWithR:237 G:145 B:33 A:1.0]; } + (instancetype)mandarinColor { return [[self class] colorWithR:247 G:145 B:55 A:1.0]; } #pragma mark - Browns + (instancetype)chiliPowderColor { return [[self class] colorWithR:199 G:63 B:23 A:1.0]; } + (instancetype)burntSiennaColor { return [[self class] colorWithR:138 G:54 B:15 A:1.0]; } + (instancetype)chocolateColor { return [[self class] colorWithR:94 G:38 B:5 A:1.0]; } + (instancetype)coffeeColor { return [[self class] colorWithR:141 G:60 B:15 A:1.0]; } + (instancetype)cinnamonColor { return [[self class] colorWithR:123 G:63 B:9 A:1.0]; } + (instancetype)almondColor { return [[self class] colorWithR:196 G:142 B:72 A:1.0]; } + (instancetype)eggshellColor { return [[self class] colorWithR:252 G:230 B:201 A:1.0]; } + (instancetype)sandColor { return [[self class] colorWithR:222 G:182 B:151 A:1.0]; } + (instancetype)mudColor { return [[self class] colorWithR:70 G:45 B:29 A:1.0]; } + (instancetype)siennaColor { return [[self class] colorWithR:160 G:82 B:45 A:1.0]; } + (instancetype)dustColor { return [[self class] colorWithR:236 G:214 B:197 A:1.0]; } #pragma mark - Private #pragma mark - RGBA Helper method + (instancetype)colorWithR:(CGFloat)red G:(CGFloat)green B:(CGFloat)blue A:(CGFloat)alpha { return [[self class] colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha]; } #pragma mark - Degrees Helper method for Color Schemes + (float)addDegrees:(float)addDeg toDegree:(float)staticDeg { staticDeg += addDeg; if (staticDeg > 360) { float offset = staticDeg - 360; return offset; } else if (staticDeg < 0) { return -1 * staticDeg; } else { return staticDeg; } } - (CGFloat)radiansFromDegree:(CGFloat)degree { return degree * M_PI/180; } #pragma mark - Swizzle #pragma mark - On Load - Flip methods + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL rgbaSelector = @selector(getRed:green:blue:alpha:); SEL swizzledRGBASelector = @selector(colours_getRed:green:blue:alpha:); SEL hsbaSelector = @selector(getHue:saturation:brightness:alpha:); SEL swizzledHSBASelector = @selector(colours_getHue:saturation:brightness:alpha:); Method rgbaMethod = class_getInstanceMethod(class, rgbaSelector); Method swizzledRGBAMethod = class_getInstanceMethod(class, swizzledRGBASelector); Method hsbaMethod = class_getInstanceMethod(class, hsbaSelector); Method swizzledHSBAMethod = class_getInstanceMethod(class, swizzledHSBASelector); // Attempt adding the methods BOOL didAddRGBAMethod = class_addMethod(class, rgbaSelector, method_getImplementation(swizzledRGBAMethod), method_getTypeEncoding(swizzledRGBAMethod)); BOOL didAddHSBAMethod = class_addMethod(class, hsbaSelector, method_getImplementation(swizzledHSBAMethod), method_getTypeEncoding(swizzledHSBAMethod)); // Replace methods if (didAddRGBAMethod) { class_replaceMethod(class, swizzledRGBASelector, method_getImplementation(swizzledRGBAMethod), method_getTypeEncoding(swizzledRGBAMethod)); } else { method_exchangeImplementations(rgbaMethod, swizzledRGBAMethod); } if (didAddHSBAMethod) { class_replaceMethod(class, swizzledHSBASelector, method_getImplementation(swizzledHSBAMethod), method_getTypeEncoding(swizzledHSBAMethod)); } else { method_exchangeImplementations(hsbaMethod, swizzledHSBAMethod); } }); } #pragma mark - Swizzled Methods - (BOOL)colours_getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha { if (CGColorGetNumberOfComponents(self.CGColor) == 4) { return [self colours_getRed:red green:green blue:blue alpha:alpha]; } else if (CGColorGetNumberOfComponents(self.CGColor) == 2) { CGFloat white; CGFloat m_alpha; if ([self getWhite:&white alpha:&m_alpha]) { *red = white * 1.0; *green = white * 1.0; *blue = white * 1.0; *alpha = m_alpha; return YES; } } return NO; } - (BOOL)colours_getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha { if (CGColorGetNumberOfComponents(self.CGColor) == 4) { return [self colours_getHue:hue saturation:saturation brightness:brightness alpha:alpha]; } else if (CGColorGetNumberOfComponents(self.CGColor) == 2) { CGFloat white = 0; CGFloat a = 0; if ([self getWhite:&white alpha:&a]) { *hue = 0; *saturation = 0; *brightness = white * 1.0; *alpha = a * 1.0; return YES; } } return NO; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/DateToolsExample-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.mattyork.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/DateToolsExample-Prefix.pch ================================================ // // Prefix header // // The contents of this file are implicitly included at the beginning of every source file. // #import #ifndef __IPHONE_5_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif #ifdef __OBJC__ #import #import #endif ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/DateToolsViewController.h ================================================ // // DateToolsViewController.h // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import @interface DateToolsViewController : UIViewController @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/DateToolsViewController.m ================================================ // // DateToolsViewController.m // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import "DateToolsViewController.h" #import "NSDate+DateTools.h" #import "Colours.h" @interface DateToolsViewController () @property (weak, nonatomic) IBOutlet UIScrollView *MasterScrollView; @property NSTimer *updateTimer; @property NSDate *selectedDate; @property NSDateFormatter *formatter; //Time Ago View @property (strong, nonatomic) IBOutlet UIView *TimeAgoView; @property (weak, nonatomic) IBOutlet UILabel *TimeAgoLabel; @property (weak, nonatomic) IBOutlet UISlider *TimeAgoSlider; @property (weak, nonatomic) IBOutlet UILabel *SecondsLabel; @property (weak, nonatomic) IBOutlet UILabel *MinutesLabel; @property (weak, nonatomic) IBOutlet UILabel *HoursLabel; @property (weak, nonatomic) IBOutlet UILabel *DaysLabel; @property (weak, nonatomic) IBOutlet UILabel *WeeksLabel; @property (weak, nonatomic) IBOutlet UILabel *MonthsLabel; @property (weak, nonatomic) IBOutlet UILabel *YearsLabel; @end @implementation DateToolsViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization self.title = @"NSDate+DateTools"; self.tabBarItem.title = @"NSDate+DateTools"; self.tabBarItem.image = [UIImage imageNamed:@"Calendar"]; self.tabBarItem.selectedImage = [UIImage imageNamed:@"Calendar_filled"]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; //Setup date formatter self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"HHmm MMMM d yyyy"]; //Set initial date self.selectedDate = [self.formatter dateFromString:@"0000 November 5 1605"]; self.TimeAgoSlider.value = [self.selectedDate timeIntervalSinceNow]; //Set up timer for updating UI self.updateTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimeAgoLabels) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.updateTimer forMode:NSRunLoopCommonModes]; [self setupViews]; [self updateTimeAgoLabels]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } -(void)setupViews{ [self.MasterScrollView addSubview:self.TimeAgoView]; [self.MasterScrollView setContentSize:self.TimeAgoView.frame.size]; self.SecondsLabel.textColor = [UIColor tealColor]; self.MinutesLabel.textColor = [UIColor moneyGreenColor]; self.HoursLabel.textColor = [UIColor salmonColor]; self.DaysLabel.textColor = [UIColor violetColor]; self.WeeksLabel.textColor = [UIColor tealColor]; self.MonthsLabel.textColor = [UIColor waveColor]; self.YearsLabel.textColor = [UIColor bananaColor]; } #pragma mark - Update -(void)updateTimeAgoLabels{ //Account for now if (self.TimeAgoSlider.value == 0) { self.selectedDate = [NSDate date]; } //Set time ago label self.TimeAgoLabel.text = [self.formatter stringFromDate:self.selectedDate]; //Set date component labels self.SecondsLabel.text = [NSString stringWithFormat:@"%.0f", self.selectedDate.secondsAgo]; self.MinutesLabel.text = [NSString stringWithFormat:@"%.0f", self.selectedDate.minutesAgo]; self.HoursLabel.text = [NSString stringWithFormat:@"%.0f", self.selectedDate.hoursAgo]; self.DaysLabel.text = [NSString stringWithFormat:@"%ld", (long)self.selectedDate.daysAgo]; self.WeeksLabel.text = [NSString stringWithFormat:@"%ld", (long)self.selectedDate.weeksAgo]; self.MonthsLabel.text = [NSString stringWithFormat:@"%ld", (long)self.selectedDate.monthsAgo]; self.YearsLabel.text = [NSString stringWithFormat:@"%ld", (long)self.selectedDate.yearsAgo]; } - (IBAction)sliderValueDidChange:(UISlider *)sender { self.selectedDate = [NSDate dateWithTimeIntervalSinceNow:sender.value]; //Update UI [self updateTimeAgoLabels]; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/DateToolsViewController.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/ExampleNavigationController.h ================================================ // // ExampleNavigationController.h // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import @interface ExampleNavigationController : UINavigationController @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/ExampleNavigationController.m ================================================ // // ExampleNavigationController.m // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import "ExampleNavigationController.h" #import "Colours.h" @interface ExampleNavigationController () @end @implementation ExampleNavigationController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. if ([self.navigationBar respondsToSelector:@selector(setTranslucent:)]) { self.navigationBar.translucent = NO; self.navigationBar.barTintColor = [UIColor infoBlueColor]; self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:21.0]}; [[UIBarButtonItem appearance] setTintColor:[UIColor whiteColor]]; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/Images.xcassets/LaunchImage.launchimage/Contents.json ================================================ { "images" : [ { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "subtype" : "retina4", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/TimePeriodsViewController.h ================================================ // // TimePeriodsViewController.h // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import @interface TimePeriodsViewController : UIViewController @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/TimePeriodsViewController.m ================================================ // // TimePeriodsViewController.m // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import "TimePeriodsViewController.h" #import "DTTimePeriod.h" @interface TimePeriodsViewController () @property (weak, nonatomic) IBOutlet UIView *AView; @property (weak, nonatomic) IBOutlet UIView *BView; @property (weak, nonatomic) IBOutlet UIView *CView; //Relationships @property (weak, nonatomic) IBOutlet UILabel *ABRelationship; @property (weak, nonatomic) IBOutlet UILabel *ACRelationship; @property (weak, nonatomic) IBOutlet UILabel *BARelationship; @property (weak, nonatomic) IBOutlet UILabel *BCRelationship; @property (weak, nonatomic) IBOutlet UILabel *CARelationship; @property (weak, nonatomic) IBOutlet UILabel *CBRelationship; @end @implementation TimePeriodsViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization // Custom initialization self.title = @"Time Periods"; self.tabBarItem.title = @"Time Periods"; self.tabBarItem.image = [UIImage imageNamed:@"Recents"]; self.tabBarItem.selectedImage = [UIImage imageNamed:@"Recents_filled"]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. //Setup pan recognizers UIPanGestureRecognizer *recognizerA = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [recognizerA setMaximumNumberOfTouches:1]; [recognizerA setMinimumNumberOfTouches:1]; [self.AView addGestureRecognizer:recognizerA]; UIPanGestureRecognizer *recognizerB = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [recognizerB setMaximumNumberOfTouches:1]; [recognizerB setMinimumNumberOfTouches:1]; [self.BView addGestureRecognizer:recognizerB]; UIPanGestureRecognizer *recognizerC = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [recognizerC setMaximumNumberOfTouches:1]; [recognizerC setMinimumNumberOfTouches:1]; [self.CView addGestureRecognizer:recognizerC]; //Set initial relationships [self updateRelationships]; //Set up info button for alert [self.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:@"Info" style:UIBarButtonItemStyleBordered target:self action:@selector(showInfo)]]; } -(void)showInfo{ [[[UIAlertView alloc] initWithTitle:@"Legend" message:@"Ins. - Inside\nEnc. - Enclosing\n\nFor more information on the various DTTimePeriod relationships, please see the DateTools README on GitHub." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Pan Recognizers - (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer { CGPoint translation = [recognizer translationInView:self.view]; recognizer.view.frame = CGRectMake(MAX(10, MIN((self.view.frame.size.width-recognizer.view.frame.size.width - 10), recognizer.view.frame.origin.x + translation.x)), recognizer.view.frame.origin.y, recognizer.view.frame.size.width, recognizer.view.frame.size.height); [recognizer setTranslation:CGPointMake(0, 0) inView:self.view]; [self updateRelationships]; } #pragma mark - Update -(void)updateRelationships{ NSInteger AOffset = -300 + (self.AView.frame.origin.x - 10); NSInteger BOffset = -300 + (self.BView.frame.origin.x - 10); NSInteger COffset = -300 + (self.CView.frame.origin.x - 10); //AOffset *= 4; //BOffset *= 4; //COffset *= 4; DTTimePeriod *aPeriod = [DTTimePeriod timePeriodWithStartDate:[NSDate dateWithTimeIntervalSince1970:AOffset] endDate:[NSDate dateWithTimeIntervalSince1970:AOffset+self.AView.frame.size.width]]; DTTimePeriod *bPeriod = [DTTimePeriod timePeriodWithStartDate:[NSDate dateWithTimeIntervalSince1970:BOffset] endDate:[NSDate dateWithTimeIntervalSince1970:BOffset+self.BView.frame.size.width]]; DTTimePeriod *cPeriod = [DTTimePeriod timePeriodWithStartDate:[NSDate dateWithTimeIntervalSince1970:COffset] endDate:[NSDate dateWithTimeIntervalSince1970:COffset+self.CView.frame.size.width]]; //Set A relationships self.ABRelationship.text = [self stringForRelation:[aPeriod relationToPeriod:bPeriod] forPeriodName:@"B"]; self.ACRelationship.text = [self stringForRelation:[aPeriod relationToPeriod:cPeriod] forPeriodName:@"C"]; //Set B relationships self.BARelationship.text = [self stringForRelation:[bPeriod relationToPeriod:aPeriod] forPeriodName:@"A"]; self.BCRelationship.text = [self stringForRelation:[bPeriod relationToPeriod:cPeriod] forPeriodName:@"C"]; //Set C relationships self.CARelationship.text = [self stringForRelation:[cPeriod relationToPeriod:aPeriod] forPeriodName:@"A"]; self.CBRelationship.text = [self stringForRelation:[cPeriod relationToPeriod:bPeriod] forPeriodName:@"B"]; } -(NSString *)stringForRelation:(DTTimePeriodRelation)relation forPeriodName:(NSString *)periodName{ switch (relation) { case DTTimePeriodRelationAfter: return [NSString stringWithFormat:@"After %@", periodName]; case DTTimePeriodRelationBefore: return [NSString stringWithFormat:@"Before %@", periodName]; case DTTimePeriodRelationEnclosing: return [NSString stringWithFormat:@"Enclosing %@", periodName]; case DTTimePeriodRelationEnclosingEndTouching: return [NSString stringWithFormat:@"Enc. End Touch %@", periodName]; case DTTimePeriodRelationEnclosingStartTouching: return [NSString stringWithFormat:@"Enc. Start Touch %@", periodName]; case DTTimePeriodRelationEndInside: return [NSString stringWithFormat:@"Ends Inside %@", periodName]; case DTTimePeriodRelationEndTouching: return [NSString stringWithFormat:@"Ends Touching %@", periodName]; case DTTimePeriodRelationExactMatch: return [NSString stringWithFormat:@"Exact Match %@", periodName]; case DTTimePeriodRelationInside: return [NSString stringWithFormat:@"Inside %@", periodName]; case DTTimePeriodRelationInsideEndTouching: return [NSString stringWithFormat:@"Ins. End Touch %@", periodName]; case DTTimePeriodRelationInsideStartTouching: return [NSString stringWithFormat:@"Ins. Start Touch %@", periodName]; case DTTimePeriodRelationNone: return [NSString stringWithFormat:@"No Relation to %@", periodName]; case DTTimePeriodRelationStartInside: return [NSString stringWithFormat:@"Starts Inside %@", periodName]; case DTTimePeriodRelationStartTouching: return [NSString stringWithFormat:@"Starts Touching %@", periodName]; default: break; } typedef NS_ENUM(NSUInteger, DTTimePeriodRelation){ DTTimePeriodRelationAfter, DTTimePeriodRelationStartTouching, DTTimePeriodRelationStartInside, DTTimePeriodRelationInsideStartTouching, DTTimePeriodRelationEnclosingStartTouching, DTTimePeriodRelationEnclosing, DTTimePeriodRelationEnclosingEndTouching, DTTimePeriodRelationExactMatch, DTTimePeriodRelationInside, DTTimePeriodRelationInsideEndTouching, DTTimePeriodRelationEndInside, DTTimePeriodRelationEndTouching, DTTimePeriodRelationBefore, DTTimePeriodRelationNone //One or more of the dates does not exist }; } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/TimePeriodsViewController.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample/main.m ================================================ // // main.m // DateToolsExample // // Created by Matthew York on 3/19/14. // // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 0AFD486518F0BBC0004D0FE1 /* DateTools.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0AFD486418F0BBC0004D0FE1 /* DateTools.bundle */; }; 901CF0E31B6CA9FE00F6414B /* DateTools.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 901CF0D31B6CA9FE00F6414B /* DateTools.bundle */; }; 901CF0E41B6CA9FE00F6414B /* DateTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0D41B6CA9FE00F6414B /* DateTools.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0E51B6CA9FE00F6414B /* DTConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0D51B6CA9FE00F6414B /* DTConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0E61B6CA9FE00F6414B /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0D61B6CA9FE00F6414B /* DTConstants.m */; }; 901CF0E71B6CA9FE00F6414B /* DTError.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0D71B6CA9FE00F6414B /* DTError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0E81B6CA9FE00F6414B /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0D81B6CA9FE00F6414B /* DTError.m */; }; 901CF0E91B6CA9FE00F6414B /* DTTimePeriod.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0D91B6CA9FE00F6414B /* DTTimePeriod.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0EA1B6CA9FE00F6414B /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0DA1B6CA9FE00F6414B /* DTTimePeriod.m */; }; 901CF0EB1B6CA9FE00F6414B /* DTTimePeriodChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0DB1B6CA9FE00F6414B /* DTTimePeriodChain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0EC1B6CA9FE00F6414B /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0DC1B6CA9FE00F6414B /* DTTimePeriodChain.m */; }; 901CF0ED1B6CA9FE00F6414B /* DTTimePeriodCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0DD1B6CA9FE00F6414B /* DTTimePeriodCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0EE1B6CA9FE00F6414B /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0DE1B6CA9FE00F6414B /* DTTimePeriodCollection.m */; }; 901CF0EF1B6CA9FE00F6414B /* DTTimePeriodGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0DF1B6CA9FE00F6414B /* DTTimePeriodGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0F01B6CA9FE00F6414B /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0E01B6CA9FE00F6414B /* DTTimePeriodGroup.m */; }; 901CF0F11B6CA9FE00F6414B /* NSDate+DateTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 901CF0E11B6CA9FE00F6414B /* NSDate+DateTools.h */; settings = {ATTRIBUTES = (Public, ); }; }; 901CF0F21B6CA9FE00F6414B /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 901CF0E21B6CA9FE00F6414B /* NSDate+DateTools.m */; }; D935DB441B567FBD00BAA4F0 /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = D935DB431B567FBD00BAA4F0 /* DTConstants.m */; }; F007632018DE5F5E00A99075 /* DateToolsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F007631E18DE5F5E00A99075 /* DateToolsViewController.m */; }; F007632118DE5F5E00A99075 /* DateToolsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F007631F18DE5F5E00A99075 /* DateToolsViewController.xib */; }; F007632518DE5FE300A99075 /* Colours.m in Sources */ = {isa = PBXBuildFile; fileRef = F007632418DE5FE300A99075 /* Colours.m */; }; F007632C18DE61EE00A99075 /* Calendar_filled.png in Resources */ = {isa = PBXBuildFile; fileRef = F007632818DE61EE00A99075 /* Calendar_filled.png */; }; F007632D18DE61EE00A99075 /* Calendar_filled@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F007632918DE61EE00A99075 /* Calendar_filled@2x.png */; }; F007632E18DE61EE00A99075 /* Calendar.png in Resources */ = {isa = PBXBuildFile; fileRef = F007632A18DE61EE00A99075 /* Calendar.png */; }; F007632F18DE61EE00A99075 /* Calendar@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F007632B18DE61EE00A99075 /* Calendar@2x.png */; }; F007633418DE628900A99075 /* ExampleNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = F007633318DE628900A99075 /* ExampleNavigationController.m */; }; F007633918DE63C400A99075 /* TimePeriodsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F007633718DE63C400A99075 /* TimePeriodsViewController.m */; }; F007633A18DE63C400A99075 /* TimePeriodsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F007633818DE63C400A99075 /* TimePeriodsViewController.xib */; }; F007633F18DE647600A99075 /* Recents_filled.png in Resources */ = {isa = PBXBuildFile; fileRef = F007633B18DE647600A99075 /* Recents_filled.png */; }; F007634018DE647600A99075 /* Recents_filled@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F007633C18DE647600A99075 /* Recents_filled@2x.png */; }; F007634118DE647600A99075 /* Recents.png in Resources */ = {isa = PBXBuildFile; fileRef = F007633D18DE647600A99075 /* Recents.png */; }; F007634218DE647600A99075 /* Recents@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F007633E18DE647600A99075 /* Recents@2x.png */; }; F07A1A0818DCC76500B3E9FF /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = F07A1A0718DCC76500B3E9FF /* DTTimePeriodCollection.m */; }; F0A426D618D9FDB500E236C0 /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = F0A426D518D9FDB500E236C0 /* DTTimePeriod.m */; }; F0EE17E718DEB1A10010FAD8 /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17E218DEB1A10010FAD8 /* DTError.m */; }; F0EE17E818DEB1A10010FAD8 /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17E218DEB1A10010FAD8 /* DTError.m */; }; F0EE17E918DEB1A10010FAD8 /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17E418DEB1A10010FAD8 /* DTTimePeriodChain.m */; }; F0EE17EA18DEB1A10010FAD8 /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17E418DEB1A10010FAD8 /* DTTimePeriodChain.m */; }; F0EE17EB18DEB1A10010FAD8 /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17E618DEB1A10010FAD8 /* DTTimePeriodGroup.m */; }; F0EE17EC18DEB1A10010FAD8 /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17E618DEB1A10010FAD8 /* DTTimePeriodGroup.m */; }; F0F08D9F18D9E80E00214C6B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F08D9E18D9E80E00214C6B /* Foundation.framework */; }; F0F08DA118D9E80E00214C6B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F08DA018D9E80E00214C6B /* CoreGraphics.framework */; }; F0F08DA318D9E80E00214C6B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F08DA218D9E80E00214C6B /* UIKit.framework */; }; F0F08DA918D9E80E00214C6B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F0F08DA718D9E80E00214C6B /* InfoPlist.strings */; }; F0F08DAB18D9E80E00214C6B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F0F08DAA18D9E80E00214C6B /* main.m */; }; F0F08DAF18D9E80E00214C6B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F0F08DAE18D9E80E00214C6B /* AppDelegate.m */; }; F0F08DB718D9E80E00214C6B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F0F08DB618D9E80E00214C6B /* Images.xcassets */; }; F0F08DBE18D9E80E00214C6B /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F08DBD18D9E80E00214C6B /* XCTest.framework */; }; F0F08DBF18D9E80E00214C6B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F08D9E18D9E80E00214C6B /* Foundation.framework */; }; F0F08DC018D9E80E00214C6B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0F08DA218D9E80E00214C6B /* UIKit.framework */; }; F0F08DC818D9E80E00214C6B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F0F08DC618D9E80E00214C6B /* InfoPlist.strings */; }; F0F08DD918D9E94F00214C6B /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = F0F08DD818D9E94F00214C6B /* NSDate+DateTools.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ F0F08DC118D9E80E00214C6B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F0F08D9318D9E80E00214C6B /* Project object */; proxyType = 1; remoteGlobalIDString = F0F08D9A18D9E80E00214C6B; remoteInfo = DateToolsExample; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 0AFD486418F0BBC0004D0FE1 /* DateTools.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = DateTools.bundle; path = ../../../DateTools/DateTools.bundle; sourceTree = ""; }; 901CF0D31B6CA9FE00F6414B /* DateTools.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = DateTools.bundle; sourceTree = ""; }; 901CF0D41B6CA9FE00F6414B /* DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateTools.h; sourceTree = ""; }; 901CF0D51B6CA9FE00F6414B /* DTConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTConstants.h; sourceTree = ""; }; 901CF0D61B6CA9FE00F6414B /* DTConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTConstants.m; sourceTree = ""; }; 901CF0D71B6CA9FE00F6414B /* DTError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTError.h; sourceTree = ""; }; 901CF0D81B6CA9FE00F6414B /* DTError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTError.m; sourceTree = ""; }; 901CF0D91B6CA9FE00F6414B /* DTTimePeriod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriod.h; sourceTree = ""; }; 901CF0DA1B6CA9FE00F6414B /* DTTimePeriod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriod.m; sourceTree = ""; }; 901CF0DB1B6CA9FE00F6414B /* DTTimePeriodChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriodChain.h; sourceTree = ""; }; 901CF0DC1B6CA9FE00F6414B /* DTTimePeriodChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodChain.m; sourceTree = ""; }; 901CF0DD1B6CA9FE00F6414B /* DTTimePeriodCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriodCollection.h; sourceTree = ""; }; 901CF0DE1B6CA9FE00F6414B /* DTTimePeriodCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodCollection.m; sourceTree = ""; }; 901CF0DF1B6CA9FE00F6414B /* DTTimePeriodGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTTimePeriodGroup.h; sourceTree = ""; }; 901CF0E01B6CA9FE00F6414B /* DTTimePeriodGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodGroup.m; sourceTree = ""; }; 901CF0E11B6CA9FE00F6414B /* NSDate+DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+DateTools.h"; sourceTree = ""; }; 901CF0E21B6CA9FE00F6414B /* NSDate+DateTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+DateTools.m"; sourceTree = ""; }; 908837C91B637C240063096B /* DateTools.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DateTools.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 908837CC1B637C240063096B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D935DB431B567FBD00BAA4F0 /* DTConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTConstants.m; path = ../../../DateTools/DTConstants.m; sourceTree = ""; }; F007631D18DE5F5E00A99075 /* DateToolsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateToolsViewController.h; sourceTree = ""; }; F007631E18DE5F5E00A99075 /* DateToolsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateToolsViewController.m; sourceTree = ""; }; F007631F18DE5F5E00A99075 /* DateToolsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DateToolsViewController.xib; sourceTree = ""; }; F007632318DE5FE300A99075 /* Colours.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Colours.h; sourceTree = ""; }; F007632418DE5FE300A99075 /* Colours.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Colours.m; sourceTree = ""; }; F007632818DE61EE00A99075 /* Calendar_filled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Calendar_filled.png; sourceTree = ""; }; F007632918DE61EE00A99075 /* Calendar_filled@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Calendar_filled@2x.png"; sourceTree = ""; }; F007632A18DE61EE00A99075 /* Calendar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Calendar.png; sourceTree = ""; }; F007632B18DE61EE00A99075 /* Calendar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Calendar@2x.png"; sourceTree = ""; }; F007633218DE628900A99075 /* ExampleNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleNavigationController.h; sourceTree = ""; }; F007633318DE628900A99075 /* ExampleNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleNavigationController.m; sourceTree = ""; }; F007633618DE63C400A99075 /* TimePeriodsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimePeriodsViewController.h; sourceTree = ""; }; F007633718DE63C400A99075 /* TimePeriodsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimePeriodsViewController.m; sourceTree = ""; }; F007633818DE63C400A99075 /* TimePeriodsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TimePeriodsViewController.xib; sourceTree = ""; }; F007633B18DE647600A99075 /* Recents_filled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Recents_filled.png; sourceTree = ""; }; F007633C18DE647600A99075 /* Recents_filled@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Recents_filled@2x.png"; sourceTree = ""; }; F007633D18DE647600A99075 /* Recents.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Recents.png; sourceTree = ""; }; F007633E18DE647600A99075 /* Recents@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Recents@2x.png"; sourceTree = ""; }; F07A1A0618DCC76500B3E9FF /* DTTimePeriodCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriodCollection.h; path = ../../../DateTools/DTTimePeriodCollection.h; sourceTree = ""; }; F07A1A0718DCC76500B3E9FF /* DTTimePeriodCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodCollection.m; path = ../../../DateTools/DTTimePeriodCollection.m; sourceTree = ""; }; F0A426D418D9FDB500E236C0 /* DTTimePeriod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriod.h; path = ../../../DateTools/DTTimePeriod.h; sourceTree = ""; }; F0A426D518D9FDB500E236C0 /* DTTimePeriod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriod.m; path = ../../../DateTools/DTTimePeriod.m; sourceTree = ""; }; F0A426D918DA358C00E236C0 /* DTConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTConstants.h; path = ../../../DateTools/DTConstants.h; sourceTree = ""; }; F0EE17E018DEAE190010FAD8 /* DateTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DateTools.h; path = ../../../DateTools/DateTools.h; sourceTree = ""; }; F0EE17E118DEB1A10010FAD8 /* DTError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTError.h; path = ../../../DateTools/DTError.h; sourceTree = ""; }; F0EE17E218DEB1A10010FAD8 /* DTError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTError.m; path = ../../../DateTools/DTError.m; sourceTree = ""; }; F0EE17E318DEB1A10010FAD8 /* DTTimePeriodChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriodChain.h; path = ../../../DateTools/DTTimePeriodChain.h; sourceTree = ""; }; F0EE17E418DEB1A10010FAD8 /* DTTimePeriodChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodChain.m; path = ../../../DateTools/DTTimePeriodChain.m; sourceTree = ""; }; F0EE17E518DEB1A10010FAD8 /* DTTimePeriodGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriodGroup.h; path = ../../../DateTools/DTTimePeriodGroup.h; sourceTree = ""; }; F0EE17E618DEB1A10010FAD8 /* DTTimePeriodGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodGroup.m; path = ../../../DateTools/DTTimePeriodGroup.m; sourceTree = ""; }; F0F08D9B18D9E80E00214C6B /* DateToolsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DateToolsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; F0F08D9E18D9E80E00214C6B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; F0F08DA018D9E80E00214C6B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; F0F08DA218D9E80E00214C6B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; F0F08DA618D9E80E00214C6B /* DateToolsExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DateToolsExample-Info.plist"; sourceTree = ""; }; F0F08DA818D9E80E00214C6B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; F0F08DAA18D9E80E00214C6B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; F0F08DAC18D9E80E00214C6B /* DateToolsExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DateToolsExample-Prefix.pch"; sourceTree = ""; }; F0F08DAD18D9E80E00214C6B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; F0F08DAE18D9E80E00214C6B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; F0F08DB618D9E80E00214C6B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; F0F08DBC18D9E80E00214C6B /* DateToolsExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DateToolsExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F0F08DBD18D9E80E00214C6B /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; F0F08DC518D9E80E00214C6B /* DateToolsExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DateToolsExampleTests-Info.plist"; sourceTree = ""; }; F0F08DC718D9E80E00214C6B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; F0F08DD718D9E94F00214C6B /* NSDate+DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDate+DateTools.h"; path = "../../../DateTools/NSDate+DateTools.h"; sourceTree = ""; }; F0F08DD818D9E94F00214C6B /* NSDate+DateTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDate+DateTools.m"; path = "../../../DateTools/NSDate+DateTools.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 908837C51B637C240063096B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; F0F08D9818D9E80E00214C6B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( F0F08DA118D9E80E00214C6B /* CoreGraphics.framework in Frameworks */, F0F08DA318D9E80E00214C6B /* UIKit.framework in Frameworks */, F0F08D9F18D9E80E00214C6B /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; F0F08DB918D9E80E00214C6B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( F0F08DBE18D9E80E00214C6B /* XCTest.framework in Frameworks */, F0F08DC018D9E80E00214C6B /* UIKit.framework in Frameworks */, F0F08DBF18D9E80E00214C6B /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 901CF0D21B6CA9FE00F6414B /* DateTools */ = { isa = PBXGroup; children = ( 901CF0D31B6CA9FE00F6414B /* DateTools.bundle */, 901CF0D41B6CA9FE00F6414B /* DateTools.h */, 901CF0D51B6CA9FE00F6414B /* DTConstants.h */, 901CF0D61B6CA9FE00F6414B /* DTConstants.m */, 901CF0D71B6CA9FE00F6414B /* DTError.h */, 901CF0D81B6CA9FE00F6414B /* DTError.m */, 901CF0D91B6CA9FE00F6414B /* DTTimePeriod.h */, 901CF0DA1B6CA9FE00F6414B /* DTTimePeriod.m */, 901CF0DB1B6CA9FE00F6414B /* DTTimePeriodChain.h */, 901CF0DC1B6CA9FE00F6414B /* DTTimePeriodChain.m */, 901CF0DD1B6CA9FE00F6414B /* DTTimePeriodCollection.h */, 901CF0DE1B6CA9FE00F6414B /* DTTimePeriodCollection.m */, 901CF0DF1B6CA9FE00F6414B /* DTTimePeriodGroup.h */, 901CF0E01B6CA9FE00F6414B /* DTTimePeriodGroup.m */, 901CF0E11B6CA9FE00F6414B /* NSDate+DateTools.h */, 901CF0E21B6CA9FE00F6414B /* NSDate+DateTools.m */, ); name = DateTools; path = ../../../DateTools; sourceTree = ""; }; 908837CA1B637C240063096B /* DateTools */ = { isa = PBXGroup; children = ( 901CF0D21B6CA9FE00F6414B /* DateTools */, 908837CB1B637C240063096B /* Supporting Files */, ); path = DateTools; sourceTree = ""; }; 908837CB1B637C240063096B /* Supporting Files */ = { isa = PBXGroup; children = ( 908837CC1B637C240063096B /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; F007632218DE5F9E00A99075 /* Colours */ = { isa = PBXGroup; children = ( F007632318DE5FE300A99075 /* Colours.h */, F007632418DE5FE300A99075 /* Colours.m */, ); name = Colours; sourceTree = ""; }; F007632618DE612D00A99075 /* Images */ = { isa = PBXGroup; children = ( F007632718DE613400A99075 /* Tab bar */, ); name = Images; sourceTree = ""; }; F007632718DE613400A99075 /* Tab bar */ = { isa = PBXGroup; children = ( F007632818DE61EE00A99075 /* Calendar_filled.png */, F007632918DE61EE00A99075 /* Calendar_filled@2x.png */, F007632A18DE61EE00A99075 /* Calendar.png */, F007632B18DE61EE00A99075 /* Calendar@2x.png */, F007633B18DE647600A99075 /* Recents_filled.png */, F007633C18DE647600A99075 /* Recents_filled@2x.png */, F007633D18DE647600A99075 /* Recents.png */, F007633E18DE647600A99075 /* Recents@2x.png */, ); name = "Tab bar"; sourceTree = ""; }; F007633018DE626600A99075 /* Controllers */ = { isa = PBXGroup; children = ( F007633218DE628900A99075 /* ExampleNavigationController.h */, F007633318DE628900A99075 /* ExampleNavigationController.m */, F007633118DE626D00A99075 /* Date Tools */, F007633518DE63AB00A99075 /* Time Periods */, ); name = Controllers; sourceTree = ""; }; F007633118DE626D00A99075 /* Date Tools */ = { isa = PBXGroup; children = ( F007631D18DE5F5E00A99075 /* DateToolsViewController.h */, F007631E18DE5F5E00A99075 /* DateToolsViewController.m */, F007631F18DE5F5E00A99075 /* DateToolsViewController.xib */, ); name = "Date Tools"; sourceTree = ""; }; F007633518DE63AB00A99075 /* Time Periods */ = { isa = PBXGroup; children = ( F007633618DE63C400A99075 /* TimePeriodsViewController.h */, F007633718DE63C400A99075 /* TimePeriodsViewController.m */, F007633818DE63C400A99075 /* TimePeriodsViewController.xib */, ); name = "Time Periods"; sourceTree = ""; }; F0F08D9218D9E80E00214C6B = { isa = PBXGroup; children = ( F0F08DA418D9E80E00214C6B /* DateToolsExample */, F0F08DC318D9E80E00214C6B /* DateToolsExampleTests */, 908837CA1B637C240063096B /* DateTools */, F0F08D9D18D9E80E00214C6B /* Frameworks */, F0F08D9C18D9E80E00214C6B /* Products */, ); sourceTree = ""; }; F0F08D9C18D9E80E00214C6B /* Products */ = { isa = PBXGroup; children = ( F0F08D9B18D9E80E00214C6B /* DateToolsExample.app */, F0F08DBC18D9E80E00214C6B /* DateToolsExampleTests.xctest */, 908837C91B637C240063096B /* DateTools.framework */, ); name = Products; sourceTree = ""; }; F0F08D9D18D9E80E00214C6B /* Frameworks */ = { isa = PBXGroup; children = ( F0F08D9E18D9E80E00214C6B /* Foundation.framework */, F0F08DA018D9E80E00214C6B /* CoreGraphics.framework */, F0F08DA218D9E80E00214C6B /* UIKit.framework */, F0F08DBD18D9E80E00214C6B /* XCTest.framework */, ); name = Frameworks; sourceTree = ""; }; F0F08DA418D9E80E00214C6B /* DateToolsExample */ = { isa = PBXGroup; children = ( F0F08DAD18D9E80E00214C6B /* AppDelegate.h */, F0F08DAE18D9E80E00214C6B /* AppDelegate.m */, F0F08DD318D9E81C00214C6B /* DateTools */, F007633018DE626600A99075 /* Controllers */, F0F08DB618D9E80E00214C6B /* Images.xcassets */, F0F08DA518D9E80E00214C6B /* Supporting Files */, ); path = DateToolsExample; sourceTree = ""; }; F0F08DA518D9E80E00214C6B /* Supporting Files */ = { isa = PBXGroup; children = ( F007632618DE612D00A99075 /* Images */, F007632218DE5F9E00A99075 /* Colours */, F0F08DA618D9E80E00214C6B /* DateToolsExample-Info.plist */, F0F08DA718D9E80E00214C6B /* InfoPlist.strings */, F0F08DAA18D9E80E00214C6B /* main.m */, F0F08DAC18D9E80E00214C6B /* DateToolsExample-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; F0F08DC318D9E80E00214C6B /* DateToolsExampleTests */ = { isa = PBXGroup; children = ( F0F08DC418D9E80E00214C6B /* Supporting Files */, ); path = DateToolsExampleTests; sourceTree = ""; }; F0F08DC418D9E80E00214C6B /* Supporting Files */ = { isa = PBXGroup; children = ( F0F08DC518D9E80E00214C6B /* DateToolsExampleTests-Info.plist */, F0F08DC618D9E80E00214C6B /* InfoPlist.strings */, ); name = "Supporting Files"; sourceTree = ""; }; F0F08DD318D9E81C00214C6B /* DateTools */ = { isa = PBXGroup; children = ( 0AFD486418F0BBC0004D0FE1 /* DateTools.bundle */, F0EE17E118DEB1A10010FAD8 /* DTError.h */, F0EE17E218DEB1A10010FAD8 /* DTError.m */, F0EE17E018DEAE190010FAD8 /* DateTools.h */, F0A426D918DA358C00E236C0 /* DTConstants.h */, D935DB431B567FBD00BAA4F0 /* DTConstants.m */, F0F08DD718D9E94F00214C6B /* NSDate+DateTools.h */, F0F08DD818D9E94F00214C6B /* NSDate+DateTools.m */, F0A426D418D9FDB500E236C0 /* DTTimePeriod.h */, F0A426D518D9FDB500E236C0 /* DTTimePeriod.m */, F0EE17E518DEB1A10010FAD8 /* DTTimePeriodGroup.h */, F0EE17E618DEB1A10010FAD8 /* DTTimePeriodGroup.m */, F07A1A0618DCC76500B3E9FF /* DTTimePeriodCollection.h */, F07A1A0718DCC76500B3E9FF /* DTTimePeriodCollection.m */, F0EE17E318DEB1A10010FAD8 /* DTTimePeriodChain.h */, F0EE17E418DEB1A10010FAD8 /* DTTimePeriodChain.m */, ); name = DateTools; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 908837C61B637C240063096B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 901CF0EF1B6CA9FE00F6414B /* DTTimePeriodGroup.h in Headers */, 901CF0E91B6CA9FE00F6414B /* DTTimePeriod.h in Headers */, 901CF0E41B6CA9FE00F6414B /* DateTools.h in Headers */, 901CF0E71B6CA9FE00F6414B /* DTError.h in Headers */, 901CF0EB1B6CA9FE00F6414B /* DTTimePeriodChain.h in Headers */, 901CF0F11B6CA9FE00F6414B /* NSDate+DateTools.h in Headers */, 901CF0E51B6CA9FE00F6414B /* DTConstants.h in Headers */, 901CF0ED1B6CA9FE00F6414B /* DTTimePeriodCollection.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 908837C81B637C240063096B /* DateTools */ = { isa = PBXNativeTarget; buildConfigurationList = 908837E01B637C240063096B /* Build configuration list for PBXNativeTarget "DateTools" */; buildPhases = ( 908837C41B637C240063096B /* Sources */, 908837C51B637C240063096B /* Frameworks */, 908837C61B637C240063096B /* Headers */, 908837C71B637C240063096B /* Resources */, ); buildRules = ( ); dependencies = ( ); name = DateTools; productName = DateTools; productReference = 908837C91B637C240063096B /* DateTools.framework */; productType = "com.apple.product-type.framework"; }; F0F08D9A18D9E80E00214C6B /* DateToolsExample */ = { isa = PBXNativeTarget; buildConfigurationList = F0F08DCD18D9E80E00214C6B /* Build configuration list for PBXNativeTarget "DateToolsExample" */; buildPhases = ( F0F08D9718D9E80E00214C6B /* Sources */, F0F08D9818D9E80E00214C6B /* Frameworks */, F0F08D9918D9E80E00214C6B /* Resources */, ); buildRules = ( ); dependencies = ( ); name = DateToolsExample; productName = DateToolsExample; productReference = F0F08D9B18D9E80E00214C6B /* DateToolsExample.app */; productType = "com.apple.product-type.application"; }; F0F08DBB18D9E80E00214C6B /* DateToolsExampleTests */ = { isa = PBXNativeTarget; buildConfigurationList = F0F08DD018D9E80E00214C6B /* Build configuration list for PBXNativeTarget "DateToolsExampleTests" */; buildPhases = ( F0F08DB818D9E80E00214C6B /* Sources */, F0F08DB918D9E80E00214C6B /* Frameworks */, F0F08DBA18D9E80E00214C6B /* Resources */, ); buildRules = ( ); dependencies = ( F0F08DC218D9E80E00214C6B /* PBXTargetDependency */, ); name = DateToolsExampleTests; productName = DateToolsExampleTests; productReference = F0F08DBC18D9E80E00214C6B /* DateToolsExampleTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ F0F08D9318D9E80E00214C6B /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0510; TargetAttributes = { 908837C81B637C240063096B = { CreatedOnToolsVersion = 6.4; }; F0F08DBB18D9E80E00214C6B = { TestTargetID = F0F08D9A18D9E80E00214C6B; }; }; }; buildConfigurationList = F0F08D9618D9E80E00214C6B /* Build configuration list for PBXProject "DateToolsExample" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = F0F08D9218D9E80E00214C6B; productRefGroup = F0F08D9C18D9E80E00214C6B /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( F0F08D9A18D9E80E00214C6B /* DateToolsExample */, F0F08DBB18D9E80E00214C6B /* DateToolsExampleTests */, 908837C81B637C240063096B /* DateTools */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 908837C71B637C240063096B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 901CF0E31B6CA9FE00F6414B /* DateTools.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; F0F08D9918D9E80E00214C6B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( F007633F18DE647600A99075 /* Recents_filled.png in Resources */, F007632118DE5F5E00A99075 /* DateToolsViewController.xib in Resources */, F0F08DB718D9E80E00214C6B /* Images.xcassets in Resources */, F007633A18DE63C400A99075 /* TimePeriodsViewController.xib in Resources */, F0F08DA918D9E80E00214C6B /* InfoPlist.strings in Resources */, F007634118DE647600A99075 /* Recents.png in Resources */, 0AFD486518F0BBC0004D0FE1 /* DateTools.bundle in Resources */, F007632E18DE61EE00A99075 /* Calendar.png in Resources */, F007632F18DE61EE00A99075 /* Calendar@2x.png in Resources */, F007634018DE647600A99075 /* Recents_filled@2x.png in Resources */, F007632C18DE61EE00A99075 /* Calendar_filled.png in Resources */, F007632D18DE61EE00A99075 /* Calendar_filled@2x.png in Resources */, F007634218DE647600A99075 /* Recents@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; F0F08DBA18D9E80E00214C6B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( F0F08DC818D9E80E00214C6B /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 908837C41B637C240063096B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 901CF0EA1B6CA9FE00F6414B /* DTTimePeriod.m in Sources */, 901CF0F01B6CA9FE00F6414B /* DTTimePeriodGroup.m in Sources */, 901CF0E81B6CA9FE00F6414B /* DTError.m in Sources */, 901CF0EE1B6CA9FE00F6414B /* DTTimePeriodCollection.m in Sources */, 901CF0E61B6CA9FE00F6414B /* DTConstants.m in Sources */, 901CF0F21B6CA9FE00F6414B /* NSDate+DateTools.m in Sources */, 901CF0EC1B6CA9FE00F6414B /* DTTimePeriodChain.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F0F08D9718D9E80E00214C6B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D935DB441B567FBD00BAA4F0 /* DTConstants.m in Sources */, F0EE17E718DEB1A10010FAD8 /* DTError.m in Sources */, F0EE17EB18DEB1A10010FAD8 /* DTTimePeriodGroup.m in Sources */, F0A426D618D9FDB500E236C0 /* DTTimePeriod.m in Sources */, F007633418DE628900A99075 /* ExampleNavigationController.m in Sources */, F0EE17E918DEB1A10010FAD8 /* DTTimePeriodChain.m in Sources */, F07A1A0818DCC76500B3E9FF /* DTTimePeriodCollection.m in Sources */, F0F08DAF18D9E80E00214C6B /* AppDelegate.m in Sources */, F007632518DE5FE300A99075 /* Colours.m in Sources */, F007632018DE5F5E00A99075 /* DateToolsViewController.m in Sources */, F0F08DD918D9E94F00214C6B /* NSDate+DateTools.m in Sources */, F007633918DE63C400A99075 /* TimePeriodsViewController.m in Sources */, F0F08DAB18D9E80E00214C6B /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F0F08DB818D9E80E00214C6B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F0EE17E818DEB1A10010FAD8 /* DTError.m in Sources */, F0EE17EC18DEB1A10010FAD8 /* DTTimePeriodGroup.m in Sources */, F0EE17EA18DEB1A10010FAD8 /* DTTimePeriodChain.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ F0F08DC218D9E80E00214C6B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F0F08D9A18D9E80E00214C6B /* DateToolsExample */; targetProxy = F0F08DC118D9E80E00214C6B /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ F0F08DA718D9E80E00214C6B /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( F0F08DA818D9E80E00214C6B /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; F0F08DC618D9E80E00214C6B /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( F0F08DC718D9E80E00214C6B /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 908837DC1B637C240063096B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_UNREACHABLE_CODE = YES; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = DateTools/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.4; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 908837DD1B637C240063096B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_UNREACHABLE_CODE = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = DateTools/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.4; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; F0F08DCB18D9E80E00214C6B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 7.1; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; F0F08DCC18D9E80E00214C6B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 7.1; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; F0F08DCE18D9E80E00214C6B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsExample/DateToolsExample-Prefix.pch"; INFOPLIST_FILE = "DateToolsExample/DateToolsExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; F0F08DCF18D9E80E00214C6B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsExample/DateToolsExample-Prefix.pch"; INFOPLIST_FILE = "DateToolsExample/DateToolsExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; F0F08DD118D9E80E00214C6B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/DateToolsExample.app/DateToolsExample"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsExample/DateToolsExample-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = "DateToolsExampleTests/DateToolsExampleTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Debug; }; F0F08DD218D9E80E00214C6B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/DateToolsExample.app/DateToolsExample"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsExample/DateToolsExample-Prefix.pch"; INFOPLIST_FILE = "DateToolsExampleTests/DateToolsExampleTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 908837E01B637C240063096B /* Build configuration list for PBXNativeTarget "DateTools" */ = { isa = XCConfigurationList; buildConfigurations = ( 908837DC1B637C240063096B /* Debug */, 908837DD1B637C240063096B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F0F08D9618D9E80E00214C6B /* Build configuration list for PBXProject "DateToolsExample" */ = { isa = XCConfigurationList; buildConfigurations = ( F0F08DCB18D9E80E00214C6B /* Debug */, F0F08DCC18D9E80E00214C6B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F0F08DCD18D9E80E00214C6B /* Build configuration list for PBXNativeTarget "DateToolsExample" */ = { isa = XCConfigurationList; buildConfigurations = ( F0F08DCE18D9E80E00214C6B /* Debug */, F0F08DCF18D9E80E00214C6B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F0F08DD018D9E80E00214C6B /* Build configuration list for PBXNativeTarget "DateToolsExampleTests" */ = { isa = XCConfigurationList; buildConfigurations = ( F0F08DD118D9E80E00214C6B /* Debug */, F0F08DD218D9E80E00214C6B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = F0F08D9318D9E80E00214C6B /* Project object */; } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExample.xcodeproj/xcshareddata/xcschemes/DateTools.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExampleTests/DateToolsExampleTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.mattyork.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Examples/DateToolsExample/DateToolsExampleTests/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Matthew York Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: archive/bitbar/App/Vendor/DateTools/README.md ================================================ ![Banner](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/DateToolsHeader.png) ## DateTools DateTools was written to streamline date and time handling in Objective-C. Classes and concepts from other languages served as an inspiration for DateTools, especially the [DateTime](http://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx) structure and [Time Period Library](Time Period Library) for .NET. Through these classes and others, DateTools removes the boilerplate required to access date components, handles more nuanced date comparisons, and serves as the foundation for entirely new concepts like Time Periods and their collections. [![Build Status](https://travis-ci.org/MatthewYork/DateTools.svg?branch=master)](https://travis-ci.org/MatthewYork/DateTools) [![Cocoapods](https://cocoapod-badges.herokuapp.com/v/DateTools/badge.png)](http://cocoapods.org/?q=datetools) ####Featured In
Yahoo! Livetext My Disney Experience ALDI Guidebook Pitch Locator Pro
## Installation **Cocoapods** pod 'DateTools' **Manual Installation** All the classes required for DateTools are located in the DateTools folder in the root of this repository. They are listed below: * DateTools.h * NSDate+DateTools.{h,m} * DTConstants.h * DTError.{h,m} * DTTimePeriod.{h,m} * DTTimePeriodGroup.{h,m} * DTTimePeriodCollection.{h,m} * DTTimePeriodChain.{h,m} The following bundle is necessary if you would like to support internationalization or would like to use the "Time Ago" functionality. You can add localizations at the `Localizations` subheading under `Info` in the `Project` menu. * DateTools.bundle DateTools.h contains the headers for all the other files. Import this if you want to link to the entire framework. ## Table of Contents * [**NSDate+DateTools**](#nsdate-datetools) * [Time Ago](#time-ago) * [Date Components](#date-components) * [Date Editing](#date-editing) * [Date Comparison](#date-comparison) * [Formatted Date Strings](#formatted-date-strings) * [**Time Periods**](#time-periods) * [Initialization](#initialization) * [Time Period Info](#time-period-info) * [Manipulation](#manipulation) * [Relationships](#relationships) * [**Time Period Groups**](#time-period-groups) * [Time Period Collections](#time-period-collections) * [Time Period Chains](#time-period-chains) * [**Unit Tests**](#unit-tests) * [**Credits**](#credits) * [**License**](#license) ##NSDate+DateTools One of the missions of DateTools was to make NSDate feel more complete. There are many other languages that allow direct access to information about dates from their date classes, but NSDate (sadly) does not. It safely works only in the Unix time offsets through the timeIntervalSince... methods for building dates and remains calendar agnostic. But that's not always what we want to do. Sometimes, we want to work with dates based on their date components (like year, month, day, etc) at a more abstract level. This is where DateTools comes in. ####Time Ago No date library would be complete without the ability to quickly make an NSString based on how much earlier a date is than now. DateTools has you covered. These "time ago" strings come in a long and short form, with the latter closely resembling Twitter. You can get these strings like so: ```objc NSDate *timeAgoDate = [NSDate dateWithTimeIntervalSinceNow:-4]; NSLog(@"Time Ago: %@", timeAgoDate.timeAgoSinceNow); NSLog(@"Time Ago: %@", timeAgoDate.shortTimeAgoSinceNow); //Output: //Time Ago: 4 seconds ago //Time Ago: 4s ``` Assuming you have added the localization to your project, `DateTools` currently supports the following languages: - ar (Arabic) - bg (Bulgarian) - ca (Catalan) - zh_Hans (Chinese Simplified) - zh_Hant (Chinese Traditional) - cs (Czech) - da (Danish) - nl (Dutch) - en (English) - fi (Finnish) - fr (French) - de (German) - gre (Greek) - gu (Gujarati) - he (Hebrew) - hi (Hindi) - hu (Hungarian) - is (Icelandic) - id (Indonesian) - it (Italian) - ja (Japanese) - ko (Korean) - lv (Latvian) - ms (Malay) - nb (Norwegian) - pl (Polish) - pt (Portuguese) - ro (Romanian) - ru (Russian) - sl (Slovenian) - es (Spanish) - sv (Swedish) - th (Thai) - tr (Turkish) - uk (Ukrainian) - vi (Vietnamese) - cy (Welsh) - hr (Croatian) If you know a language not listed here, please consider submitting a translation. [Localization codes by language](http://stackoverflow.com/questions/3040677/locale-codes-for-iphone-lproj-folders). This project is user driven (by people like you). Pull requests close faster than issues (merged or rejected). Thanks to Kevin Lawler for his work on [NSDate+TimeAgo](https://github.com/kevinlawler/NSDate-TimeAgo), which has been officially merged into this library. ####Date Components There is a lot of boilerplate associated with getting date components from an NSDate. You have to set up a calendar, use the desired flags for the components you want, and finally extract them out of the calendar. With DateTools, this: ```objc //Create calendar NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit; NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:date]; //Get components NSInteger year = dateComponents.year; NSInteger month = dateComponents.month; ``` ...becomes this: ```objc NSInteger year = date.year; NSInteger month = date.month; ``` And if you would like to use a non-Gregorian calendar, that option is available as well. ```objc NSInteger day = [date dayWithCalendar:calendar]; ``` If you would like to override the default calendar that DateTools uses, simply change it in the defaultCalendar method of NSDate+DateTools.m. ####Date Editing The date editing methods in NSDate+DateTools makes it easy to shift a date earlier or later by adding and subtracting date components. For instance, if you would like a date that is 1 year later from a given date, simply call the method dateByAddingYears. With DateTools, this: ```objc //Create calendar NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:[NSDate defaultCalendar]]; NSDateComponents *components = [[NSDateComponents alloc] init]; //Make changes [components setYear:1]; //Get new date with updated year NSDate *newDate = [calendar dateByAddingComponents:components toDate:date options:0]; ``` ...becomes this: ```objc NSDate *newDate = [date dateByAddingYears:1]; ``` Subtraction of date components is also fully supported through the dateBySubtractingYears ####Date Comparison Another mission of the DateTools category is to greatly increase the flexibility of date comparisons. NSDate gives you four basic methods: * isEqualToDate: * earlierDate: * laterDate: * compare: earlierDate: and laterDate: are great, but it would be nice to have a boolean response to help when building logic in code; to easily ask "is this date earlier than that one?". DateTools has a set of proxy methods that do just that as well as a few other methods for extended flexibility. The new methods are: * isEarlierThan * isEarlierThanOrEqualTo * isLaterThan * isLaterThanOrEqualTo These methods are great for comparing dates in a boolean fashion, but what if we want to compare the dates and return some meaningful information about how far they are apart? NSDate comes with two methods timeIntervalSinceDate: and timeIntervalSinceNow which gives you a double offset representing the number of seconds between the two dates. This is great and all, but there are times when one wants to know how many years or days are between two dates. For this, DateTools goes back to the ever-trusty NSCalendar and abstracts out all the necessary code for you. With Date Tools, this: ```objc NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:[NSDate defaultCalendar]]; NSDate *earliest = [firstDate earlierDate:secondDate]; NSDate *latest = (secondDate == firstDate) ? secondDate : firstDate; NSInteger multiplier = (secondDate == firstDate) ? -1 : 1; NSDateComponents *components = [calendar components:allCalendarUnitFlags fromDate:earliest toDate:latest options:0]; NSInteger yearsApart = multiplier*(components.month + 12*components.year); ``` ..becomes this: ```objc NSInteger yearsApart = [firstDate yearsFrom:secondDate]; ``` Methods for comparison in this category include: * yearsFrom:, yearsUntil, yearsAgo, yearsEarlierThan:, yearsLaterThan: * monthsFrom:, monthsUntil, monthsAgo, monthsEarlierThan:, monthsLaterThan: * weeksFrom:, weeksUntil, weeksAgo, weeksEarlierThan:, weeksLaterThan: * daysFrom:, daysUntil, daysAgo, daysEarlierThan:, daysLaterThan: * hoursFrom:, hoursUntil, hoursAgo, hoursEarlierThan:, hoursLaterThan: * minutesFrom:, minutesUntil, minutesAgo, minutesEarlierThan:, minutesLaterThan: * secondsFrom:, secondsUntil, secondsAgo, secondsEarlierThan:, secondsLaterThan: ####Formatted Date Strings Just for kicks, DateTools has a few convenience methods for quickly creating strings from dates. Those two methods are formattedDateWithStyle: and formattedDateWithFormat:. The current locale is used unless otherwise specified by additional method parameters. Again, just for kicks, really. ##Time Periods Dates are important, but the real world is a little less discrete than that. Life is made up of spans of time, like an afternoon appointment or a weeklong vacation. In DateTools, time periods are represented by the DTTimePeriod class and come with a suite of initializaiton, manipulation, and comparison methods to make working with them a breeze. ####Initialization Time peroids consist of an NSDate start date and end date. To initialize a time period, call the init function. ```objc DTTimePeriod *timePeriod = [[DTTimePeriod alloc] initWithStartDate:startDate endDate:endDate]; ``` or, if you would like to create a time period of a known length that starts or ends at a certain time, try out a few other init methods. The method below, for example, creates a time period starting at the current time that is exactly 5 hours long. ```objc DTTimePeriod *timePeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeHour amount:5 startingAt:[NSDate date]]; ``` ####Time Period Info A host of methods have been extended to give information about an instance of DTTimePeriod. A few are listed below * hasStartDate - Returns true if the period has a start date * hasEndDate - Returns true if the period has an end date * isMoment - Returns true if the period has the same start and end date * durationIn.... - Returns the length of the time period in the requested units ####Manipulation Time periods may also be manipulated. They may be shifted earlier or later as well as expanded and contracted. **Shifting** When a time period is shifted, the start dates and end dates are both moved earlier or later by the amounts requested. To shift a time period earlier, call shiftEarlierWithSize:amount: and to shift it later, call shiftLaterWithSize:amount:. The amount field serves as a multipler, just like in the above initializaion method. **Lengthening/Shortening** When a time periods is lengthened or shortened, it does so anchoring one date of the time period and then changing the other one. There is also an option to anchor the centerpoint of the time period, changing both the start and end dates. An example of lengthening a time period is shown below: ```objc DTTimePeriod *timePeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeMinute endingAt:[NSDate date]]; [timePeriod lengthenWithAnchorDate:DTTimePeriodAnchorEnd size:DTTimePeriodSizeMinute amount:1]; ``` This doubles a time period of duration 1 minute to duration 2 minutes. The end date of "now" is retained and only the start date is shifted 1 minute earlier. ####Relationships There may come a need, say when you are making a scheduling app, when it might be good to know how two time periods relate to one another. Are they the same? Is one inside of another? All these questions may be asked using the relationship methods of DTTimePeriod. **The Basics** Below is a chart of all the possible relationships between two time periods: ![TimePeriods](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/PeriodRelations.png) A suite of methods have been extended to check for the basic relationships. They are listed below: * isEqualToPeriod: * isInside: * contains: * overlapsWith: * intersects: You can also check for the official relationship (like those shown in the chart) with the following method: ```objc -(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period; ``` All of the possible relationships have been enumerated in the DTTimePeriodRelation enum. **For a better grasp on how time periods relate to one another, check out the "Time Periods" tab in the example application. Here you can slide a few time periods around and watch their relationships change.** ![TimePeriods](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/TimePeriodsDemo.gif) ##Time Period Groups Time period groups are the final abstraction of date and time in DateTools. Here, time periods are gathered and organized into something useful. There are two main types of time period groups, DTTimePeriodCollection and DTTimePeriodChain. At a high level, think about a collection as a loose group where overlaps may occur and a chain a more linear, tight group where overlaps are not allowed. Both collections and chains operate like an NSArray. You may add,insert and remove DTTimePeriod objects from them just as you would objects in an array. The difference is how these periods are handled under the hood. ###Time Period Collections Time period collections serve as loose sets of time periods. They are unorganized unless you decide to sort them, and have their own characteristics like a StartDate and EndDate that are extrapolated from the time periods within. Time period collections allow overlaps within their set of time periods. ![TimePeriodCollections](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/TimePeriodCollection.png) To make a new collection, call the class method like so: ```objc //Create collection DTTimePeriodCollection *collection = [DTTimePeriodCollection collection]; //Create a few time periods DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[dateFormatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[dateFormatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[dateFormatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[dateFormatter dateFromString:@"2016 11 05 18:15:12.000"]]; //Add time periods to the colleciton [collection addTimePeriod:firstPeriod]; [collection addTimePeriod:secondPeriod]; //Retreive collection items DTTimePeriod *firstPeriod = collection[0]; ``` **Sorting** Sorting time periods in a collection is easy, just call one of the sort methods. There are a total of three sort options, listed below: * **Start Date** - sortByStartAscending, sortByStartDescending * **End Date** - sortByEndAscending, sortByEndDescending * **Time Period Duration** - sortByDurationAscending, sortByDurationDescending **Operations** It is also possible to check an NSDate's or DTTimePeriod's relationship to the collection. For instance, if you would like to see all the time periods that intersect with a certain date, you can call the periodsIntersectedByDate: method. The result is a new DTTimePeriodCollection with all those periods that intersect the provided date. There are a host of other methods to try out as well, including a full equality check between two collections. ![TimePeriodCollectionOperations](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/TimePeriodCollectionOperations.png) ###Time Period Chains Time period chains serve as a tightly coupled set of time periods. They are always organized by start and end date, and have their own characteristics like a StartDate and EndDate that are extrapolated from the time periods within. Time period chains do not allow overlaps within their set of time periods. This type of group is ideal for modeling schedules like sequential meetings or appointments. ![TimePeriodChains](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/TimePeriodChain.png) To make a new chain, call the class method like so: ```objc //Create chain DTTimePeriodChain *chain = [DTTimePeriodChain chain]; //Create a few time periods DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[dateFormatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[dateFormatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[dateFormatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[dateFormatter dateFromString:@"2016 11 05 18:15:12.000"]]; //Add test periods [chain addTimePeriod:firstPeriod]; [chain addTimePeriod:secondPeriod]; //Retreive chain items DTTimePeriod *firstPeriod = chain[0]; ``` Any time a date is added to the time chain, it retains its duration, but is modified to have its StartDate be the same as the latest period in the chain's EndDate. This helps keep the tightly coupled structure of the chain's time periods. Inserts (besides those at index 0) shift dates after insertion index by the duration of the new time period while leaving those at indexes before untouched. Insertions at index 0 shift the start date of the collection by the duration of the new time period. A full list of operations can be seen below. **Operations** Like collections, chains have an equality check and the ability to be shifted earlier and later. Here is a short list of other operations. ![TimePeriodChainOperations](https://raw.githubusercontent.com/MatthewYork/Resources/master/DateTools/TimePeriodChainOperations.png) ##Documentation All methods and variables have been documented and are available for option+click inspection, just like the SDK classes. This includes an explanation of the methods as well as what their input and output parameters are for. Please raise an issue if you ever feel documentation is confusing or misleading and we will get it fixed up! ##Unit Tests Unit tests were performed on all the major classes in the library for quality assurance. You can find theses under the "Tests" folder at the top of the library. There are over 300 test cases in all! If you ever find a test case that is incomplete, please open an issue so we can get it fixed. Continuous integration testing is performed by Travis CI: [![Build Status](https://travis-ci.org/MatthewYork/DateTools.svg?branch=master)](https://travis-ci.org/MatthewYork/DateTools) ##Credits Thanks to [Kevin Lawler](https://github.com/kevinlawler) for his initial work on NSDate+TimeAgo. It laid the foundation for DateTools' timeAgo methods. You can find this great project [here](https://github.com/kevinlawler/NSDate-TimeAgo). Many thanks to the .NET team for their DateTime class and a major thank you to [Jani Giannoudis](http://www.codeproject.com/Members/Jani-Giannoudis) for his work on ITimePeriod. Images were first published through itenso.com through [Code Project](http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET) I would also like to thank **God** through whom all things live and move and have their being. [Acts 17:28](http://www.biblegateway.com/passage/?search=Acts+17%3A16-34&version=NIV) ##License The MIT License (MIT) Copyright (c) 2014 Matthew York Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/AppDelegate.h ================================================ // // AppDelegate.h // DateToolsTests // // Created by Matthew York on 3/22/14. // // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/AppDelegate.m ================================================ // // AppDelegate.m // DateToolsTests // // Created by Matthew York on 3/22/14. // // #import "AppDelegate.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/Base.lproj/Main.storyboard ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/DateToolsTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.mattyork.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/DateToolsTests-Prefix.pch ================================================ // // Prefix header // // The contents of this file are implicitly included at the beginning of every source file. // #import #ifndef __IPHONE_5_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif #ifdef __OBJC__ #import #import #endif ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/Images.xcassets/LaunchImage.launchimage/Contents.json ================================================ { "images" : [ { "orientation" : "portrait", "idiom" : "iphone", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", "subtype" : "retina4", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/ViewController.h ================================================ // // ViewController.h // DateToolsTests // // Created by Matthew York on 3/22/14. // // #import @interface ViewController : UIViewController @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/ViewController.m ================================================ // // ViewController.m // DateToolsTests // // Created by Matthew York on 3/22/14. // // #import "ViewController.h" #import "NSDate+DateTools.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //Time ago test NSLog(@"10 months Ago: %@", [[NSDate date] dateBySubtractingMonths:10].timeAgoSinceNow); NSLog(@"8 weeks Ago: %@", [[NSDate date] dateBySubtractingWeeks:8].timeAgoSinceNow); NSLog(@"3 days Ago: %@", [[NSDate date] dateBySubtractingDays:3].timeAgoSinceNow); NSLog(@"2 hours Ago: %@", [[NSDate date] dateBySubtractingHours:2].timeAgoSinceNow); NSLog(@"5 minutes Ago: %@", [[NSDate date] dateBySubtractingMinutes:5].timeAgoSinceNow); NSLog(@"1 second Ago: %@", [[NSDate date] dateBySubtractingSeconds:1].timeAgoSinceNow); NSLog(@"now Ago: %@", [NSDate date].timeAgoSinceNow); //Short time ago test NSLog(@"10 months Ago: %@", [[NSDate date] dateBySubtractingMonths:10].shortTimeAgoSinceNow); NSLog(@"8 weeks Ago: %@", [[NSDate date] dateBySubtractingWeeks:8].shortTimeAgoSinceNow); NSLog(@"3 days Ago: %@", [[NSDate date] dateBySubtractingDays:3].shortTimeAgoSinceNow); NSLog(@"2 hours Ago: %@", [[NSDate date] dateBySubtractingHours:2].shortTimeAgoSinceNow); NSLog(@"5 minutes Ago: %@", [[NSDate date] dateBySubtractingMinutes:5].shortTimeAgoSinceNow); NSLog(@"1 second Ago: %@", [[NSDate date] dateBySubtractingSeconds:1].shortTimeAgoSinceNow); NSLog(@"now Ago: %@", [NSDate date].timeAgoSinceNow); //Test formatters NSString *dateStringFormatTest = [[NSDate date] formattedDateWithFormat:@"dd MMM, yyyy"]; NSString *dateStringStyleTest = [[NSDate date] formattedDateWithStyle:NSDateFormatterLongStyle timeZone:[NSTimeZone localTimeZone] locale:[NSLocale currentLocale]]; NSString *dateStringStyleTest2 = [[NSDate date] formattedDateWithStyle:NSDateFormatterShortStyle timeZone:[NSTimeZone localTimeZone] locale:[NSLocale currentLocale]]; NSLog(@"%@", dateStringFormatTest); NSLog(@"%@", dateStringStyleTest); NSLog(@"%@", dateStringStyleTest2); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/es.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/es.lproj/Main.strings ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/ja.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/ja.lproj/Main.strings ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests/main.m ================================================ // // main.m // DateToolsTests // // Created by Matthew York on 3/22/14. // // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 0AFD485E18F082FE004D0FE1 /* DateTools.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0AFD485D18F082FE004D0FE1 /* DateTools.bundle */; }; 0AFD486018F08640004D0FE1 /* DTTimeAgoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AFD485F18F08640004D0FE1 /* DTTimeAgoTests.m */; }; F00762C218DE5D7500A99075 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F00762C118DE5D7500A99075 /* Foundation.framework */; }; F00762C418DE5D7500A99075 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F00762C318DE5D7500A99075 /* CoreGraphics.framework */; }; F00762C618DE5D7500A99075 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F00762C518DE5D7500A99075 /* UIKit.framework */; }; F00762CC18DE5D7500A99075 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F00762CA18DE5D7500A99075 /* InfoPlist.strings */; }; F00762CE18DE5D7500A99075 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762CD18DE5D7500A99075 /* main.m */; }; F00762D218DE5D7500A99075 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762D118DE5D7500A99075 /* AppDelegate.m */; }; F00762D518DE5D7500A99075 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F00762D318DE5D7500A99075 /* Main.storyboard */; }; F00762D818DE5D7500A99075 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762D718DE5D7500A99075 /* ViewController.m */; }; F00762DA18DE5D7500A99075 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F00762D918DE5D7500A99075 /* Images.xcassets */; }; F00762E118DE5D7500A99075 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F00762E018DE5D7500A99075 /* XCTest.framework */; }; F00762E218DE5D7500A99075 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F00762C118DE5D7500A99075 /* Foundation.framework */; }; F00762E318DE5D7500A99075 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F00762C518DE5D7500A99075 /* UIKit.framework */; }; F00762EB18DE5D7500A99075 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F00762E918DE5D7500A99075 /* InfoPlist.strings */; }; F00762FB18DE5D9900A99075 /* DateToolsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762F618DE5D9900A99075 /* DateToolsTests.m */; }; F00762FC18DE5D9900A99075 /* DTTimePeriodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762F718DE5D9900A99075 /* DTTimePeriodTests.m */; }; F00762FD18DE5D9900A99075 /* DTTimePeriodGroupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762F818DE5D9900A99075 /* DTTimePeriodGroupTests.m */; }; F00762FE18DE5D9900A99075 /* DTTimePeriodChainTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762F918DE5D9900A99075 /* DTTimePeriodChainTests.m */; }; F00762FF18DE5D9900A99075 /* DTTimePeriodCollectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F00762FA18DE5D9900A99075 /* DTTimePeriodCollectionTests.m */; }; F007631018DE5DC400A99075 /* NSDate+DateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = F007630518DE5DC400A99075 /* NSDate+DateTools.m */; }; F007631218DE5DC400A99075 /* DTTimePeriod.m in Sources */ = {isa = PBXBuildFile; fileRef = F007630718DE5DC400A99075 /* DTTimePeriod.m */; }; F007631618DE5DC400A99075 /* DTTimePeriodCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = F007630B18DE5DC400A99075 /* DTTimePeriodCollection.m */; }; F075ABC91B9A52DA00AC95C8 /* DTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = F075ABC81B9A52DA00AC95C8 /* DTConstants.m */; }; F0EE17F318DEB1C20010FAD8 /* DTError.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17EE18DEB1C20010FAD8 /* DTError.m */; }; F0EE17F518DEB1C20010FAD8 /* DTTimePeriodChain.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17F018DEB1C20010FAD8 /* DTTimePeriodChain.m */; }; F0EE17F718DEB1C20010FAD8 /* DTTimePeriodGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0EE17F218DEB1C20010FAD8 /* DTTimePeriodGroup.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ F00762E418DE5D7500A99075 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F00762B618DE5D7500A99075 /* Project object */; proxyType = 1; remoteGlobalIDString = F00762BD18DE5D7500A99075; remoteInfo = DateToolsTests; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 0AFD485D18F082FE004D0FE1 /* DateTools.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = DateTools.bundle; path = ../../../DateTools/DateTools.bundle; sourceTree = ""; }; 0AFD485F18F08640004D0FE1 /* DTTimeAgoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimeAgoTests.m; sourceTree = ""; }; 0AFD486118F0AB99004D0FE1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Main.strings; sourceTree = ""; }; 0AFD486218F0AB99004D0FE1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; 0AFD486318F0AB99004D0FE1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; F00762BE18DE5D7500A99075 /* DateToolsTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DateToolsTests.app; sourceTree = BUILT_PRODUCTS_DIR; }; F00762C118DE5D7500A99075 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; F00762C318DE5D7500A99075 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; F00762C518DE5D7500A99075 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; F00762C918DE5D7500A99075 /* DateToolsTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DateToolsTests-Info.plist"; sourceTree = ""; }; F00762CB18DE5D7500A99075 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; F00762CD18DE5D7500A99075 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; F00762CF18DE5D7500A99075 /* DateToolsTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DateToolsTests-Prefix.pch"; sourceTree = ""; }; F00762D018DE5D7500A99075 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; F00762D118DE5D7500A99075 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; F00762D418DE5D7500A99075 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; F00762D618DE5D7500A99075 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; F00762D718DE5D7500A99075 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; F00762D918DE5D7500A99075 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; F00762DF18DE5D7500A99075 /* DateToolsTestsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DateToolsTestsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F00762E018DE5D7500A99075 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; F00762E818DE5D7500A99075 /* DateToolsTestsTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DateToolsTestsTests-Info.plist"; sourceTree = ""; }; F00762EA18DE5D7500A99075 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; F00762F618DE5D9900A99075 /* DateToolsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateToolsTests.m; sourceTree = ""; }; F00762F718DE5D9900A99075 /* DTTimePeriodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodTests.m; sourceTree = ""; }; F00762F818DE5D9900A99075 /* DTTimePeriodGroupTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodGroupTests.m; sourceTree = ""; }; F00762F918DE5D9900A99075 /* DTTimePeriodChainTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodChainTests.m; sourceTree = ""; }; F00762FA18DE5D9900A99075 /* DTTimePeriodCollectionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTTimePeriodCollectionTests.m; sourceTree = ""; }; F007630118DE5DC400A99075 /* DTConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTConstants.h; path = ../../../DateTools/DTConstants.h; sourceTree = ""; }; F007630418DE5DC400A99075 /* NSDate+DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDate+DateTools.h"; path = "../../../DateTools/NSDate+DateTools.h"; sourceTree = ""; }; F007630518DE5DC400A99075 /* NSDate+DateTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDate+DateTools.m"; path = "../../../DateTools/NSDate+DateTools.m"; sourceTree = ""; }; F007630618DE5DC400A99075 /* DTTimePeriod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriod.h; path = ../../../DateTools/DTTimePeriod.h; sourceTree = ""; }; F007630718DE5DC400A99075 /* DTTimePeriod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriod.m; path = ../../../DateTools/DTTimePeriod.m; sourceTree = ""; }; F007630A18DE5DC400A99075 /* DTTimePeriodCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriodCollection.h; path = ../../../DateTools/DTTimePeriodCollection.h; sourceTree = ""; }; F007630B18DE5DC400A99075 /* DTTimePeriodCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodCollection.m; path = ../../../DateTools/DTTimePeriodCollection.m; sourceTree = ""; }; F022EC1418F44D3700743E17 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Main.strings; sourceTree = ""; }; F075ABC81B9A52DA00AC95C8 /* DTConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTConstants.m; path = ../../../DateTools/DTConstants.m; sourceTree = ""; }; F0EE17ED18DEB1C20010FAD8 /* DTError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTError.h; path = ../../../DateTools/DTError.h; sourceTree = ""; }; F0EE17EE18DEB1C20010FAD8 /* DTError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTError.m; path = ../../../DateTools/DTError.m; sourceTree = ""; }; F0EE17EF18DEB1C20010FAD8 /* DTTimePeriodChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriodChain.h; path = ../../../DateTools/DTTimePeriodChain.h; sourceTree = ""; }; F0EE17F018DEB1C20010FAD8 /* DTTimePeriodChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodChain.m; path = ../../../DateTools/DTTimePeriodChain.m; sourceTree = ""; }; F0EE17F118DEB1C20010FAD8 /* DTTimePeriodGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTTimePeriodGroup.h; path = ../../../DateTools/DTTimePeriodGroup.h; sourceTree = ""; }; F0EE17F218DEB1C20010FAD8 /* DTTimePeriodGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTTimePeriodGroup.m; path = ../../../DateTools/DTTimePeriodGroup.m; sourceTree = ""; }; F0EE17F918DEB1CF0010FAD8 /* DateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DateTools.h; path = ../../../DateTools/DateTools.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ F00762BB18DE5D7500A99075 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( F00762C418DE5D7500A99075 /* CoreGraphics.framework in Frameworks */, F00762C618DE5D7500A99075 /* UIKit.framework in Frameworks */, F00762C218DE5D7500A99075 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; F00762DC18DE5D7500A99075 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( F00762E118DE5D7500A99075 /* XCTest.framework in Frameworks */, F00762E318DE5D7500A99075 /* UIKit.framework in Frameworks */, F00762E218DE5D7500A99075 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ F00762B518DE5D7500A99075 = { isa = PBXGroup; children = ( F00762C718DE5D7500A99075 /* DateToolsTests */, F00762E618DE5D7500A99075 /* DateToolsTestsTests */, F00762C018DE5D7500A99075 /* Frameworks */, F00762BF18DE5D7500A99075 /* Products */, ); sourceTree = ""; }; F00762BF18DE5D7500A99075 /* Products */ = { isa = PBXGroup; children = ( F00762BE18DE5D7500A99075 /* DateToolsTests.app */, F00762DF18DE5D7500A99075 /* DateToolsTestsTests.xctest */, ); name = Products; sourceTree = ""; }; F00762C018DE5D7500A99075 /* Frameworks */ = { isa = PBXGroup; children = ( F00762C118DE5D7500A99075 /* Foundation.framework */, F00762C318DE5D7500A99075 /* CoreGraphics.framework */, F00762C518DE5D7500A99075 /* UIKit.framework */, F00762E018DE5D7500A99075 /* XCTest.framework */, ); name = Frameworks; sourceTree = ""; }; F00762C718DE5D7500A99075 /* DateToolsTests */ = { isa = PBXGroup; children = ( F007630018DE5DB000A99075 /* DateTools */, F00762D018DE5D7500A99075 /* AppDelegate.h */, F00762D118DE5D7500A99075 /* AppDelegate.m */, F00762D318DE5D7500A99075 /* Main.storyboard */, F00762D618DE5D7500A99075 /* ViewController.h */, F00762D718DE5D7500A99075 /* ViewController.m */, F00762D918DE5D7500A99075 /* Images.xcassets */, F00762C818DE5D7500A99075 /* Supporting Files */, ); path = DateToolsTests; sourceTree = ""; }; F00762C818DE5D7500A99075 /* Supporting Files */ = { isa = PBXGroup; children = ( F00762C918DE5D7500A99075 /* DateToolsTests-Info.plist */, F00762CA18DE5D7500A99075 /* InfoPlist.strings */, F00762CD18DE5D7500A99075 /* main.m */, F00762CF18DE5D7500A99075 /* DateToolsTests-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; F00762E618DE5D7500A99075 /* DateToolsTestsTests */ = { isa = PBXGroup; children = ( F00762F618DE5D9900A99075 /* DateToolsTests.m */, F00762F718DE5D9900A99075 /* DTTimePeriodTests.m */, F00762F818DE5D9900A99075 /* DTTimePeriodGroupTests.m */, F00762F918DE5D9900A99075 /* DTTimePeriodChainTests.m */, F00762FA18DE5D9900A99075 /* DTTimePeriodCollectionTests.m */, 0AFD485F18F08640004D0FE1 /* DTTimeAgoTests.m */, F00762E718DE5D7500A99075 /* Supporting Files */, ); path = DateToolsTestsTests; sourceTree = ""; }; F00762E718DE5D7500A99075 /* Supporting Files */ = { isa = PBXGroup; children = ( F00762E818DE5D7500A99075 /* DateToolsTestsTests-Info.plist */, F00762E918DE5D7500A99075 /* InfoPlist.strings */, ); name = "Supporting Files"; sourceTree = ""; }; F007630018DE5DB000A99075 /* DateTools */ = { isa = PBXGroup; children = ( 0AFD485D18F082FE004D0FE1 /* DateTools.bundle */, F0EE17F918DEB1CF0010FAD8 /* DateTools.h */, F0EE17ED18DEB1C20010FAD8 /* DTError.h */, F0EE17EE18DEB1C20010FAD8 /* DTError.m */, F007630118DE5DC400A99075 /* DTConstants.h */, F075ABC81B9A52DA00AC95C8 /* DTConstants.m */, F007630418DE5DC400A99075 /* NSDate+DateTools.h */, F007630518DE5DC400A99075 /* NSDate+DateTools.m */, F007630618DE5DC400A99075 /* DTTimePeriod.h */, F007630718DE5DC400A99075 /* DTTimePeriod.m */, F0EE17F118DEB1C20010FAD8 /* DTTimePeriodGroup.h */, F0EE17F218DEB1C20010FAD8 /* DTTimePeriodGroup.m */, F007630A18DE5DC400A99075 /* DTTimePeriodCollection.h */, F007630B18DE5DC400A99075 /* DTTimePeriodCollection.m */, F0EE17EF18DEB1C20010FAD8 /* DTTimePeriodChain.h */, F0EE17F018DEB1C20010FAD8 /* DTTimePeriodChain.m */, ); name = DateTools; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ F00762BD18DE5D7500A99075 /* DateToolsTests */ = { isa = PBXNativeTarget; buildConfigurationList = F00762F018DE5D7500A99075 /* Build configuration list for PBXNativeTarget "DateToolsTests" */; buildPhases = ( F00762BA18DE5D7500A99075 /* Sources */, F00762BB18DE5D7500A99075 /* Frameworks */, F00762BC18DE5D7500A99075 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = DateToolsTests; productName = DateToolsTests; productReference = F00762BE18DE5D7500A99075 /* DateToolsTests.app */; productType = "com.apple.product-type.application"; }; F00762DE18DE5D7500A99075 /* DateToolsTestsTests */ = { isa = PBXNativeTarget; buildConfigurationList = F00762F318DE5D7500A99075 /* Build configuration list for PBXNativeTarget "DateToolsTestsTests" */; buildPhases = ( F00762DB18DE5D7500A99075 /* Sources */, F00762DC18DE5D7500A99075 /* Frameworks */, F00762DD18DE5D7500A99075 /* Resources */, ); buildRules = ( ); dependencies = ( F00762E518DE5D7500A99075 /* PBXTargetDependency */, ); name = DateToolsTestsTests; productName = DateToolsTestsTests; productReference = F00762DF18DE5D7500A99075 /* DateToolsTestsTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ F00762B618DE5D7500A99075 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0510; TargetAttributes = { F00762DE18DE5D7500A99075 = { TestTargetID = F00762BD18DE5D7500A99075; }; }; }; buildConfigurationList = F00762B918DE5D7500A99075 /* Build configuration list for PBXProject "DateToolsTests" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ja, es, ); mainGroup = F00762B518DE5D7500A99075; productRefGroup = F00762BF18DE5D7500A99075 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( F00762BD18DE5D7500A99075 /* DateToolsTests */, F00762DE18DE5D7500A99075 /* DateToolsTestsTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ F00762BC18DE5D7500A99075 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( F00762DA18DE5D7500A99075 /* Images.xcassets in Resources */, F00762CC18DE5D7500A99075 /* InfoPlist.strings in Resources */, F00762D518DE5D7500A99075 /* Main.storyboard in Resources */, 0AFD485E18F082FE004D0FE1 /* DateTools.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; F00762DD18DE5D7500A99075 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( F00762EB18DE5D7500A99075 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ F00762BA18DE5D7500A99075 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F007631618DE5DC400A99075 /* DTTimePeriodCollection.m in Sources */, F0EE17F718DEB1C20010FAD8 /* DTTimePeriodGroup.m in Sources */, F00762D818DE5D7500A99075 /* ViewController.m in Sources */, F00762D218DE5D7500A99075 /* AppDelegate.m in Sources */, F0EE17F518DEB1C20010FAD8 /* DTTimePeriodChain.m in Sources */, F075ABC91B9A52DA00AC95C8 /* DTConstants.m in Sources */, F0EE17F318DEB1C20010FAD8 /* DTError.m in Sources */, F007631218DE5DC400A99075 /* DTTimePeriod.m in Sources */, F007631018DE5DC400A99075 /* NSDate+DateTools.m in Sources */, F00762CE18DE5D7500A99075 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F00762DB18DE5D7500A99075 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0AFD486018F08640004D0FE1 /* DTTimeAgoTests.m in Sources */, F00762FB18DE5D9900A99075 /* DateToolsTests.m in Sources */, F00762FD18DE5D9900A99075 /* DTTimePeriodGroupTests.m in Sources */, F00762FF18DE5D9900A99075 /* DTTimePeriodCollectionTests.m in Sources */, F00762FC18DE5D9900A99075 /* DTTimePeriodTests.m in Sources */, F00762FE18DE5D9900A99075 /* DTTimePeriodChainTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ F00762E518DE5D7500A99075 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F00762BD18DE5D7500A99075 /* DateToolsTests */; targetProxy = F00762E418DE5D7500A99075 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ F00762CA18DE5D7500A99075 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( F00762CB18DE5D7500A99075 /* en */, 0AFD486218F0AB99004D0FE1 /* ja */, ); name = InfoPlist.strings; sourceTree = ""; }; F00762D318DE5D7500A99075 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( F00762D418DE5D7500A99075 /* Base */, 0AFD486118F0AB99004D0FE1 /* ja */, F022EC1418F44D3700743E17 /* es */, ); name = Main.storyboard; sourceTree = ""; }; F00762E918DE5D7500A99075 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( F00762EA18DE5D7500A99075 /* en */, 0AFD486318F0AB99004D0FE1 /* ja */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ F00762EE18DE5D7500A99075 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; F00762EF18DE5D7500A99075 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; F00762F118DE5D7500A99075 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsTests/DateToolsTests-Prefix.pch"; INFOPLIST_FILE = "DateToolsTests/DateToolsTests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; F00762F218DE5D7500A99075 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsTests/DateToolsTests-Prefix.pch"; INFOPLIST_FILE = "DateToolsTests/DateToolsTests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; F00762F418DE5D7500A99075 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/DateToolsTests.app/DateToolsTests"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsTests/DateToolsTests-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = "DateToolsTestsTests/DateToolsTestsTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Debug; }; F00762F518DE5D7500A99075 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/DateToolsTests.app/DateToolsTests"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DateToolsTests/DateToolsTests-Prefix.pch"; INFOPLIST_FILE = "DateToolsTestsTests/DateToolsTestsTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ F00762B918DE5D7500A99075 /* Build configuration list for PBXProject "DateToolsTests" */ = { isa = XCConfigurationList; buildConfigurations = ( F00762EE18DE5D7500A99075 /* Debug */, F00762EF18DE5D7500A99075 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F00762F018DE5D7500A99075 /* Build configuration list for PBXNativeTarget "DateToolsTests" */ = { isa = XCConfigurationList; buildConfigurations = ( F00762F118DE5D7500A99075 /* Debug */, F00762F218DE5D7500A99075 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F00762F318DE5D7500A99075 /* Build configuration list for PBXNativeTarget "DateToolsTestsTests" */ = { isa = XCConfigurationList; buildConfigurations = ( F00762F418DE5D7500A99075 /* Debug */, F00762F518DE5D7500A99075 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = F00762B618DE5D7500A99075 /* Project object */; } ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests.xcodeproj/xcshareddata/xcschemes/DateToolsTests.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTests.xcodeproj/xcshareddata/xcschemes/DateToolsTestsTests.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DTTimeAgoTests.m ================================================ // // DTTimeAgoTests.m // DateToolsTests // // Created by kevin on 2014.04.05. // // #import #import "NSDate+DateTools.h" @interface DTTimeAgoTests : XCTestCase @property NSDateFormatter *formatter; @property NSDate *date0; @property NSDate *date1; @end @implementation DTTimeAgoTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"yyyy MM dd HH:mm:ss.SSS"]; self.date0 = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testBasicLongTimeAgo { NSString *now = [self.date0 timeAgoSinceDate:self.date0]; XCTAssert(now && now.length > 0, @"'Now' is nil or empty."); NSString *ago = [self.date1 timeAgoSinceDate:self.date0]; XCTAssert(ago && ago.length > 0, @"Ago is nil or empty."); } - (void)testLongTimeAgo2Days { self.date0 = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 timeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"2 days ago")); } - (void)testLongTimeAgo1DayAndHalf { self.date0 = [self.formatter dateFromString:@"2014 11 06 9:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 timeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"Yesterday")); } - (void)testLongTimeAgoExactlyYesterday { self.date0 = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 timeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"Yesterday")); } - (void)testLongTimeAgoLessThan24hoursButYesterday { self.date0 = [self.formatter dateFromString:@"2014 11 06 20:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 timeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"22 hours ago")); } - (void)testLongTimeAgoLessThan24hoursSameDay { self.date0 = [self.formatter dateFromString:@"2014 11 07 10:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 timeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"8 hours ago")); } - (void)testLongTimeAgoBetween24And48Hours { self.date0 = [self.formatter dateFromString:@"2014 11 07 10:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 08 18:15:12.000"]; NSString *ago = [self.date0 timeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"Yesterday")); } - (void)testBasicShortTimeAgo { NSString *now = [self.date0 shortTimeAgoSinceDate:self.date0]; XCTAssert(now && now.length > 0, @"'Now' is nil or empty."); NSString *ago = [self.date1 shortTimeAgoSinceDate:self.date0]; XCTAssert(ago && ago.length > 0, @"Ago is nil or empty."); } - (void)testShortTimeAgo2Days { self.date0 = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 shortTimeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"2d")); } - (void)testShortTimeAgo1DayAndHalf { self.date0 = [self.formatter dateFromString:@"2014 11 06 9:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 shortTimeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"1d")); } - (void)testShortTimeAgoExactlyYesterday { self.date0 = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 shortTimeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"1d")); } - (void)testShortTimeAgoLessThan24hoursButYesterday { self.date0 = [self.formatter dateFromString:@"2014 11 06 20:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 shortTimeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"22h")); } - (void)testShortTimeAgoLessThan24hoursSameDay { self.date0 = [self.formatter dateFromString:@"2014 11 07 10:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; NSString *ago = [self.date0 shortTimeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"8h")); } - (void)testShortTimeAgoBetween24And48Hours { self.date0 = [self.formatter dateFromString:@"2014 11 07 10:15:12.000"]; self.date1 = [self.formatter dateFromString:@"2014 11 08 18:15:12.000"]; NSString *ago = [self.date0 shortTimeAgoSinceDate:self.date1]; XCTAssertEqualObjects(ago, DateToolsLocalizedStrings(@"1d")); } - (void)testLongTimeAgoLocalizationsAccessible { NSString *en_local = @"Yesterday"; NSString *ja_local = @"昨日"; NSString *key = en_local; NSString *path = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"DateTools.bundle/ja.lproj"]; NSBundle *bundle = [NSBundle bundleWithPath:path]; NSString *ja_result = NSLocalizedStringFromTableInBundle(key, @"DateTools", bundle, nil); XCTAssertEqualObjects(ja_local, ja_result, @"Could not access localizations."); } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DTTimePeriodChainTests.m ================================================ // // DTTimePeriodChainTests.m // DateToolsExample // // Created by Matthew York on 3/21/14. // // #import #import "DTTimePeriodChain.h" @interface DTTimePeriodChainTests : XCTestCase @property NSDateFormatter *formatter; @property DTTimePeriodChain *controlChain; @end @implementation DTTimePeriodChainTests - (void)setUp { [super setUp]; //Initialize control DTTimePeriodChain self.controlChain = [[DTTimePeriodChain alloc] init]; //Initialize formatter self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"yyyy MM dd HH:mm:ss.SSS"]; //Create test DTTimePeriods that are 1 year long DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; //Add test periods [self.controlChain addTimePeriod:firstPeriod]; [self.controlChain addTimePeriod:secondPeriod]; [self.controlChain addTimePeriod:thirdPeriod]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } #pragma mark - Custom Init / Factory Chain -(void)testInitsAndFactories{ DTTimePeriodChain *initCompareChain = [[DTTimePeriodChain alloc] init]; DTTimePeriodChain *factoryCompareChain = [DTTimePeriodChain chain]; XCTAssertTrue([initCompareChain isEqualToChain:factoryCompareChain], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Chain Existence Manipulation -(void)testAddTimePeriod{ //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testInsertTimePeriod{ //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testChain insertTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]] atInedx:1]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testRemoveTimePeriodAtIndex{ //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [self.controlChain removeTimePeriodAtIndex:1]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testRemoveLatestTimePeriod{ //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [self.controlChain removeLatestTimePeriod]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testRemoveEarliestTimePeriod{ //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testChain shiftEarlierWithSize:DTTimePeriodSizeSecond amount:[[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]] durationInSeconds]]; [self.controlChain removeEarliestTimePeriod]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Chain Time Manipulation -(void)testShiftEarlier{ //Create test chain DTTimePeriodChain *testChainOriginal = [DTTimePeriodChain chain]; [testChainOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChainOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testChainOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2012 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; //Shift control chain [self.controlChain shiftEarlierWithSize:DTTimePeriodSizeYear amount:2]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); //Check equal XCTAssertFalse([self.controlChain isEqualToChain:testChainOriginal], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftLater{ //Create test chain DTTimePeriodChain *testChainOriginal = [DTTimePeriodChain chain]; [testChainOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChainOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testChainOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; //Create test chain DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2018 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2018 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2019 11 05 18:15:12.000"]]]; //Shift control chain [self.controlChain shiftLaterWithSize:DTTimePeriodSizeYear amount:2]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); //Check equal XCTAssertFalse([self.controlChain isEqualToChain:testChainOriginal], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Chain Relationship -(void)testIsEqualToChain{ //Create test chains DTTimePeriodChain *testChain = [DTTimePeriodChain chain]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; DTTimePeriodChain *testChainOutOfOrder = [DTTimePeriodChain chain]; [testChainOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testChainOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testChainOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; //Check equal XCTAssertTrue([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); //Check unequal [testChain addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; XCTAssertFalse([self.controlChain isEqualToChain:testChain], @"%s Failed", __PRETTY_FUNCTION__); //Check same periods out of order XCTAssertFalse([self.controlChain isEqualToChain:testChainOutOfOrder], @"%s Failed", __PRETTY_FUNCTION__); } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DTTimePeriodCollectionTests.m ================================================ // // DTTimePeriodCollectionTests.m // DateToolsExample // // Created by Matthew York on 3/21/14. // // #import #import "DTTimePeriodCollection.h" @interface DTTimePeriodCollectionTests : XCTestCase @property NSDateFormatter *formatter; @property DTTimePeriodCollection *controlCollection; @end @implementation DTTimePeriodCollectionTests - (void)setUp { [super setUp]; //Initialize control DTTimePeriodChain self.controlCollection = [[DTTimePeriodCollection alloc] init]; //Initialize formatter self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"yyyy MM dd HH:mm:ss.SSS"]; //Create test DTTimePeriods DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; DTTimePeriod *fourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Add test periods [self.controlCollection addTimePeriod:firstPeriod]; [self.controlCollection addTimePeriod:secondPeriod]; [self.controlCollection addTimePeriod:thirdPeriod]; [self.controlCollection addTimePeriod:fourthPeriod]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } #pragma mark - Custom Init / Factory Methods -(void)testInitsAndFactories{ DTTimePeriodCollection *initCompareCollection = [[DTTimePeriodCollection alloc] init]; DTTimePeriodCollection *factoryCompareCollection = [DTTimePeriodCollection collection]; XCTAssertTrue([initCompareCollection isEqualToCollection:factoryCompareCollection considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Collection Manipulation -(void)testAddTimePeriod{ //Initialize control DTTimePeriodChain DTTimePeriodCollection *testCollection = [[DTTimePeriodCollection alloc] init]; //Create test DTTimePeriods that are 1 year long DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; DTTimePeriod *fourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Add test periods [testCollection addTimePeriod:firstPeriod]; [testCollection addTimePeriod:secondPeriod]; [testCollection addTimePeriod:thirdPeriod]; [testCollection addTimePeriod:fourthPeriod]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollection considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testInsertTimePeriod{ //Initialize control DTTimePeriodChain DTTimePeriodCollection *testCollection = [[DTTimePeriodCollection alloc] init]; //Create test DTTimePeriods that are 1 year long DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; DTTimePeriod *fourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Add test periods [testCollection addTimePeriod:firstPeriod]; [testCollection addTimePeriod:secondPeriod]; [testCollection addTimePeriod:fourthPeriod]; [testCollection insertTimePeriod:thirdPeriod atIndex:2]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollection considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testRemoveTimePeriodAtIndex{ //Initialize control DTTimePeriodChain DTTimePeriodCollection *testCollection = [[DTTimePeriodCollection alloc] init]; //Create test DTTimePeriods that are 1 year long DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; DTTimePeriod *fourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Add test periods [testCollection addTimePeriod:firstPeriod]; [testCollection addTimePeriod:thirdPeriod]; [testCollection addTimePeriod:fourthPeriod]; //Remove time period from control [self.controlCollection removeTimePeriodAtIndex:1]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollection considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Chain Time Manipulation -(void)testShiftEarlier{ //Create test chain DTTimePeriodCollection *testCollectionOriginal = [DTTimePeriodCollection collection]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2012 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"]]]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"]]]; //Create test chain DTTimePeriodCollection *controlCopy = [self.controlCollection copy]; //Shift control chain [self.controlCollection shiftEarlierWithSize:DTTimePeriodSizeYear amount:2]; //Check equal XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOriginal considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); //Check equal XCTAssertFalse([self.controlCollection isEqualToCollection:controlCopy considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftLater{ //Create test chain DTTimePeriodCollection *testCollectionOriginal = [DTTimePeriodCollection collection]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2018 11 05 18:15:12.000"]]]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2018 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2019 11 05 18:15:12.000"]]]; [testCollectionOriginal addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2019 4 05 18:15:12.000"]]]; //Create test chain DTTimePeriodCollection *controlCopy = [self.controlCollection copy]; //Shift control chain [self.controlCollection shiftLaterWithSize:DTTimePeriodSizeYear amount:2]; //Check equal XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOriginal considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); //Check equal XCTAssertFalse([self.controlCollection isEqualToCollection:controlCopy considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Sorting -(void)testSortByStartAscending{ //Create ordered array DTTimePeriodCollection *testCollectionOrdered = [DTTimePeriodCollection collection]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; //Sort control [self.controlCollection sortByStartAscending]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOrdered considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSortByStartDescending{ //Create ordered array DTTimePeriodCollection *testCollectionOrdered = [DTTimePeriodCollection collection]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; //Sort control [self.controlCollection sortByStartDescending]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOrdered considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSortByEndAscending{ //Create ordered array DTTimePeriodCollection *testCollectionOrdered = [DTTimePeriodCollection collection]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; //Sort control [self.controlCollection sortByEndAscending]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOrdered considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSortByEndDescending{ //Create ordered array DTTimePeriodCollection *testCollectionOrdered = [DTTimePeriodCollection collection]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testCollectionOrdered addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; //Sort control [self.controlCollection sortByEndDescending]; XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOrdered considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSortByDurationAscending{ //Create some time periods to sort DTTimePeriod *period2Days = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 04 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 06 18:15:12.000"]]; DTTimePeriod *period4Months = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 07 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *period5Months = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 06 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *period2years = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Create unordered array DTTimePeriodCollection *testCollectionUnordered = [DTTimePeriodCollection collection]; [testCollectionUnordered addTimePeriod:period2years]; [testCollectionUnordered addTimePeriod:period5Months]; [testCollectionUnordered addTimePeriod:period4Months]; [testCollectionUnordered addTimePeriod:period2Days]; //Create ordered array DTTimePeriodCollection *testCollectionOrdered = [DTTimePeriodCollection collection]; [testCollectionOrdered addTimePeriod:period2Days]; [testCollectionOrdered addTimePeriod:period4Months]; [testCollectionOrdered addTimePeriod:period5Months]; [testCollectionOrdered addTimePeriod:period2years]; //Sort unordered [testCollectionUnordered sortByDurationAscending]; XCTAssertTrue([testCollectionUnordered isEqualToCollection:testCollectionOrdered considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSortByDurationDescending{ //Create some time periods to sort DTTimePeriod *period2Days = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 04 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 06 18:15:12.000"]]; DTTimePeriod *period4Months = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 07 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *period5Months = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 06 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *period2years = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Create unordered array DTTimePeriodCollection *testCollectionUnordered = [DTTimePeriodCollection collection]; [testCollectionUnordered addTimePeriod:period4Months]; [testCollectionUnordered addTimePeriod:period2Days]; [testCollectionUnordered addTimePeriod:period2years]; [testCollectionUnordered addTimePeriod:period5Months]; //Create ordered array DTTimePeriodCollection *testCollectionOrdered = [DTTimePeriodCollection collection]; [testCollectionOrdered addTimePeriod:period2years]; [testCollectionOrdered addTimePeriod:period5Months]; [testCollectionOrdered addTimePeriod:period4Months]; [testCollectionOrdered addTimePeriod:period2Days]; //Sort unordered [testCollectionUnordered sortByDurationDescending]; XCTAssertTrue([testCollectionUnordered isEqualToCollection:testCollectionOrdered considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Collection Relationship -(void)testPeriodsInside{ //Check positve match DTTimePeriod *testPeriodMatch = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 06 18:15:12.000"]]; DTTimePeriodCollection *testCollectionMatch = [DTTimePeriodCollection collection]; [testCollectionMatch addTimePeriod:self.controlCollection[0]]; XCTAssertTrue([testCollectionMatch isEqualToCollection:[self.controlCollection periodsInside:testPeriodMatch] considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); //Check too narrow DTTimePeriod *testPeriodNarrow = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 06 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 02 18:15:12.000"]]; DTTimePeriodCollection *testCollectionNarrow = [DTTimePeriodCollection collection]; XCTAssertTrue([testCollectionNarrow isEqualToCollection:[self.controlCollection periodsInside:testPeriodNarrow] considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); //Random no XCTAssertFalse([self.controlCollection isEqualToCollection:[self.controlCollection periodsInside:testPeriodMatch] considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testPeriodsIntersectedByDate{ //Check positve match NSDate *testDate = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; DTTimePeriodCollection *testCollectionMatch = [DTTimePeriodCollection collection]; [testCollectionMatch addTimePeriod:self.controlCollection[0]]; [testCollectionMatch addTimePeriod:self.controlCollection[1]]; [testCollectionMatch addTimePeriod:self.controlCollection[3]]; XCTAssertTrue([testCollectionMatch isEqualToCollection:[self.controlCollection periodsIntersectedByDate:testDate] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testPeriodsIntersectedByPeriod{ //Check positve match DTTimePeriod *testPeriodMatch = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriodCollection *testCollectionMatch = [DTTimePeriodCollection collection]; [testCollectionMatch addTimePeriod:self.controlCollection[0]]; [testCollectionMatch addTimePeriod:self.controlCollection[1]]; [testCollectionMatch addTimePeriod:self.controlCollection[3]]; XCTAssertTrue([testCollectionMatch isEqualToCollection:[self.controlCollection periodsIntersectedByPeriod:testPeriodMatch] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); //Check too early DTTimePeriod *testPeriodEarly = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2012 11 06 18:15:12.000"] endDate:[self.formatter dateFromString:@"2013 11 02 18:15:12.000"]]; DTTimePeriodCollection *testCollectionEarly = [DTTimePeriodCollection collection]; XCTAssertTrue([testCollectionEarly isEqualToCollection:[self.controlCollection periodsIntersectedByPeriod:testPeriodEarly] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); //Random no XCTAssertFalse([self.controlCollection isEqualToCollection:[self.controlCollection periodsIntersectedByPeriod:testPeriodMatch] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testPeriodsOverlappedByPeriod{ //Check positve match DTTimePeriod *testPeriodMatch = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriodCollection *testCollectionMatch = [DTTimePeriodCollection collection]; [testCollectionMatch addTimePeriod:self.controlCollection[0]]; [testCollectionMatch addTimePeriod:self.controlCollection[3]]; XCTAssertTrue([testCollectionMatch isEqualToCollection:[self.controlCollection periodsOverlappedByPeriod:testPeriodMatch] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); //Check too early DTTimePeriod *testPeriodEarly = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2012 11 06 18:15:12.000"] endDate:[self.formatter dateFromString:@"2013 11 02 18:15:12.000"]]; DTTimePeriodCollection *testCollectionEarly = [DTTimePeriodCollection collection]; XCTAssertTrue([testCollectionEarly isEqualToCollection:[self.controlCollection periodsOverlappedByPeriod:testPeriodEarly] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); //Random no XCTAssertFalse([self.controlCollection isEqualToCollection:[self.controlCollection periodsOverlappedByPeriod:testPeriodMatch] considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsEqualToCollection{ //Create test chains DTTimePeriodCollection *testCollection = [DTTimePeriodCollection collection]; [testCollection addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; [testCollection addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testCollection addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testCollection addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]]; DTTimePeriodCollection *testCollectionOutOfOrder = [DTTimePeriodCollection collection]; [testCollectionOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]]; [testCollectionOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]]; [testCollectionOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]]; [testCollectionOutOfOrder addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; //Check equal XCTAssertTrue([self.controlCollection isEqualToCollection:testCollection considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); //Check unequal [testCollection addTimePeriod:[DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]]; XCTAssertFalse([self.controlCollection isEqualToCollection:testCollection considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); //Check same periods out of order XCTAssertTrue([self.controlCollection isEqualToCollection:testCollectionOutOfOrder considerOrder:NO], @"%s Failed", __PRETTY_FUNCTION__); XCTAssertFalse([self.controlCollection isEqualToCollection:testCollectionOutOfOrder considerOrder:YES], @"%s Failed", __PRETTY_FUNCTION__); } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DTTimePeriodGroupTests.m ================================================ // // DTTimePeriodGroupTests.m // DateToolsExample // // Created by Matthew York on 3/22/14. // // #import #import "DTTimePeriodCollection.h" #import "DTTimePeriodChain.h" @interface DTTimePeriodGroupTests : XCTestCase @property NSDateFormatter *formatter; @property DTTimePeriodCollection *controlCollection; @end @implementation DTTimePeriodGroupTests - (void)setUp { [super setUp]; //Initialize control DTTimePeriodChain self.controlCollection = [[DTTimePeriodCollection alloc] init]; //Initialize formatter self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"yyyy MM dd HH:mm:ss.SSS"]; //Create test DTTimePeriods that are 1 year long DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; DTTimePeriod *fourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Add test periods [self.controlCollection addTimePeriod:firstPeriod]; [self.controlCollection addTimePeriod:secondPeriod]; [self.controlCollection addTimePeriod:thirdPeriod]; [self.controlCollection addTimePeriod:fourthPeriod]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } #pragma mark - Group Info -(void)testDurationInYears{ XCTAssertEqual(3, self.controlCollection.durationInYears, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInWeeks{ XCTAssertEqual(156, self.controlCollection.durationInWeeks, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInDays{ XCTAssertEqual(1096, self.controlCollection.durationInDays, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInHours{ XCTAssertEqual(26304, self.controlCollection.durationInHours, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInMinutes{ XCTAssertEqual(1578240, self.controlCollection.durationInMinutes, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInSeconds{ XCTAssertEqual(94694400, self.controlCollection.durationInSeconds, @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Comparison -(void)testHasSameCharacteristicsAs{ DTTimePeriodCollection *collectionSame = [[DTTimePeriodCollection alloc] init]; DTTimePeriodChain *chain = [[DTTimePeriodChain alloc] init]; //Create test DTTimePeriods to construct same as control DTTimePeriod *firstPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; DTTimePeriod *secondPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; DTTimePeriod *thirdPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 11 05 18:15:12.000"]]; DTTimePeriod *fourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; DTTimePeriod *alternateFourthPeriod = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 4 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2017 4 05 18:15:12.000"]]; //Add test periods [collectionSame addTimePeriod:firstPeriod]; [collectionSame addTimePeriod:secondPeriod]; [collectionSame addTimePeriod:thirdPeriod]; [collectionSame addTimePeriod:fourthPeriod]; [chain addTimePeriod:firstPeriod]; [chain addTimePeriod:secondPeriod]; [chain addTimePeriod:thirdPeriod]; [chain addTimePeriod:fourthPeriod]; //Test same as control XCTAssertTrue([self.controlCollection hasSameCharacteristicsAs:collectionSame], @"%s Failed", __PRETTY_FUNCTION__); //Test differnt chain XCTAssertFalse([self.controlCollection hasSameCharacteristicsAs:chain], @"%s Failed", __PRETTY_FUNCTION__); //Test alternate [collectionSame removeTimePeriodAtIndex:3]; [collectionSame addTimePeriod:alternateFourthPeriod]; XCTAssertTrue([self.controlCollection hasSameCharacteristicsAs:collectionSame], @"%s Failed", __PRETTY_FUNCTION__); } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DTTimePeriodTests.m ================================================ // // DTTimePeriodTests.m // DateToolsExample // // Created by Matthew York on 3/19/14. // // #import #import "DTTimePeriod.h" #import "NSDate+DateTools.h" @interface DTTimePeriodTests : XCTestCase @property NSDateFormatter *formatter; @property DTTimePeriod *controlTimePeriod; @end @implementation DTTimePeriodTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. self.controlTimePeriod = [[DTTimePeriod alloc] init]; //Create TimePeriod that is 2 years long self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"yyyy MM dd HH:mm:ss.SSS"]; self.controlTimePeriod.StartDate = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; self.controlTimePeriod.EndDate = [self.formatter dateFromString:@"2016 11 05 18:15:12.000"]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } #pragma mark - Custom Init / Factory Methods -(void)testBasicInitsAndFactoryMethods{ //Basic init DTTimePeriod *testPeriodInit = [[DTTimePeriod alloc] initWithStartDate:self.controlTimePeriod.StartDate endDate:self.controlTimePeriod.EndDate]; XCTAssertTrue([self.controlTimePeriod.StartDate isEqualToDate:testPeriodInit.StartDate] && [self.controlTimePeriod.EndDate isEqualToDate:testPeriodInit.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Basic factory DTTimePeriod *testPeriodFactoryInit = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:self.controlTimePeriod.EndDate]; XCTAssertTrue([self.controlTimePeriod.StartDate isEqualToDate:testPeriodFactoryInit.StartDate] && [self.controlTimePeriod.EndDate isEqualToDate:testPeriodFactoryInit.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testFactoryStartingAt{ //Test dates NSDate *startLaterSecond = [self.formatter dateFromString:@"2014 11 05 18:15:13.000"]; NSDate *startLaterMinute = [self.formatter dateFromString:@"2014 11 05 18:16:12.000"]; NSDate *startLaterHour = [self.formatter dateFromString:@"2014 11 05 19:15:12.000"]; NSDate *startLaterDay = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; NSDate *startLaterWeek = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; NSDate *startLaterMonth = [self.formatter dateFromString:@"2014 12 05 18:15:12.000"]; NSDate *startLaterYear = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; //Starting At //Second time period DTTimePeriod *testPeriodSecond = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodSecond.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodSecond.EndDate isEqualToDate:startLaterSecond], @"%s Failed", __PRETTY_FUNCTION__); //Minute time period DTTimePeriod *testPeriodMinute = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeMinute startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodMinute.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodMinute.EndDate isEqualToDate:startLaterMinute], @"%s Failed", __PRETTY_FUNCTION__); //Hour time period DTTimePeriod *testPeriodHour = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeHour startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodHour.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodHour.EndDate isEqualToDate:startLaterHour], @"%s Failed", __PRETTY_FUNCTION__); //Day time period DTTimePeriod *testPeriodDay = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeDay startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodDay.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodDay.EndDate isEqualToDate:startLaterDay], @"%s Failed", __PRETTY_FUNCTION__); //Week time period DTTimePeriod *testPeriodWeek = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeWeek startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodWeek.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodWeek.EndDate isEqualToDate:startLaterWeek], @"%s Failed", __PRETTY_FUNCTION__); //Month time period DTTimePeriod *testPeriodMonth = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeMonth startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodMonth.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodMonth.EndDate isEqualToDate:startLaterMonth], @"%s Failed", __PRETTY_FUNCTION__); //Year time period DTTimePeriod *testPeriodYear = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeYear startingAt:self.controlTimePeriod.StartDate]; XCTAssertTrue([testPeriodYear.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriodYear.EndDate isEqualToDate:startLaterYear], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testFactoryEndingAt { //Test End dates NSDate *endEarlierSecond = [self.formatter dateFromString:@"2016 11 05 18:15:11.000"]; NSDate *endEarlierMinute = [self.formatter dateFromString:@"2016 11 05 18:14:12.000"]; NSDate *endEarlierHour = [self.formatter dateFromString:@"2016 11 05 17:15:12.000"]; NSDate *endEarlierDay = [self.formatter dateFromString:@"2016 11 04 18:15:12.000"]; NSDate *endEarlierWeek = [self.formatter dateFromString:@"2016 10 29 18:15:12.000"]; NSDate *endEarlierMonth = [self.formatter dateFromString:@"2016 10 05 18:15:12.000"]; NSDate *endEarlierYear = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; //Ending At //Second time period DTTimePeriod *testPeriodSecond = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodSecond.StartDate isEqualToDate:endEarlierSecond] && [testPeriodSecond.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Minute time period DTTimePeriod *testPeriodMinute = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeMinute endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodMinute.StartDate isEqualToDate:endEarlierMinute] && [testPeriodMinute.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Hour time period DTTimePeriod *testPeriodHour = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeHour endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodHour.StartDate isEqualToDate:endEarlierHour] && [testPeriodHour.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Day time period DTTimePeriod *testPeriodDay = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeDay endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodDay.StartDate isEqualToDate:endEarlierDay] && [testPeriodDay.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Week time period DTTimePeriod *testPeriodWeek = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeWeek endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodWeek.StartDate isEqualToDate:endEarlierWeek] && [testPeriodWeek.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Month time period DTTimePeriod *testPeriodMonth = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeMonth endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodMonth.StartDate isEqualToDate:endEarlierMonth] && [testPeriodMonth.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); //Year time period DTTimePeriod *testPeriodYear = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeYear endingAt:self.controlTimePeriod.EndDate]; XCTAssertTrue([testPeriodYear.StartDate isEqualToDate:endEarlierYear] && [testPeriodYear.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Time Period Information -(void)testHasStartDate{ //Has start date XCTAssertTrue([self.controlTimePeriod hasStartDate], @"%s Failed", __PRETTY_FUNCTION__); //Deosn't have start date DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:nil endDate:self.controlTimePeriod.EndDate]; XCTAssertFalse([testPeriod hasStartDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testHasEndDate{ //Has end date XCTAssertTrue([self.controlTimePeriod hasEndDate], @"%s Failed", __PRETTY_FUNCTION__); //Deosn't have end date DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:nil]; XCTAssertFalse([testPeriod hasEndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsMoment{ //Is moment DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:self.controlTimePeriod.StartDate]; XCTAssertTrue(testPeriod.isMoment, @"%s Failed", __PRETTY_FUNCTION__); //Is not moment XCTAssertFalse(self.controlTimePeriod.isMoment, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInYears{ XCTAssertEqual(2, [self.controlTimePeriod durationInYears], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInWeeks{ XCTAssertEqual(104, [self.controlTimePeriod durationInWeeks], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInDays{ XCTAssertEqual(731, [self.controlTimePeriod durationInDays], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInHours{ DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:[self.controlTimePeriod.StartDate dateByAddingHours:4]]; XCTAssertEqual(4, [testPeriod durationInHours], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInMinutes{ DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:[self.controlTimePeriod.StartDate dateByAddingHours:4]]; XCTAssertEqual(240, [testPeriod durationInMinutes], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDurationInSeconds{ DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:[self.controlTimePeriod.StartDate dateByAddingHours:4]]; XCTAssertEqual(14400, [testPeriod durationInSeconds], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Time Period Relationship -(void)testIsSamePeriod{ //Same XCTAssertTrue([self.controlTimePeriod isEqualToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Different ending DTTimePeriod *differentEndPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:[self.controlTimePeriod.EndDate dateByAddingYears:1]]; XCTAssertFalse([self.controlTimePeriod isEqualToPeriod:differentEndPeriod], @"%s Failed", __PRETTY_FUNCTION__); //Different beginning DTTimePeriod *differentStartPeriod = [DTTimePeriod timePeriodWithStartDate:[self.controlTimePeriod.StartDate dateBySubtractingYears:1] endDate:self.controlTimePeriod.EndDate]; XCTAssertFalse([self.controlTimePeriod isEqualToPeriod:differentStartPeriod], @"%s Failed", __PRETTY_FUNCTION__); //Both endings different DTTimePeriod *differentStartAndEndPeriod = [DTTimePeriod timePeriodWithStartDate:[self.controlTimePeriod.StartDate dateBySubtractingYears:1] endDate:[self.controlTimePeriod.EndDate dateBySubtractingWeeks:1]]; XCTAssertFalse([self.controlTimePeriod isEqualToPeriod:differentStartAndEndPeriod], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsInside{ //POSITIVE MATCHES //Test exact match DTTimePeriod *testTimePeriodExact = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([testTimePeriodExact isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test same start DTTimePeriod *testTimePeriodSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; XCTAssertTrue([testTimePeriodSameStart isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test same end DTTimePeriod *testTimePeriodSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([testTimePeriodSameEnd isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test completely inside DTTimePeriod *testTimePeriodCompletelyInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 04 05 18:15:12.000"]]; XCTAssertTrue([testTimePeriodCompletelyInside isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //NEGATIVE MATCHES //Test before DTTimePeriod *testTimePeriodBefore = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 04 18:15:12.000"]]; XCTAssertFalse([testTimePeriodBefore isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test end same as start DTTimePeriod *testTimePeriodEndSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]; XCTAssertFalse([testTimePeriodEndSameStart isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test end inside DTTimePeriod *testTimePeriodEndInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"]]; XCTAssertFalse([testTimePeriodEndInside isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test start inside DTTimePeriod *testTimePeriodStartInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"]]; XCTAssertFalse([testTimePeriodStartInside isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test start same as end DTTimePeriod *testTimePeriodStartSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 10 18:15:12.000"]]; XCTAssertFalse([testTimePeriodStartSameEnd isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test after DTTimePeriod *testTimePeriodAfter = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 10 18:15:12.000"]]; XCTAssertFalse([testTimePeriodAfter isInside:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testContains{ //POSITIVE MATCHES //Test exact match DTTimePeriod *testTimePeriodExact = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod contains:testTimePeriodExact], @"%s Failed", __PRETTY_FUNCTION__); //Test same start DTTimePeriod *testTimePeriodSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod contains:testTimePeriodSameStart], @"%s Failed", __PRETTY_FUNCTION__); //Test same end DTTimePeriod *testTimePeriodSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod contains:testTimePeriodSameEnd], @"%s Failed", __PRETTY_FUNCTION__); //Test completely inside DTTimePeriod *testTimePeriodCompletelyInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 04 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod contains:testTimePeriodCompletelyInside], @"%s Failed", __PRETTY_FUNCTION__); //NEGATIVE MATCHES //Test before DTTimePeriod *testTimePeriodBefore = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 04 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod contains:testTimePeriodBefore], @"%s Failed", __PRETTY_FUNCTION__); //Test end same as start DTTimePeriod *testTimePeriodEndSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod contains:testTimePeriodEndSameStart], @"%s Failed", __PRETTY_FUNCTION__); //Test end inside DTTimePeriod *testTimePeriodEndInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod contains:testTimePeriodEndInside], @"%s Failed", __PRETTY_FUNCTION__); //Test start inside DTTimePeriod *testTimePeriodStartInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod contains:testTimePeriodStartInside], @"%s Failed", __PRETTY_FUNCTION__); //Test start same as end DTTimePeriod *testTimePeriodStartSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 10 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod contains:testTimePeriodStartSameEnd], @"%s Failed", __PRETTY_FUNCTION__); //Test after DTTimePeriod *testTimePeriodAfter = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 10 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod contains:testTimePeriodAfter], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testOverlapsWith{ //POSITIVE MATCHES //Test exact match DTTimePeriod *testTimePeriodExact = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod overlapsWith:testTimePeriodExact], @"%s Failed", __PRETTY_FUNCTION__); //Test same start DTTimePeriod *testTimePeriodSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod overlapsWith:testTimePeriodSameStart], @"%s Failed", __PRETTY_FUNCTION__); //Test same end DTTimePeriod *testTimePeriodSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod overlapsWith:testTimePeriodSameEnd], @"%s Failed", __PRETTY_FUNCTION__); //Test completely inside DTTimePeriod *testTimePeriodCompletelyInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 04 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod overlapsWith:testTimePeriodCompletelyInside], @"%s Failed", __PRETTY_FUNCTION__); //Test start inside DTTimePeriod *testTimePeriodStartInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod overlapsWith:testTimePeriodStartInside], @"%s Failed", __PRETTY_FUNCTION__); //Test end inside DTTimePeriod *testTimePeriodEndInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod overlapsWith:testTimePeriodEndInside], @"%s Failed", __PRETTY_FUNCTION__); //NEGATIVE MATCHES //Test before DTTimePeriod *testTimePeriodBefore = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 04 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod overlapsWith:testTimePeriodBefore], @"%s Failed", __PRETTY_FUNCTION__); //Test end same as start DTTimePeriod *testTimePeriodEndSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod overlapsWith:testTimePeriodEndSameStart], @"%s Failed", __PRETTY_FUNCTION__); //Test start same as end DTTimePeriod *testTimePeriodStartSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 10 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod overlapsWith:testTimePeriodStartSameEnd], @"%s Failed", __PRETTY_FUNCTION__); //Test after DTTimePeriod *testTimePeriodAfter = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 10 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod overlapsWith:testTimePeriodAfter], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIntersects{ //POSITIVE MATCHES //Test exact match DTTimePeriod *testTimePeriodExact = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodExact], @"%s Failed", __PRETTY_FUNCTION__); //Test same start DTTimePeriod *testTimePeriodSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodSameStart], @"%s Failed", __PRETTY_FUNCTION__); //Test same end DTTimePeriod *testTimePeriodSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodSameEnd], @"%s Failed", __PRETTY_FUNCTION__); //Test completely inside DTTimePeriod *testTimePeriodCompletelyInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 04 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodCompletelyInside], @"%s Failed", __PRETTY_FUNCTION__); //Test start inside DTTimePeriod *testTimePeriodStartInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodStartInside], @"%s Failed", __PRETTY_FUNCTION__); //Test end inside DTTimePeriod *testTimePeriodEndInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodEndInside], @"%s Failed", __PRETTY_FUNCTION__); //Test end same as start DTTimePeriod *testTimePeriodEndSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodEndSameStart], @"%s Failed", __PRETTY_FUNCTION__); //Test start same as end DTTimePeriod *testTimePeriodStartSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 10 18:15:12.000"]]; XCTAssertTrue([self.controlTimePeriod intersects:testTimePeriodStartSameEnd], @"%s Failed", __PRETTY_FUNCTION__); //NEGATIVE MATCHES //Test before DTTimePeriod *testTimePeriodBefore = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 04 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod intersects:testTimePeriodBefore], @"%s Failed", __PRETTY_FUNCTION__); //Test after DTTimePeriod *testTimePeriodAfter = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 10 18:15:12.000"]]; XCTAssertFalse([self.controlTimePeriod intersects:testTimePeriodAfter], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testRelationToPeriod{ //Test exact match DTTimePeriod *testTimePeriodExact = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationExactMatch, [testTimePeriodExact relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test same start DTTimePeriod *testTimePeriodSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2015 11 05 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationInsideStartTouching, [testTimePeriodSameStart relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test same end DTTimePeriod *testTimePeriodSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationInsideEndTouching, [testTimePeriodSameEnd relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test completely inside DTTimePeriod *testTimePeriodCompletelyInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2015 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 04 05 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationInside, [testTimePeriodCompletelyInside relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //NEGATIVE MATCHES //Test before DTTimePeriod *testTimePeriodBefore = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 04 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationBefore, [testTimePeriodBefore relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test end same as start DTTimePeriod *testTimePeriodEndSameStart = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2013 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 05 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationEndTouching, [testTimePeriodEndSameStart relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test end inside DTTimePeriod *testTimePeriodEndInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 02 18:15:12.000"] endDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationEndInside, [testTimePeriodEndInside relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test start inside DTTimePeriod *testTimePeriodStartInside = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2014 11 07 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationStartInside, [testTimePeriodStartInside relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test start same as end DTTimePeriod *testTimePeriodStartSameEnd = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 11 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 11 10 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationStartTouching, [testTimePeriodStartSameEnd relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //Test after DTTimePeriod *testTimePeriodAfter = [DTTimePeriod timePeriodWithStartDate:[self.formatter dateFromString:@"2016 12 05 18:15:12.000"] endDate:[self.formatter dateFromString:@"2016 12 10 18:15:12.000"]]; XCTAssertEqual(DTTimePeriodRelationAfter, [testTimePeriodAfter relationToPeriod:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testGapBetween{ //We are going to treat some of these as False=noGap and True=gap //No Gap Same XCTAssertFalse([self.controlTimePeriod gapBetween:self.controlTimePeriod], @"%s Failed", __PRETTY_FUNCTION__); //No Gap End Inside DTTimePeriod *testPeriodNoGap = [DTTimePeriod timePeriodWithStartDate:[self.controlTimePeriod.StartDate dateBySubtractingDays:1] endDate:[self.controlTimePeriod.EndDate dateBySubtractingDays:1]]; XCTAssertFalse([self.controlTimePeriod gapBetween:testPeriodNoGap], @"%s Failed", __PRETTY_FUNCTION__); //Gap receiver early DTTimePeriod *testPeriodReceiverEarly = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeWeek startingAt:[self.controlTimePeriod.EndDate dateByAddingYears:1]]; XCTAssertTrue([self.controlTimePeriod gapBetween:testPeriodReceiverEarly], @"%s Failed", __PRETTY_FUNCTION__); //Gap parameter early DTTimePeriod *testPeriodParameterEarly = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeWeek endingAt:[self.controlTimePeriod.StartDate dateBySubtractingYears:1]]; XCTAssertTrue([self.controlTimePeriod gapBetween:testPeriodParameterEarly], @"%s Failed", __PRETTY_FUNCTION__); //Gap of 1 minute DTTimePeriod *testPeriodParameter1MinuteEarly = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond endingAt:[self.controlTimePeriod.StartDate dateBySubtractingMinutes:1]]; XCTAssertEqual(60, [self.controlTimePeriod gapBetween:testPeriodParameter1MinuteEarly], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Date Relationships -(void)testContainsDate{ NSDate *testDateBefore = [self.formatter dateFromString:@"2014 10 05 18:15:12.000"]; NSDate *testDateBetween = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; NSDate *testDateAfter = [self.formatter dateFromString:@"2016 12 05 18:15:12.000"]; //Test before XCTAssertFalse([self.controlTimePeriod containsDate:testDateBefore interval:DTTimePeriodIntervalOpen], @"%s Failed", __PRETTY_FUNCTION__); XCTAssertFalse([self.controlTimePeriod containsDate:testDateBefore interval:DTTimePeriodIntervalClosed], @"%s Failed", __PRETTY_FUNCTION__); //Test on start date XCTAssertFalse([self.controlTimePeriod containsDate:self.controlTimePeriod.StartDate interval:DTTimePeriodIntervalOpen], @"%s Failed", __PRETTY_FUNCTION__); XCTAssertTrue([self.controlTimePeriod containsDate:self.controlTimePeriod.StartDate interval:DTTimePeriodIntervalClosed], @"%s Failed", __PRETTY_FUNCTION__); //Test in middle XCTAssertTrue([self.controlTimePeriod containsDate:testDateBetween interval:DTTimePeriodIntervalClosed], @"%s Failed", __PRETTY_FUNCTION__); XCTAssertTrue([self.controlTimePeriod containsDate:testDateBetween interval:DTTimePeriodIntervalClosed], @"%s Failed", __PRETTY_FUNCTION__); //Test on end date XCTAssertFalse([self.controlTimePeriod containsDate:self.controlTimePeriod.EndDate interval:DTTimePeriodIntervalOpen], @"%s Failed", __PRETTY_FUNCTION__); XCTAssertTrue([self.controlTimePeriod containsDate:self.controlTimePeriod.EndDate interval:DTTimePeriodIntervalClosed], @"%s Failed", __PRETTY_FUNCTION__); //Test after XCTAssertFalse([self.controlTimePeriod containsDate:testDateAfter interval:DTTimePeriodIntervalOpen], @"%s Failed", __PRETTY_FUNCTION__); XCTAssertFalse([self.controlTimePeriod containsDate:testDateAfter interval:DTTimePeriodIntervalClosed], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Period Manipulation #pragma mark Shift Earlier -(void)testShiftSecondEarlier{ NSDate *startEarlierSecond = [self.formatter dateFromString:@"2014 11 05 18:15:11.000"]; NSDate *endEarlierSecond = [self.formatter dateFromString:@"2016 11 05 18:15:11.000"]; //Second time period DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlierSecond endDate:endEarlierSecond]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeSecond]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftMinuteEarlier{ NSDate *startEarlier = [self.formatter dateFromString:@"2014 11 05 18:14:12.000"]; NSDate *endEarlier = [self.formatter dateFromString:@"2016 11 05 18:14:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlier endDate:endEarlier]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeMinute]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftHourEarlier{ NSDate *startEarlier = [self.formatter dateFromString:@"2014 11 05 17:15:12.000"]; NSDate *endEarlier = [self.formatter dateFromString:@"2016 11 05 17:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlier endDate:endEarlier]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeHour]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftDayEarlier{ NSDate *startEarlier = [self.formatter dateFromString:@"2014 11 04 18:15:12.000"]; NSDate *endEarlier = [self.formatter dateFromString:@"2016 11 04 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlier endDate:endEarlier]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeDay]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftWeekEarlier{ NSDate *startEarlier = [self.formatter dateFromString:@"2014 10 29 18:15:12.000"]; NSDate *endEarlier = [self.formatter dateFromString:@"2016 10 29 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlier endDate:endEarlier]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeWeek]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftMonthEarlier{ NSDate *startEarlier = [self.formatter dateFromString:@"2014 10 05 18:15:12.000"]; NSDate *endEarlier = [self.formatter dateFromString:@"2016 10 05 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlier endDate:endEarlier]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeMonth]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftYearEarlier{ NSDate *startEarlier = [self.formatter dateFromString:@"2013 11 05 18:15:12.000"]; NSDate *endEarlier = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startEarlier endDate:endEarlier]; [self.controlTimePeriod shiftEarlierWithSize:DTTimePeriodSizeYear]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Shift Later -(void)testShiftSecondLater{ NSDate *startLater = [self.formatter dateFromString:@"2014 11 05 18:15:13.000"]; NSDate *endLater = [self.formatter dateFromString:@"2016 11 05 18:15:13.000"]; //Second time period DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeSecond]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftMinuteLater{ NSDate *startLater = [self.formatter dateFromString:@"2014 11 05 18:16:12.000"]; NSDate *endLater = [self.formatter dateFromString:@"2016 11 05 18:16:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeMinute]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftHourLater{ NSDate *startLater = [self.formatter dateFromString:@"2014 11 05 19:15:12.000"]; NSDate *endLater = [self.formatter dateFromString:@"2016 11 05 19:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeHour]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftDayLater{ NSDate *startLater = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; NSDate *endLater = [self.formatter dateFromString:@"2016 11 06 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeDay]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftWeekLater{ NSDate *startLater = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; NSDate *endLater = [self.formatter dateFromString:@"2016 11 12 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeWeek]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftMonthLater{ NSDate *startLater = [self.formatter dateFromString:@"2014 12 05 18:15:12.000"]; NSDate *endLater = [self.formatter dateFromString:@"2016 12 05 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeMonth]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShiftYearLater{ NSDate *startLater = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; NSDate *endLater = [self.formatter dateFromString:@"2017 11 05 18:15:12.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:startLater endDate:endLater]; [self.controlTimePeriod shiftLaterWithSize:DTTimePeriodSizeYear]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Lengthen / Shorten -(void)testLengthenAnchorStart{ //Test dates NSDate *lengthenedEnd = [self.formatter dateFromString:@"2016 11 05 18:15:14.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:lengthenedEnd]; [self.controlTimePeriod lengthenWithAnchorDate:DTTimePeriodAnchorStart size:DTTimePeriodSizeSecond amount:2]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testLengthenAnchorCenter{ //Test dates NSDate *lengthenedStart = [self.formatter dateFromString:@"2014 11 05 18:15:11.000"]; NSDate *lengthenedEnd = [self.formatter dateFromString:@"2016 11 05 18:15:13.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:lengthenedStart endDate:lengthenedEnd]; [self.controlTimePeriod lengthenWithAnchorDate:DTTimePeriodAnchorCenter size:DTTimePeriodSizeSecond amount:2]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testLengthenAnchorEnd{ //Test dates NSDate *lengthenedStart = [self.formatter dateFromString:@"2014 11 05 18:15:10.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:lengthenedStart endDate:self.controlTimePeriod.EndDate]; [self.controlTimePeriod lengthenWithAnchorDate:DTTimePeriodAnchorEnd size:DTTimePeriodSizeSecond amount:2]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShortenAnchorStart{ //Test dates NSDate *shortenedEnd = [self.formatter dateFromString:@"2016 11 05 18:15:10.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:self.controlTimePeriod.StartDate endDate:shortenedEnd]; [self.controlTimePeriod shortenWithAnchorDate:DTTimePeriodAnchorStart size:DTTimePeriodSizeSecond amount:2]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShortenAnchorCenter{ //Test dates NSDate *shortenedStart = [self.formatter dateFromString:@"2014 11 05 18:15:13.000"]; NSDate *shortenedEnd = [self.formatter dateFromString:@"2016 11 05 18:15:11.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:shortenedStart endDate:shortenedEnd]; [self.controlTimePeriod shortenWithAnchorDate:DTTimePeriodAnchorCenter size:DTTimePeriodSizeSecond amount:2]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testShortenAnchorEnd{ //Test dates NSDate *shortenedStart = [self.formatter dateFromString:@"2014 11 05 18:15:14.000"]; DTTimePeriod *testPeriod = [DTTimePeriod timePeriodWithStartDate:shortenedStart endDate:self.controlTimePeriod.EndDate]; [self.controlTimePeriod shortenWithAnchorDate:DTTimePeriodAnchorEnd size:DTTimePeriodSizeSecond amount:2]; XCTAssertTrue([testPeriod.StartDate isEqualToDate:self.controlTimePeriod.StartDate] && [testPeriod.EndDate isEqualToDate:self.controlTimePeriod.EndDate], @"%s Failed", __PRETTY_FUNCTION__); } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DateToolsTests.m ================================================ // // DateToolsTests.m // DateToolsExample // // Created by Matthew York on 3/19/14. // // #import #import "NSDate+DateTools.h" @interface DateToolsTests : XCTestCase @property NSDateFormatter *formatter; @property NSDate *controlDate; @end @implementation DateToolsTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. self.formatter = [[NSDateFormatter alloc] init]; [self.formatter setDateFormat:@"yyyy MM dd HH:mm:ss.SSS"]; self.controlDate = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } #pragma mark - Date Components - (void)testEra { XCTAssertEqual(1, [[NSDate date] era], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testYear{ XCTAssertEqual(2014, self.controlDate.year, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testMonth{ XCTAssertEqual(11, self.controlDate.month, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDay{ XCTAssertEqual(5, self.controlDate.day, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testHour{ XCTAssertEqual(18, self.controlDate.hour, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testMinute{ XCTAssertEqual(15, self.controlDate.minute, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testSecond{ XCTAssertEqual(12, self.controlDate.second, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testWeekday{ XCTAssertEqual(4, self.controlDate.weekday, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testWeekdayOrdinal{ XCTAssertEqual(1, self.controlDate.weekdayOrdinal, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testQuarter{ //Quarter is a little funky right now //XCTAssertEqual(4, self.testDate.quarter, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testWeekOfMonth{ XCTAssertEqual(2, self.controlDate.weekOfMonth, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testWeekOfYear{ XCTAssertEqual(45, self.controlDate.weekOfYear, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testYearForWeekOfYear{ XCTAssertEqual(2014, self.controlDate.yearForWeekOfYear, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDaysInMonth{ XCTAssertEqual(30, self.controlDate.daysInMonth, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDaysInYear{ //Non leap year (2014) XCTAssertEqual(365, self.controlDate.daysInYear, @"%s Failed", __PRETTY_FUNCTION__); //Leap year (2000) XCTAssertEqual(366, [self.controlDate dateBySubtractingYears:14].daysInYear, @"%s Failed", __PRETTY_FUNCTION__); } - (void)testIsInLeapYear{ //Not leap year XCTAssertFalse([self.controlDate isInLeapYear], @"%s Failed", __PRETTY_FUNCTION__); //Is leap year (%400) 2000 XCTAssertTrue([[self.controlDate dateBySubtractingYears:14] isInLeapYear], @"%s Failed", __PRETTY_FUNCTION__); //Not leap year (%100) 1900 XCTAssertFalse([[self.controlDate dateBySubtractingYears:114] isInLeapYear], @"%s Failed", __PRETTY_FUNCTION__); //Is leap year (%4) 2016 XCTAssertTrue([[self.controlDate dateByAddingYears:2] isInLeapYear], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsToday{ //Test true now XCTAssertTrue([NSDate date].isToday, @"%s Failed", __PRETTY_FUNCTION__); //Test true past (Technically, could fail if you ran the test precisely at midnight, but...) XCTAssertTrue([[NSDate date] dateBySubtractingSeconds:1].isToday, @"%s Failed", __PRETTY_FUNCTION__); //Test true future (Technically, could fail if you ran the test precisely at midnight, but...) XCTAssertTrue([[NSDate date] dateByAddingSeconds:1].isToday, @"%s Failed", __PRETTY_FUNCTION__); //Tests false past XCTAssertFalse([[NSDate date] dateBySubtractingDays:2].isToday, @"%s Failed", __PRETTY_FUNCTION__); //Tests false future XCTAssertFalse([[NSDate date] dateByAddingDays:1].isToday, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsTomorrow{ //Test false with now XCTAssertFalse([NSDate date].isTomorrow, @"%s Failed", __PRETTY_FUNCTION__); //Test false past XCTAssertFalse([[NSDate date] dateBySubtractingDays:1].isTomorrow, @"%s Failed", __PRETTY_FUNCTION__); //Test true future XCTAssertTrue([[NSDate date] dateByAddingDays:1].isTomorrow, @"%s Failed", __PRETTY_FUNCTION__); //Tests false future XCTAssertFalse([[NSDate date] dateByAddingDays:2].isTomorrow, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsYesterday{ //Test false with now XCTAssertFalse([NSDate date].isYesterday, @"%s Failed", __PRETTY_FUNCTION__); //Test true past XCTAssertTrue([[NSDate date] dateBySubtractingDays:1].isYesterday, @"%s Failed", __PRETTY_FUNCTION__); //Test false future XCTAssertFalse([[NSDate date] dateByAddingDays:1].isYesterday, @"%s Failed", __PRETTY_FUNCTION__); //Tests false future XCTAssertFalse([[NSDate date] dateBySubtractingDays:2].isYesterday, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsWeekend{ //Created test dates NSDate *testFriday = [self.formatter dateFromString:@"2015 09 04 12:45:12.000"]; NSDate *testMonday = [self.formatter dateFromString:@"2015 02 16 00:00:00.000"]; NSDate *testWeekend = [self.formatter dateFromString:@"2015 09 05 17:45:12.000"]; //Test false with friday and monday XCTAssertFalse(testFriday.isWeekend, @"%s Failed", __PRETTY_FUNCTION__); XCTAssertFalse(testMonday.isWeekend, @"%s Failed", __PRETTY_FUNCTION__); //Test true past XCTAssertTrue(testWeekend.isWeekend, @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsSameDay { //Test same time stamp XCTAssertTrue([[NSDate date] isSameDay:[NSDate date]], @"%s Failed", __PRETTY_FUNCTION__); //Test true same day NSDate *testSameDay1 = [self.formatter dateFromString:@"2014 11 05 12:45:12.000"]; NSDate *testSameDay2 = [self.formatter dateFromString:@"2014 11 05 17:45:12.000"]; XCTAssertTrue([testSameDay1 isSameDay:testSameDay2], @"%s Failed", __PRETTY_FUNCTION__); //Test false 1 day ahead XCTAssertFalse([testSameDay1 isSameDay:[[NSDate date] dateByAddingDays:1]], @"%s Failed", __PRETTY_FUNCTION__); //Test false 1 day before XCTAssertFalse([testSameDay1 isSameDay:[[NSDate date] dateBySubtractingDays:1]], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsSameDayStatic { //Test true same time stamp XCTAssertTrue([NSDate isSameDay:[NSDate date] asDate:[NSDate date]], @"%s Failed", __PRETTY_FUNCTION__); //Test true same day NSDate *testSameDay1 = [self.formatter dateFromString:@"2014 11 05 12:45:12.000"]; NSDate *testSameDay2 = [self.formatter dateFromString:@"2014 11 05 17:45:12.000"]; XCTAssertTrue([NSDate isSameDay:testSameDay1 asDate:testSameDay2], @"%s Failed", __PRETTY_FUNCTION__); //Test false 1 day ahead XCTAssertFalse([NSDate isSameDay:[NSDate date] asDate:[[NSDate date] dateByAddingDays:1]], @"%s Failed", __PRETTY_FUNCTION__); //Test false 1 day before XCTAssertFalse([NSDate isSameDay:[NSDate date] asDate:[[NSDate date] dateBySubtractingDays:1]], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Date Editing #pragma mark Date Creating - (void)testDateWithYearMonthDayHourMinuteSecond{ XCTAssertEqual(YES, [self.controlDate isEqualToDate:[NSDate dateWithYear:2014 month:11 day:5 hour:18 minute:15 second:12]], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateWithStringFormatStringTimeZone { NSDate *testDate = [NSDate dateWithString:@"2015-02-27T18:15:00" formatString:@"yyyy-MM-dd'T'HH:mm:ss" timeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; XCTAssertEqual(YES, [testDate isEqualToDate:[NSDate dateWithString:@"2015-02-27T19:15:00" formatString:@"yyyy-MM-dd'T'HH:mm:ss" timeZone:[NSTimeZone timeZoneWithName:@"Europe/Warsaw"]]], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Date By Adding - (void)testDateByAddingYears{ NSDate *testDate = [self.formatter dateFromString:@"2016 11 05 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingYears:2] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateByAddingMonths{ NSDate *testDate = [self.formatter dateFromString:@"2015 01 05 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingMonths:2] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateByAddingWeeks{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingWeeks:1] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateByAddingDays{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 07 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingDays:2] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateByAddingHours{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 06 6:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingHours:12] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateByAddingMinutes{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 18:30:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingMinutes:15] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateByAddingSeconds{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 18:16:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateByAddingSeconds:60] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Date By Subtracting - (void)testDateBySubtractingYears{ NSDate *testDate = [self.formatter dateFromString:@"2000 11 05 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingYears:14] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateBySubtractingMonths{ NSDate *testDate = [self.formatter dateFromString:@"2014 4 05 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingMonths:7] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateBySubtractingWeeks{ NSDate *testDate = [self.formatter dateFromString:@"2014 10 29 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingWeeks:1] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateBySubtractingDays{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 01 18:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingDays:4] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateBySubtractingHours{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 00:15:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingHours:18] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateBySubtractingMinutes{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 17:45:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingMinutes:30] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } - (void)testDateBySubtractingSeconds{ NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 18:14:12.000"]; XCTAssertEqual(YES, [[self.controlDate dateBySubtractingSeconds:60] isEqualToDate:testDate], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark - Date Comparison #pragma mark Time From -(void)testYearsFrom{ //Under a year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a year NSDate *testDate2 = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; XCTAssertEqual(-1, [self.controlDate yearsFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Year number later, still less than a year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 04 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsFrom:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Year number earlier, still less than a year NSDate *testDate5 = [self.formatter dateFromString:@"2013 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsFrom:testDate5], @"%s Failed", __PRETTY_FUNCTION__); //Over a year earlier NSDate *testDate6 = [self.formatter dateFromString:@"2012 11 04 18:15:12.000"]; XCTAssertEqual(2, [self.controlDate yearsFrom:testDate6], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later NSDate *testDate7 = [self.formatter dateFromString:@"2017 11 12 18:15:12.000"]; XCTAssertEqual(-3, [self.controlDate yearsFrom:testDate7], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later, but less than a year in final comparison year NSDate *testDate8 = [self.formatter dateFromString:@"2017 11 3 18:15:12.000"]; XCTAssertEqual(-2, [self.controlDate yearsFrom:testDate8], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year earlier, but less than a year in final comparison year NSDate *testDate9 = [self.formatter dateFromString:@"2012 11 8 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate yearsFrom:testDate9], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testMonthsFrom{ //Under a month NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a month NSDate *testDate2 = [self.formatter dateFromString:@"2014 12 05 18:15:12.000"]; XCTAssertEqual(-1, [self.controlDate monthsFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Year number later, still less than a year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 04 18:15:12.000"]; XCTAssertEqual(-11, [self.controlDate monthsFrom:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Year number earlier, still less than a year NSDate *testDate5 = [self.formatter dateFromString:@"2013 11 06 18:15:12.000"]; XCTAssertEqual(11, [self.controlDate monthsFrom:testDate5], @"%s Failed", __PRETTY_FUNCTION__); //Over a year earlier NSDate *testDate6 = [self.formatter dateFromString:@"2012 11 04 18:15:12.000"]; XCTAssertEqual(24, [self.controlDate monthsFrom:testDate6], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later NSDate *testDate7 = [self.formatter dateFromString:@"2017 11 12 18:15:12.000"]; XCTAssertEqual(-36, [self.controlDate monthsFrom:testDate7], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testWeeksFrom{ //Same week NSDate *testSameDate = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate weeksFrom:testSameDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(-1, [self.controlDate weeksFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Eariler year NSDate *testDate2 = [self.formatter dateFromString:@"2013 11 12 18:15:12.000"]; XCTAssertEqual(51, [self.controlDate weeksFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Later year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 12 18:15:12.000"]; XCTAssertEqual(-53, [self.controlDate weeksFrom:testDate3], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDaysFrom{ //Same day NSDate *testSameDate = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate daysFrom:testSameDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(-7, [self.controlDate daysFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Eariler year NSDate *testDate2 = [self.formatter dateFromString:@"2013 11 12 18:15:12.000"]; XCTAssertEqual(358, [self.controlDate daysFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Later year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 12 18:15:12.000"]; XCTAssertEqual(-372, [self.controlDate daysFrom:testDate3], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testHoursFrom{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(-2, [self.controlDate hoursFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(3, [self.controlDate hoursFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testMinutesFrom{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(-120, [self.controlDate minutesFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(180, [self.controlDate minutesFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSecondsFrom{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(-7200, [self.controlDate secondsFrom:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(10800, [self.controlDate secondsFrom:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Earlier Than -(void)testYearsEarlierThan{ //Under a year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a year NSDate *testDate2 = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate yearsEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Year number later, still less than a year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 04 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsEarlierThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Year number earlier, still less than a year NSDate *testDate5 = [self.formatter dateFromString:@"2013 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsEarlierThan:testDate5], @"%s Failed", __PRETTY_FUNCTION__); //Over a year earlier NSDate *testDate6 = [self.formatter dateFromString:@"2012 11 04 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsEarlierThan:testDate6], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later NSDate *testDate7 = [self.formatter dateFromString:@"2017 11 12 18:15:12.000"]; XCTAssertEqual(3, [self.controlDate yearsEarlierThan:testDate7], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later, but less than a year in final comparison year NSDate *testDate8 = [self.formatter dateFromString:@"2017 11 3 18:15:12.000"]; XCTAssertEqual(2, [self.controlDate yearsEarlierThan:testDate8], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year earlier, but less than a year in final comparison year NSDate *testDate9 = [self.formatter dateFromString:@"2012 11 8 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsEarlierThan:testDate9], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testMonthsEarlerThan{ //Under a month NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a month NSDate *testDate2 = [self.formatter dateFromString:@"2014 12 05 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate monthsEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Year number later, still less than a year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 04 18:15:12.000"]; XCTAssertEqual(11, [self.controlDate monthsEarlierThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Year number earlier, still less than a year NSDate *testDate5 = [self.formatter dateFromString:@"2013 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsEarlierThan:testDate5], @"%s Failed", __PRETTY_FUNCTION__); //Over a year earlier NSDate *testDate6 = [self.formatter dateFromString:@"2012 11 04 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsEarlierThan:testDate6], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later NSDate *testDate7 = [self.formatter dateFromString:@"2017 11 12 18:15:12.000"]; XCTAssertEqual(36, [self.controlDate monthsEarlierThan:testDate7], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testWeeksEarlierThan{ //Same week NSDate *testSameDate = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate weeksEarlierThan:testSameDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate weeksEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Eariler year NSDate *testDate2 = [self.formatter dateFromString:@"2013 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate weeksEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Later year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 12 18:15:12.000"]; XCTAssertEqual(53, [self.controlDate weeksEarlierThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDaysEarlierThan{ //Same day NSDate *testSameDate = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate daysEarlierThan:testSameDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(7, [self.controlDate daysEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Eariler year NSDate *testDate2 = [self.formatter dateFromString:@"2013 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate daysEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Later year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 12 18:15:12.000"]; XCTAssertEqual(372, [self.controlDate daysEarlierThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testHoursEarlierThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(2, [self.controlDate hoursEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(0, [self.controlDate hoursEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testMinutesEarlierThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(120, [self.controlDate minutesEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(0, [self.controlDate minutesEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSecondsEarlierThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(7200, [self.controlDate secondsEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(0, [self.controlDate secondsEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Later Than -(void)testYearsLaterThan{ //Under a year NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a year later NSDate *testDate2 = [self.formatter dateFromString:@"2015 11 05 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a year earlier NSDate *testDate3 = [self.formatter dateFromString:@"2013 11 05 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate yearsLaterThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Year number later, still less than a year NSDate *testDate4 = [self.formatter dateFromString:@"2015 11 04 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsLaterThan:testDate4], @"%s Failed", __PRETTY_FUNCTION__); //Year number earlier, still less than a year NSDate *testDate5 = [self.formatter dateFromString:@"2013 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsLaterThan:testDate5], @"%s Failed", __PRETTY_FUNCTION__); //Over a year earlier NSDate *testDate6 = [self.formatter dateFromString:@"2012 11 04 18:15:12.000"]; XCTAssertEqual(2, [self.controlDate yearsLaterThan:testDate6], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later NSDate *testDate7 = [self.formatter dateFromString:@"2017 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsLaterThan:testDate7], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later, but less than a year in final comparison year NSDate *testDate8 = [self.formatter dateFromString:@"2017 11 3 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate yearsLaterThan:testDate8], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year earlier, but less than a year in final comparison year NSDate *testDate9 = [self.formatter dateFromString:@"2012 11 8 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate yearsLaterThan:testDate9], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testMonthsLaterThan{ //Under a month NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Exactly a month NSDate *testDate2 = [self.formatter dateFromString:@"2014 12 05 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Year number later, still less than a year NSDate *testDate3 = [self.formatter dateFromString:@"2015 11 04 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsLaterThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Year number earlier, still less than a year NSDate *testDate5 = [self.formatter dateFromString:@"2013 11 06 18:15:12.000"]; XCTAssertEqual(11, [self.controlDate monthsLaterThan:testDate5], @"%s Failed", __PRETTY_FUNCTION__); //Over a year earlier NSDate *testDate6 = [self.formatter dateFromString:@"2012 11 04 18:15:12.000"]; XCTAssertEqual(24, [self.controlDate monthsLaterThan:testDate6], @"%s Failed", __PRETTY_FUNCTION__); ///Over a year later NSDate *testDate7 = [self.formatter dateFromString:@"2017 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate monthsLaterThan:testDate7], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testWeeksLaterThan{ //Same week NSDate *testSameDate = [self.formatter dateFromString:@"2014 11 06 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate weeksLaterThan:testSameDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year later NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate weeksLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 10 24 18:15:12.000"]; XCTAssertEqual(1, [self.controlDate weeksLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Eariler year NSDate *testDate3 = [self.formatter dateFromString:@"2013 11 12 18:15:12.000"]; XCTAssertEqual(51, [self.controlDate weeksLaterThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Later year NSDate *testDate4 = [self.formatter dateFromString:@"2015 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate weeksLaterThan:testDate4], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testDaysLaterThan{ //Same day NSDate *testSameDate = [self.formatter dateFromString:@"2014 11 05 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate daysLaterThan:testSameDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year later NSDate *testDate = [self.formatter dateFromString:@"2014 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate daysLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Same year earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 3 18:15:12.000"]; XCTAssertEqual(2, [self.controlDate daysLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Eariler year NSDate *testDate3 = [self.formatter dateFromString:@"2013 11 12 18:15:12.000"]; XCTAssertEqual(358, [self.controlDate daysLaterThan:testDate3], @"%s Failed", __PRETTY_FUNCTION__); //Later year NSDate *testDate4 = [self.formatter dateFromString:@"2015 11 12 18:15:12.000"]; XCTAssertEqual(0, [self.controlDate daysLaterThan:testDate4], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testHoursLaterThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(0, [self.controlDate hoursLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(3, [self.controlDate hoursLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testMinutesLaterThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(0, [self.controlDate minutesLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(180, [self.controlDate minutesLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testSecondsLaterThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(0, [self.controlDate secondsLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(10800, [self.controlDate secondsLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); } #pragma mark Comparators -(void)testIsEarlierThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(YES, [self.controlDate isEarlierThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(NO, [self.controlDate isEarlierThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Same XCTAssertEqual(NO, [self.controlDate isEarlierThan:self.controlDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsLaterThan{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(NO, [self.controlDate isLaterThan:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(YES, [self.controlDate isLaterThan:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Same XCTAssertEqual(NO, [self.controlDate isLaterThan:self.controlDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testisEarlierThanOrEqualTo{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(YES, [self.controlDate isEarlierThanOrEqualTo:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(NO, [self.controlDate isEarlierThanOrEqualTo:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Same XCTAssertEqual(YES, [self.controlDate isEarlierThanOrEqualTo:self.controlDate], @"%s Failed", __PRETTY_FUNCTION__); } -(void)testIsLaterOrEqualToDate{ //Later NSDate *testDate = [self.formatter dateFromString:@"2014 11 05 20:15:12.000"]; XCTAssertEqual(NO, [self.controlDate isLaterThanOrEqualTo:testDate], @"%s Failed", __PRETTY_FUNCTION__); //Earlier NSDate *testDate2 = [self.formatter dateFromString:@"2014 11 05 15:15:12.000"]; XCTAssertEqual(YES, [self.controlDate isLaterThanOrEqualTo:testDate2], @"%s Failed", __PRETTY_FUNCTION__); //Same XCTAssertEqual(YES, [self.controlDate isLaterThanOrEqualTo:self.controlDate], @"%s Failed", __PRETTY_FUNCTION__); } @end ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/DateToolsTestsTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.mattyork.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/es.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/DateTools/Tests/DateToolsTests/DateToolsTestsTests/ja.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/LaunchAtLoginController/LaunchAtLoginController.h ================================================ // // LaunchAtLoginController.h // // Copyright 2013 Siddharth Gupta // Copyright 2011 Tomáš Znamenáček // Copyright 2010 Ben Clark-Robinson // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the ‘Software’), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @import Foundation; @interface LaunchAtLoginController : NSObject @property(assign) BOOL launchAtLogin; - (BOOL) willLaunchAtLogin: (NSURL*) itemURL; - (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL; @end ================================================ FILE: archive/bitbar/App/Vendor/LaunchAtLoginController/LaunchAtLoginController.m ================================================ // // LaunchAtLoginController.m // // Copyright 2013 Siddharth Gupta // Copyright 2011 Tomáš Znamenáček // Copyright 2010 Ben Clark-Robinson // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the ‘Software’), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #import "LaunchAtLoginController.h" static NSString *const StartAtLoginKey = @"launchAtLogin"; @interface LaunchAtLoginController () @property(assign) LSSharedFileListRef loginItems; @end @implementation LaunchAtLoginController @synthesize loginItems; #pragma mark Change Observing void sharedFileListDidChange(LSSharedFileListRef inList, void *context) { LaunchAtLoginController *self = (__bridge id) context; [self willChangeValueForKey:StartAtLoginKey]; [self didChangeValueForKey:StartAtLoginKey]; } #pragma mark Initialization - (id) init { loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (__bridge void *)(self)); return self; } - (void) dealloc { LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (__bridge void *)(self)); CFRelease(loginItems); } #pragma mark Launch List Control - (LSSharedFileListItemRef) findItemWithURL: (NSURL*) wantedURL inFileList: (LSSharedFileListRef) fileList { if (wantedURL == NULL || fileList == NULL) return NULL; NSArray *listSnapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(fileList, NULL)); for (id itemObject in listSnapshot) { LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef) itemObject; UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; CFURLRef currentItemURL = NULL; LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(wantedURL))) { CFRelease(currentItemURL); return (LSSharedFileListItemRef)CFRetain(item); } if (currentItemURL) CFRelease(currentItemURL); } return NULL; } - (BOOL) willLaunchAtLogin: (NSURL*) itemURL { LSSharedFileListItemRef appItem = [self findItemWithURL:itemURL inFileList:loginItems]; BOOL hasAppItem = appItem != nil; if (appItem) { CFRelease(appItem); } return hasAppItem; } - (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL { LSSharedFileListItemRef appItem = [self findItemWithURL:itemURL inFileList:loginItems]; if (enabled && !appItem) { LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, (__bridge CFURLRef)itemURL, NULL, NULL); if (item) { CFRelease(item); } } else if (!enabled && appItem) LSSharedFileListItemRemove(loginItems, appItem); if (appItem) { CFRelease(appItem); } } #pragma mark Basic Interface - (NSURL*) appURL { return [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; } - (void) setLaunchAtLogin: (BOOL) enabled { [self willChangeValueForKey:StartAtLoginKey]; [self setLaunchAtLogin:enabled forURL:[self appURL]]; [self didChangeValueForKey:StartAtLoginKey]; } - (BOOL) launchAtLogin { return [self willLaunchAtLogin:[self appURL]]; } @end ================================================ FILE: archive/bitbar/App/Vendor/LaunchAtLoginController/README.md ================================================ # LaunchAtLoginController (using ARC) A modified version of the original by Mozketo (at https://github.com/Mozketo/LaunchAtLoginController) with support for ARC (Automatic Reference Counting). Tested on Mac OSX 10.8.3 without any problems. ## Description: A very simple controller for use in Cocoa Mac Apps to register/deregister itself for Launch at Login using LSSharedFileList (In simple terms, helps your Cocoa/Objective-C App to launch automatically at login). It uses LSSharedFileList which means your Users will be able to check/uncheck your App in System Preferences > Accounts > Login Items. ## IMPLEMENTATION (via Code): ### Will app launch at login? //don't forget to add #import "LaunchAtLoginController.h" to your implementation file. LaunchAtLoginController *launchController = [[LaunchAtLoginController alloc] init]; BOOL launch = [launchController launchAtLogin]; ### Set launch at login state. LaunchAtLoginController *launchController = [[LaunchAtLoginController alloc] init]; [launchController setLaunchAtLogin:YES]; ## IMPLEMENTATION (via the Interface Builder): * Open Interface Builder * Place a NSObject (the blue box) into the nib window * From the Inspector - Identity Tab set the Class to LaunchAtLoginController * Place a Checkbox on your Window/View * From the Inspector - Bindings Tab unroll the > Value item * Bind to Launch at Login Controller * Model Key Path: launchAtLogin ## IS IT WORKING: After implementing either through code or through IB, setLaunchAtLogin:YES and then check System Preferences > Accounts > Login Items. You should see your app in the list of apps that will start when the user logs in. ## CAVEATS (HelperApp Bundles): If you're trying to set a different bundle (perhaps a HelperApp as a resource to your main bundle) you will simply want to change - (NSURL *)appURL to return the path to this other bundle. ## REQUIREMENTS: None Last tested on 16th May 2013, XCode 4.6.1, Mac OSX 10.8.3 ## ORIGINAL CODE: The original version is by Mozketo (at https://github.com/Mozketo/LaunchAtLoginController). The credits for all of the code goes to him, and whoever is mentioned in his repository. The only thing I've done is modified it to work with ARC (Automatic Reference Counting) enabled. ## LICENSE: (The MIT License) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/.gitignore ================================================ .DS_Store *.swp *~.nib build/ .xcodeproj/ !*.xcodeproj/project.pbxproj *.mode1v3 *.mode2v3 xcuserdata ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/DIYAppDelegate.h ================================================ // // DIYAppDelegate.h // NSStringEmojize // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import @interface DIYAppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/DIYAppDelegate.m ================================================ // // DIYAppDelegate.m // NSStringEmojize // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import "DIYAppDelegate.h" #import "DIYViewController.h" @implementation DIYAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; DIYViewController *vc = [[DIYViewController alloc] init]; self.window.rootViewController = vc; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/DIYViewController.h ================================================ // // DIYViewController.h // NSStringEmojize // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import @interface DIYViewController : UIViewController @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/DIYViewController.m ================================================ // // DIYViewController.m // NSStringEmojize // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import "DIYViewController.h" #import "NSString+Emojize.h" @implementation DIYViewController - (void)viewDidLoad { [super viewDidLoad]; NSString *emojiString = @":+1: :-1: :100: :1234: :8ball: :a: :ab: :abc: :abcd: :accept: :aerial_tramway: :airplane: :alarm_clock: :alien: :ambulance: :anchor: :angel: :anger: :angry: :anguished: :ant: :apple: :aquarius: :aries: :arrow_backward: :arrow_double_down: :arrow_double_up: :arrow_down: :arrow_down_small: :arrow_forward: :arrow_heading_down: :arrow_heading_up: :arrow_left: :arrow_lower_left: :arrow_lower_right: :arrow_right: :arrow_right_hook: :arrow_up: :arrow_up_down: :arrow_up_small: :arrow_upper_left: :arrow_upper_right: :arrows_clockwise: :arrows_counterclockwise: :art: :articulated_lorry: :astonished: :athletic_shoe: :atm: :b: :baby: :baby_bottle: :baby_chick: :baby_symbol: :back: :baggage_claim: :balloon: :ballot_box_with_check: :bamboo: :banana: :bangbang: :bank: :bar_chart: :barber: :baseball: :basketball: :bath: :bathtub: :battery: :bear: :bee: :beer: :beers: :beetle: :beginner: :bell: :bento: :bicyclist: :bike: :bikini: :bird: :birthday: :black_circle: :black_joker: :black_large_square: :black_medium_small_square: :black_medium_square: :black_nib: :black_small_square: :black_square_button: :blossom: :blowfish: :blue_book: :blue_car: :blue_heart: :blush: :boar: :boat: :bomb: :book: :bookmark: :bookmark_tabs: :books: :boom: :boot: :bouquet: :bow: :bowling: :boy: :bread: :bride_with_veil: :bridge_at_night: :briefcase: :broken_heart: :bug: :bulb: :bullettrain_front: :bullettrain_side: :bus: :busstop: :bust_in_silhouette: :busts_in_silhouette: :cactus: :cake: :calendar: :calling: :camel: :camera: :cancer: :candy: :capital_abcd: :capricorn: :car: :card_index: :carousel_horse: :cat: :cat2: :cd: :chart: :chart_with_downwards_trend: :chart_with_upwards_trend: :checkered_flag: :cherries: :cherry_blossom: :chestnut: :chicken: :children_crossing: :chocolate_bar: :christmas_tree: :church: :cinema: :circus_tent: :city_sunrise: :city_sunset: :cl: :clap: :clapper: :clipboard: :clock1: :clock10: :clock1030: :clock11: :clock1130: :clock12: :clock1230: :clock130: :clock2: :clock230: :clock3: :clock330: :clock4: :clock430: :clock5: :clock530: :clock6: :clock630: :clock7: :clock730: :clock8: :clock830: :clock9: :clock930: :closed_book: :closed_lock_with_key: :closed_umbrella: :cloud: :clubs: :cocktail: :coffee: :cold_sweat: :collision: :computer: :confetti_ball: :confounded: :confused: :congratulations: :construction: :construction_worker: :convenience_store: :cookie: :cool: :cop: :copyright: :corn: :couple: :couple_with_heart: :couplekiss: :cow: :cow2: :credit_card: :crescent_moon: :crocodile: :crossed_flags: :crown: :cry: :crying_cat_face: :crystal_ball: :cupid: :curly_loop: :currency_exchange: :curry: :custard: :customs: :cyclone: :dancer: :dancers: :dango: :dart: :dash: :date: :deciduous_tree: :department_store: :diamond_shape_with_a_dot_inside: :diamonds: :disappointed: :disappointed_relieved: :dizzy: :dizzy_face: :do_not_litter: :dog: :dog2: :dollar: :dolls: :dolphin: :door: :doughnut: :dragon: :dragon_face: :dress: :dromedary_camel: :droplet: :dvd: :e-mail: :ear: :ear_of_rice: :earth_africa: :earth_americas: :earth_asia: :egg: :eggplant: :eight_pointed_black_star: :eight_spoked_asterisk: :electric_plug: :elephant: :email: :end: :envelope: :envelope_with_arrow: :euro: :european_castle: :european_post_office: :evergreen_tree: :exclamation: :expressionless: :eyeglasses: :eyes: :facepunch: :factory: :fallen_leaf: :family: :fast_forward: :fax: :fearful: :feet: :ferris_wheel: :file_folder: :fire: :fire_engine: :fireworks: :first_quarter_moon: :first_quarter_moon_with_face: :fish: :fish_cake: :fishing_pole_and_fish: :fist: :flags: :flashlight: :flipper: :floppy_disk: :flower_playing_cards: :flushed: :foggy: :football: :footprints: :fork_and_knife: :fountain: :four_leaf_clover: :free: :fried_shrimp: :fries: :frog: :frowning: :fuelpump: :full_moon: :full_moon_with_face: :game_die: :gem: :gemini: :ghost: :gift: :gift_heart: :girl: :globe_with_meridians: :goat: :golf: :grapes: :green_apple: :green_book: :green_heart: :grey_exclamation: :grey_question: :grimacing: :grin: :grinning: :guardsman: :guitar: :gun: :haircut: :hamburger: :hammer: :hamster: :hand: :handbag: :hankey: :hatched_chick: :hatching_chick: :headphones: :hear_no_evil: :heart: :heart_decoration: :heart_eyes: :heart_eyes_cat: :heartbeat: :heartpulse: :hearts: :heavy_check_mark: :heavy_division_sign: :heavy_dollar_sign: :heavy_exclamation_mark: :heavy_minus_sign: :heavy_multiplication_x: :heavy_plus_sign: :helicopter: :herb: :hibiscus: :high_brightness: :high_heel: :hocho: :honey_pot: :honeybee: :horse: :horse_racing: :hospital: :hotel: :hotsprings: :hourglass: :hourglass_flowing_sand: :house: :house_with_garden: :hushed: :ice_cream: :icecream: :id: :ideograph_advantage: :imp: :inbox_tray: :incoming_envelope: :information_desk_person: :information_source: :innocent: :interrobang: :iphone: :izakaya_lantern: :jack_o_lantern: :japan: :japanese_castle: :japanese_goblin: :japanese_ogre: :jeans: :joy: :joy_cat: :key: :keycap_ten: :kimono: :kiss: :kissing: :kissing_cat: :kissing_closed_eyes: :kissing_heart: :kissing_smiling_eyes: :koala: :koko: :lantern: :large_blue_circle: :large_blue_diamond: :large_orange_diamond: :last_quarter_moon: :last_quarter_moon_with_face: :laughing: :leaves: :ledger: :left_luggage: :left_right_arrow: :leftwards_arrow_with_hook: :lemon: :leo: :leopard: :libra: :light_rail: :link: :lips: :lipstick: :lock: :lock_with_ink_pen: :lollipop: :loop: :loudspeaker: :love_hotel: :love_letter: :low_brightness: :m: :mag: :mag_right: :mahjong: :mailbox: :mailbox_closed: :mailbox_with_mail: :mailbox_with_no_mail: :man: :man_with_gua_pi_mao: :man_with_turban: :mans_shoe: :maple_leaf: :mask: :massage: :meat_on_bone: :mega: :melon: :memo: :mens: :metro: :microphone: :microscope: :milky_way: :minibus: :minidisc: :mobile_phone_off: :money_with_wings: :moneybag: :monkey: :monkey_face: :monorail: :moon: :mortar_board: :mount_fuji: :mountain_bicyclist: :mountain_cableway: :mountain_railway: :mouse: :mouse2: :movie_camera: :moyai: :muscle: :mushroom: :musical_keyboard: :musical_note: :musical_score: :mute: :nail_care: :name_badge: :necktie: :negative_squared_cross_mark: :neutral_face: :new: :new_moon: :new_moon_with_face: :newspaper: :ng: :no_bell: :no_bicycles: :no_entry: :no_entry_sign: :no_good: :no_mobile_phones: :no_mouth: :no_pedestrians: :no_smoking: :non-potable_water: :nose: :notebook: :notebook_with_decorative_cover: :notes: :nut_and_bolt: :o: :o2: :ocean: :octopus: :oden: :office: :ok: :ok_hand: :ok_woman: :older_man: :older_woman: :on: :oncoming_automobile: :oncoming_bus: :oncoming_police_car: :oncoming_taxi: :open_book: :open_file_folder: :open_hands: :open_mouth: :ophiuchus: :orange_book: :outbox_tray: :ox: :package: :page_facing_up: :page_with_curl: :pager: :palm_tree: :panda_face: :paperclip: :parking: :part_alternation_mark: :partly_sunny: :passport_control: :paw_prints: :peach: :pear: :pencil: :pencil2: :penguin: :pensive: :performing_arts: :persevere: :person_frowning: :person_with_blond_hair: :person_with_pouting_face: :phone: :pig: :pig2: :pig_nose: :pill: :pineapple: :pisces: :pizza: :point_down: :point_left: :point_right: :point_up: :point_up_2: :police_car: :poodle: :poop: :post_office: :postal_horn: :postbox: :potable_water: :pouch: :poultry_leg: :pound: :pouting_cat: :pray: :princess: :punch: :purple_heart: :purse: :pushpin: :put_litter_in_its_place: :question: :rabbit: :rabbit2: :racehorse: :radio: :radio_button: :rage: :railway_car: :rainbow: :raised_hand: :raised_hands: :raising_hand: :ram: :ramen: :rat: :recycle: :red_car: :red_circle: :registered: :relaxed: :relieved: :repeat: :repeat_one: :restroom: :revolving_hearts: :rewind: :ribbon: :rice: :rice_ball: :rice_cracker: :rice_scene: :ring: :rocket: :roller_coaster: :rooster: :rose: :rotating_light: :round_pushpin: :rowboat: :rugby_football: :runner: :running: :running_shirt_with_sash: :sa: :sagittarius: :sailboat: :sake: :sandal: :santa: :satellite: :satisfied: :saxophone: :school: :school_satchel: :scissors: :scorpius: :scream: :scream_cat: :scroll: :seat: :secret: :see_no_evil: :seedling: :shaved_ice: :sheep: :shell: :ship: :shirt: :shit: :shoe: :shower: :signal_strength: :six_pointed_star: :ski: :skull: :sleeping: :sleepy: :slot_machine: :small_blue_diamond: :small_orange_diamond: :small_red_triangle: :small_red_triangle_down: :smile: :smile_cat: :smiley: :smiley_cat: :smiling_imp: :smirk: :smirk_cat: :smoking: :snail: :snake: :snowboarder: :snowflake: :snowman: :sob: :soccer: :soon: :sos: :sound: :space_invader: :spades: :spaghetti: :sparkle: :sparkler: :sparkles: :sparkling_heart: :speak_no_evil: :speaker: :speech_balloon: :speedboat: :star: :star2: :stars: :station: :statue_of_liberty: :steam_locomotive: :stew: :straight_ruler: :strawberry: :stuck_out_tongue: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_winking_eye: :sun_with_face: :sunflower: :sunglasses: :sunny: :sunrise: :sunrise_over_mountains: :surfer: :sushi: :suspension_railway: :sweat: :sweat_drops: :sweat_smile: :sweet_potato: :swimmer: :symbols: :syringe: :tada: :tanabata_tree: :tangerine: :taurus: :taxi: :tea: :telephone: :telephone_receiver: :telescope: :tennis: :tent: :thought_balloon: :thumbsdown: :thumbsup: :ticket: :tiger: :tiger2: :tired_face: :tm: :toilet: :tokyo_tower: :tomato: :tongue: :top: :tophat: :tractor: :traffic_light: :train: :train2: :tram: :triangular_flag_on_post: :triangular_ruler: :trident: :triumph: :trolleybus: :trophy: :tropical_drink: :tropical_fish: :truck: :trumpet: :tshirt: :tulip: :turtle: :tv: :twisted_rightwards_arrows: :two_hearts: :two_men_holding_hands: :two_women_holding_hands: :u5272: :u5408: :u55b6: :u6307: :u6708: :u6709: :u6e80: :u7121: :u7533: :u7981: :u7a7a: :umbrella: :unamused: :underage: :unlock: :up: :v: :vertical_traffic_light: :vhs: :vibration_mode: :video_camera: :video_game: :violin: :virgo: :volcano: :vs: :walking: :waning_crescent_moon: :waning_gibbous_moon: :warning: :watch: :water_buffalo: :watermelon: :wave: :wavy_dash: :waxing_crescent_moon: :waxing_gibbous_moon: :wc: :weary: :wedding: :whale: :whale2: :wheelchair: :white_check_mark: :white_circle: :white_flower: :white_large_square: :white_medium_small_square: :white_medium_square: :white_small_square: :white_square_button: :wind_chime: :wine_glass: :wink: :wolf: :woman: :womans_clothes: :womans_hat: :womens: :worried: :wrench: :x: :yellow_heart: :yen: :yum: :zap: :zzz:"; UITextView *emojiView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)]; emojiView.text = [emojiString emojizedString]; emojiView.font = [UIFont systemFontOfSize:36.0f]; emojiView.editable = NO; [self.view addSubview:emojiView]; } @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/NSStringEmojize-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier DIY.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/NSStringEmojize-Prefix.pch ================================================ // // Prefix header for all source files of the 'NSStringEmojize' target in the 'NSStringEmojize' project // #import #ifndef __IPHONE_3_0 #warning "This project uses features only available in iOS SDK 3.0 and later." #endif #ifdef __OBJC__ #import #import #endif ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize/main.m ================================================ // // main.m // NSStringEmojize // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import #import "DIYAppDelegate.h" int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([DIYAppDelegate class])); } } ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ DEC769E916B9AE7600764C30 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC769E816B9AE7600764C30 /* UIKit.framework */; }; DEC769EB16B9AE7600764C30 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC769EA16B9AE7600764C30 /* Foundation.framework */; }; DEC769ED16B9AE7600764C30 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC769EC16B9AE7600764C30 /* CoreGraphics.framework */; }; DEC769F316B9AE7600764C30 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DEC769F116B9AE7600764C30 /* InfoPlist.strings */; }; DEC769F516B9AE7600764C30 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC769F416B9AE7600764C30 /* main.m */; }; DEC769F916B9AE7600764C30 /* DIYAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC769F816B9AE7600764C30 /* DIYAppDelegate.m */; }; DEC769FB16B9AE7600764C30 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = DEC769FA16B9AE7600764C30 /* Default.png */; }; DEC769FD16B9AE7600764C30 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DEC769FC16B9AE7600764C30 /* Default@2x.png */; }; DEC769FF16B9AE7600764C30 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DEC769FE16B9AE7600764C30 /* Default-568h@2x.png */; }; DEC76A0716B9AE7600764C30 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC76A0616B9AE7600764C30 /* SenTestingKit.framework */; }; DEC76A0816B9AE7600764C30 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC769E816B9AE7600764C30 /* UIKit.framework */; }; DEC76A0916B9AE7600764C30 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC769EA16B9AE7600764C30 /* Foundation.framework */; }; DEC76A1116B9AE7600764C30 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DEC76A0F16B9AE7600764C30 /* InfoPlist.strings */; }; DEC76A1416B9AE7600764C30 /* NSStringEmojizeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC76A1316B9AE7600764C30 /* NSStringEmojizeTests.m */; }; DEC76A2016B9AEEE00764C30 /* NSString+Emojize.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC76A1F16B9AEEE00764C30 /* NSString+Emojize.m */; }; DEC76A2316B9B13500764C30 /* DIYViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC76A2216B9B13500764C30 /* DIYViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ DEC76A0A16B9AE7600764C30 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DEC769DD16B9AE7500764C30 /* Project object */; proxyType = 1; remoteGlobalIDString = DEC769E416B9AE7600764C30; remoteInfo = NSStringEmojize; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ C3627488187B8D4E002E44F8 /* emojis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emojis.h; sourceTree = ""; }; DEC769E516B9AE7600764C30 /* NSStringEmojize.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NSStringEmojize.app; sourceTree = BUILT_PRODUCTS_DIR; }; DEC769E816B9AE7600764C30 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; DEC769EA16B9AE7600764C30 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; DEC769EC16B9AE7600764C30 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DEC769F016B9AE7600764C30 /* NSStringEmojize-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NSStringEmojize-Info.plist"; sourceTree = ""; }; DEC769F216B9AE7600764C30 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DEC769F416B9AE7600764C30 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DEC769F616B9AE7600764C30 /* NSStringEmojize-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSStringEmojize-Prefix.pch"; sourceTree = ""; }; DEC769F716B9AE7600764C30 /* DIYAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DIYAppDelegate.h; sourceTree = ""; }; DEC769F816B9AE7600764C30 /* DIYAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DIYAppDelegate.m; sourceTree = ""; }; DEC769FA16B9AE7600764C30 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; DEC769FC16B9AE7600764C30 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; DEC769FE16B9AE7600764C30 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; DEC76A0516B9AE7600764C30 /* NSStringEmojizeTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NSStringEmojizeTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; DEC76A0616B9AE7600764C30 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; DEC76A0E16B9AE7600764C30 /* NSStringEmojizeTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NSStringEmojizeTests-Info.plist"; sourceTree = ""; }; DEC76A1016B9AE7600764C30 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DEC76A1216B9AE7600764C30 /* NSStringEmojizeTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSStringEmojizeTests.h; sourceTree = ""; }; DEC76A1316B9AE7600764C30 /* NSStringEmojizeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSStringEmojizeTests.m; sourceTree = ""; }; DEC76A1E16B9AEEE00764C30 /* NSString+Emojize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Emojize.h"; sourceTree = ""; }; DEC76A1F16B9AEEE00764C30 /* NSString+Emojize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Emojize.m"; sourceTree = ""; }; DEC76A2116B9B13500764C30 /* DIYViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DIYViewController.h; sourceTree = ""; }; DEC76A2216B9B13500764C30 /* DIYViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DIYViewController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ DEC769E216B9AE7600764C30 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DEC769E916B9AE7600764C30 /* UIKit.framework in Frameworks */, DEC769EB16B9AE7600764C30 /* Foundation.framework in Frameworks */, DEC769ED16B9AE7600764C30 /* CoreGraphics.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; DEC76A0116B9AE7600764C30 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DEC76A0716B9AE7600764C30 /* SenTestingKit.framework in Frameworks */, DEC76A0816B9AE7600764C30 /* UIKit.framework in Frameworks */, DEC76A0916B9AE7600764C30 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ DEC769DC16B9AE7500764C30 = { isa = PBXGroup; children = ( DEC76A1D16B9AEEE00764C30 /* NSStringEmojize */, DEC769EE16B9AE7600764C30 /* Example */, DEC76A0C16B9AE7600764C30 /* NSStringEmojizeTests */, DEC769E716B9AE7600764C30 /* Frameworks */, DEC769E616B9AE7600764C30 /* Products */, ); sourceTree = ""; }; DEC769E616B9AE7600764C30 /* Products */ = { isa = PBXGroup; children = ( DEC769E516B9AE7600764C30 /* NSStringEmojize.app */, DEC76A0516B9AE7600764C30 /* NSStringEmojizeTests.octest */, ); name = Products; sourceTree = ""; }; DEC769E716B9AE7600764C30 /* Frameworks */ = { isa = PBXGroup; children = ( DEC769E816B9AE7600764C30 /* UIKit.framework */, DEC769EA16B9AE7600764C30 /* Foundation.framework */, DEC769EC16B9AE7600764C30 /* CoreGraphics.framework */, DEC76A0616B9AE7600764C30 /* SenTestingKit.framework */, ); name = Frameworks; sourceTree = ""; }; DEC769EE16B9AE7600764C30 /* Example */ = { isa = PBXGroup; children = ( DEC769F716B9AE7600764C30 /* DIYAppDelegate.h */, DEC769F816B9AE7600764C30 /* DIYAppDelegate.m */, DEC76A2116B9B13500764C30 /* DIYViewController.h */, DEC76A2216B9B13500764C30 /* DIYViewController.m */, DEC769EF16B9AE7600764C30 /* Supporting Files */, ); name = Example; path = NSStringEmojize; sourceTree = ""; }; DEC769EF16B9AE7600764C30 /* Supporting Files */ = { isa = PBXGroup; children = ( DEC769F016B9AE7600764C30 /* NSStringEmojize-Info.plist */, DEC769F116B9AE7600764C30 /* InfoPlist.strings */, DEC769F416B9AE7600764C30 /* main.m */, DEC769F616B9AE7600764C30 /* NSStringEmojize-Prefix.pch */, DEC769FA16B9AE7600764C30 /* Default.png */, DEC769FC16B9AE7600764C30 /* Default@2x.png */, DEC769FE16B9AE7600764C30 /* Default-568h@2x.png */, ); name = "Supporting Files"; sourceTree = ""; }; DEC76A0C16B9AE7600764C30 /* NSStringEmojizeTests */ = { isa = PBXGroup; children = ( DEC76A1216B9AE7600764C30 /* NSStringEmojizeTests.h */, DEC76A1316B9AE7600764C30 /* NSStringEmojizeTests.m */, DEC76A0D16B9AE7600764C30 /* Supporting Files */, ); path = NSStringEmojizeTests; sourceTree = ""; }; DEC76A0D16B9AE7600764C30 /* Supporting Files */ = { isa = PBXGroup; children = ( DEC76A0E16B9AE7600764C30 /* NSStringEmojizeTests-Info.plist */, DEC76A0F16B9AE7600764C30 /* InfoPlist.strings */, ); name = "Supporting Files"; sourceTree = ""; }; DEC76A1D16B9AEEE00764C30 /* NSStringEmojize */ = { isa = PBXGroup; children = ( C3627488187B8D4E002E44F8 /* emojis.h */, DEC76A1E16B9AEEE00764C30 /* NSString+Emojize.h */, DEC76A1F16B9AEEE00764C30 /* NSString+Emojize.m */, ); name = NSStringEmojize; path = ../NSStringEmojize; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ DEC769E416B9AE7600764C30 /* NSStringEmojize */ = { isa = PBXNativeTarget; buildConfigurationList = DEC76A1716B9AE7600764C30 /* Build configuration list for PBXNativeTarget "NSStringEmojize" */; buildPhases = ( DEC769E116B9AE7600764C30 /* Sources */, DEC769E216B9AE7600764C30 /* Frameworks */, DEC769E316B9AE7600764C30 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = NSStringEmojize; productName = NSStringEmojize; productReference = DEC769E516B9AE7600764C30 /* NSStringEmojize.app */; productType = "com.apple.product-type.application"; }; DEC76A0416B9AE7600764C30 /* NSStringEmojizeTests */ = { isa = PBXNativeTarget; buildConfigurationList = DEC76A1A16B9AE7600764C30 /* Build configuration list for PBXNativeTarget "NSStringEmojizeTests" */; buildPhases = ( DEC76A0016B9AE7600764C30 /* Sources */, DEC76A0116B9AE7600764C30 /* Frameworks */, DEC76A0216B9AE7600764C30 /* Resources */, DEC76A0316B9AE7600764C30 /* ShellScript */, ); buildRules = ( ); dependencies = ( DEC76A0B16B9AE7600764C30 /* PBXTargetDependency */, ); name = NSStringEmojizeTests; productName = NSStringEmojizeTests; productReference = DEC76A0516B9AE7600764C30 /* NSStringEmojizeTests.octest */; productType = "com.apple.product-type.bundle"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ DEC769DD16B9AE7500764C30 /* Project object */ = { isa = PBXProject; attributes = { CLASSPREFIX = DIY; LastUpgradeCheck = 0460; ORGANIZATIONNAME = DIY; }; buildConfigurationList = DEC769E016B9AE7600764C30 /* Build configuration list for PBXProject "NSStringEmojize" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = DEC769DC16B9AE7500764C30; productRefGroup = DEC769E616B9AE7600764C30 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( DEC769E416B9AE7600764C30 /* NSStringEmojize */, DEC76A0416B9AE7600764C30 /* NSStringEmojizeTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ DEC769E316B9AE7600764C30 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DEC769F316B9AE7600764C30 /* InfoPlist.strings in Resources */, DEC769FB16B9AE7600764C30 /* Default.png in Resources */, DEC769FD16B9AE7600764C30 /* Default@2x.png in Resources */, DEC769FF16B9AE7600764C30 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; DEC76A0216B9AE7600764C30 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DEC76A1116B9AE7600764C30 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ DEC76A0316B9AE7600764C30 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ DEC769E116B9AE7600764C30 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DEC769F516B9AE7600764C30 /* main.m in Sources */, DEC769F916B9AE7600764C30 /* DIYAppDelegate.m in Sources */, DEC76A2016B9AEEE00764C30 /* NSString+Emojize.m in Sources */, DEC76A2316B9B13500764C30 /* DIYViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; DEC76A0016B9AE7600764C30 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DEC76A1416B9AE7600764C30 /* NSStringEmojizeTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ DEC76A0B16B9AE7600764C30 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DEC769E416B9AE7600764C30 /* NSStringEmojize */; targetProxy = DEC76A0A16B9AE7600764C30 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ DEC769F116B9AE7600764C30 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( DEC769F216B9AE7600764C30 /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; DEC76A0F16B9AE7600764C30 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( DEC76A1016B9AE7600764C30 /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ DEC76A1516B9AE7600764C30 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 6.1; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; DEC76A1616B9AE7600764C30 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 6.1; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; DEC76A1816B9AE7600764C30 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "NSStringEmojize/NSStringEmojize-Prefix.pch"; INFOPLIST_FILE = "NSStringEmojize/NSStringEmojize-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Debug; }; DEC76A1916B9AE7600764C30 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "NSStringEmojize/NSStringEmojize-Prefix.pch"; INFOPLIST_FILE = "NSStringEmojize/NSStringEmojize-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; name = Release; }; DEC76A1B16B9AE7600764C30 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/NSStringEmojize.app/NSStringEmojize"; FRAMEWORK_SEARCH_PATHS = ( "\"$(SDKROOT)/Developer/Library/Frameworks\"", "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "NSStringEmojize/NSStringEmojize-Prefix.pch"; INFOPLIST_FILE = "NSStringEmojizeTests/NSStringEmojizeTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = octest; }; name = Debug; }; DEC76A1C16B9AE7600764C30 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/NSStringEmojize.app/NSStringEmojize"; FRAMEWORK_SEARCH_PATHS = ( "\"$(SDKROOT)/Developer/Library/Frameworks\"", "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "NSStringEmojize/NSStringEmojize-Prefix.pch"; INFOPLIST_FILE = "NSStringEmojizeTests/NSStringEmojizeTests-Info.plist"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = octest; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ DEC769E016B9AE7600764C30 /* Build configuration list for PBXProject "NSStringEmojize" */ = { isa = XCConfigurationList; buildConfigurations = ( DEC76A1516B9AE7600764C30 /* Debug */, DEC76A1616B9AE7600764C30 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DEC76A1716B9AE7600764C30 /* Build configuration list for PBXNativeTarget "NSStringEmojize" */ = { isa = XCConfigurationList; buildConfigurations = ( DEC76A1816B9AE7600764C30 /* Debug */, DEC76A1916B9AE7600764C30 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DEC76A1A16B9AE7600764C30 /* Build configuration list for PBXNativeTarget "NSStringEmojizeTests" */ = { isa = XCConfigurationList; buildConfigurations = ( DEC76A1B16B9AE7600764C30 /* Debug */, DEC76A1C16B9AE7600764C30 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = DEC769DD16B9AE7500764C30 /* Project object */; } ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojize.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojizeTests/NSStringEmojizeTests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier DIY.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojizeTests/NSStringEmojizeTests.h ================================================ // // NSStringEmojizeTests.h // NSStringEmojizeTests // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import @interface NSStringEmojizeTests : SenTestCase @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojizeTests/NSStringEmojizeTests.m ================================================ // // NSStringEmojizeTests.m // NSStringEmojizeTests // // Created by Jonathan Beilin on 1/30/13. // Copyright (c) 2013 DIY. All rights reserved. // #import "NSStringEmojizeTests.h" #import "NSString+Emojize.h" @implementation NSStringEmojizeTests - (void)testFound { NSString *emojiString = @"This comment has an emoji :mushroom:"; STAssertTrue([[emojiString emojizedString] rangeOfString:@"\U0001F344"].location != NSNotFound, nil); } - (void)testNotFound { NSString *emojiString = @"This comment has an emoji :qwertyasdf:"; STAssertTrue([[emojiString emojizedString] rangeOfString:@"\U0001F344"].location == NSNotFound, nil); } @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/Example/NSStringEmojizeTests/en.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/LICENSE.md ================================================ Copyright 2012 DIY, Co. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/NSStringEmojize/NSString+Emojize.h ================================================ // // NSString+Emojize.h // Field Recorder // // Created by Jonathan Beilin on 11/5/12. // Copyright (c) 2014 DIY. All rights reserved. // // Inspired by https://github.com/larsschwegmann/Emoticonizer #import @interface NSString (Emojize) - (NSString *)emojizedString; + (NSString *)emojizedStringWithString:(NSString *)text; + (NSDictionary *)emojiAliases; @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/NSStringEmojize/NSString+Emojize.m ================================================ // // NSString+Emojize.m // Field Recorder // // Created by Jonathan Beilin on 11/5/12. // Copyright (c) 2014 DIY. All rights reserved. // #import "NSString+Emojize.h" #import "emojis.h" @implementation NSString (Emojize) - (NSString *)emojizedString { return [NSString emojizedStringWithString:self]; } + (NSString *)emojizedStringWithString:(NSString *)text { static dispatch_once_t onceToken; static NSRegularExpression *regex = nil; dispatch_once(&onceToken, ^{ regex = [[NSRegularExpression alloc] initWithPattern:@"(:[a-z0-9-+_]+:)" options:NSRegularExpressionCaseInsensitive error:NULL]; }); __block NSString *resultText = text; NSRange matchingRange = NSMakeRange(0, [resultText length]); [regex enumerateMatchesInString:resultText options:NSMatchingReportCompletion range:matchingRange usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { if (result && ([result resultType] == NSTextCheckingTypeRegularExpression) && !(flags & NSMatchingInternalError)) { NSRange range = result.range; if (range.location != NSNotFound) { NSString *code = [text substringWithRange:range]; NSString *unicode = self.emojiAliases[code]; if (unicode) { resultText = [resultText stringByReplacingOccurrencesOfString:code withString:unicode]; } } } }]; return resultText; } + (NSDictionary *)emojiAliases { static NSDictionary *_emojiAliases; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _emojiAliases = EMOJI_HASH; }); return _emojiAliases; } @end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/NSStringEmojize/emojis.h ================================================ // // emojis.h // NSStringEmojize // // Created by Andrew Sliwinski on 1/6/14. // Copyright (c) 2014 DIY. All rights reserved. // #define EMOJI_HASH @{ \ @":+1:" : @"\U0001F44D", \ @":-1:" : @"\U0001F44E", \ @":100:" : @"\U0001F4AF", \ @":1234:" : @"\U0001F522", \ @":8ball:" : @"\U0001F3B1", \ @":a:" : @"\U0001F170", \ @":ab:" : @"\U0001F18E", \ @":abc:" : @"\U0001F524", \ @":abcd:" : @"\U0001F521", \ @":accept:" : @"\U0001F251", \ @":aerial_tramway:" : @"\U0001F6A1", \ @":airplane:" : @"\U00002708", \ @":alarm_clock:" : @"\U000023F0", \ @":alien:" : @"\U0001F47D", \ @":ambulance:" : @"\U0001F691", \ @":anchor:" : @"\U00002693", \ @":angel:" : @"\U0001F47C", \ @":anger:" : @"\U0001F4A2", \ @":angry:" : @"\U0001F620", \ @":anguished:" : @"\U0001F627", \ @":ant:" : @"\U0001F41C", \ @":apple:" : @"\U0001F34E", \ @":aquarius:" : @"\U00002652", \ @":aries:" : @"\U00002648", \ @":arrow_backward:" : @"\U000025C0", \ @":arrow_double_down:" : @"\U000023EC", \ @":arrow_double_up:" : @"\U000023EB", \ @":arrow_down:" : @"\U00002B07", \ @":arrow_down_small:" : @"\U0001F53D", \ @":arrow_forward:" : @"\U000025B6", \ @":arrow_heading_down:" : @"\U00002935", \ @":arrow_heading_up:" : @"\U00002934", \ @":arrow_left:" : @"\U00002B05", \ @":arrow_lower_left:" : @"\U00002199", \ @":arrow_lower_right:" : @"\U00002198", \ @":arrow_right:" : @"\U000027A1", \ @":arrow_right_hook:" : @"\U000021AA", \ @":arrow_up:" : @"\U00002B06", \ @":arrow_up_down:" : @"\U00002195", \ @":arrow_up_small:" : @"\U0001F53C", \ @":arrow_upper_left:" : @"\U00002196", \ @":arrow_upper_right:" : @"\U00002197", \ @":arrows_clockwise:" : @"\U0001F503", \ @":arrows_counterclockwise:" : @"\U0001F504", \ @":art:" : @"\U0001F3A8", \ @":articulated_lorry:" : @"\U0001F69B", \ @":astonished:" : @"\U0001F632", \ @":athletic_shoe:" : @"\U0001F45F", \ @":atm:" : @"\U0001F3E7", \ @":b:" : @"\U0001F171", \ @":baby:" : @"\U0001F476", \ @":baby_bottle:" : @"\U0001F37C", \ @":baby_chick:" : @"\U0001F424", \ @":baby_symbol:" : @"\U0001F6BC", \ @":back:" : @"\U0001F519", \ @":baggage_claim:" : @"\U0001F6C4", \ @":balloon:" : @"\U0001F388", \ @":ballot_box_with_check:" : @"\U00002611", \ @":bamboo:" : @"\U0001F38D", \ @":banana:" : @"\U0001F34C", \ @":bangbang:" : @"\U0000203C", \ @":bank:" : @"\U0001F3E6", \ @":bar_chart:" : @"\U0001F4CA", \ @":barber:" : @"\U0001F488", \ @":baseball:" : @"\U000026BE", \ @":basketball:" : @"\U0001F3C0", \ @":bath:" : @"\U0001F6C0", \ @":bathtub:" : @"\U0001F6C1", \ @":battery:" : @"\U0001F50B", \ @":bear:" : @"\U0001F43B", \ @":bee:" : @"\U0001F41D", \ @":beer:" : @"\U0001F37A", \ @":beers:" : @"\U0001F37B", \ @":beetle:" : @"\U0001F41E", \ @":beginner:" : @"\U0001F530", \ @":bell:" : @"\U0001F514", \ @":bento:" : @"\U0001F371", \ @":bicyclist:" : @"\U0001F6B4", \ @":bike:" : @"\U0001F6B2", \ @":bikini:" : @"\U0001F459", \ @":bird:" : @"\U0001F426", \ @":birthday:" : @"\U0001F382", \ @":black_circle:" : @"\U000026AB", \ @":black_joker:" : @"\U0001F0CF", \ @":black_large_square:" : @"\U00002B1B", \ @":black_medium_small_square:" : @"\U000025FE", \ @":black_medium_square:" : @"\U000025FC", \ @":black_nib:" : @"\U00002712", \ @":black_small_square:" : @"\U000025AA", \ @":black_square_button:" : @"\U0001F532", \ @":blossom:" : @"\U0001F33C", \ @":blowfish:" : @"\U0001F421", \ @":blue_book:" : @"\U0001F4D8", \ @":blue_car:" : @"\U0001F699", \ @":blue_heart:" : @"\U0001F499", \ @":blush:" : @"\U0001F60A", \ @":boar:" : @"\U0001F417", \ @":boat:" : @"\U000026F5", \ @":bomb:" : @"\U0001F4A3", \ @":book:" : @"\U0001F4D6", \ @":bookmark:" : @"\U0001F516", \ @":bookmark_tabs:" : @"\U0001F4D1", \ @":books:" : @"\U0001F4DA", \ @":boom:" : @"\U0001F4A5", \ @":boot:" : @"\U0001F462", \ @":bouquet:" : @"\U0001F490", \ @":bow:" : @"\U0001F647", \ @":bowling:" : @"\U0001F3B3", \ @":boy:" : @"\U0001F466", \ @":bread:" : @"\U0001F35E", \ @":bride_with_veil:" : @"\U0001F470", \ @":bridge_at_night:" : @"\U0001F309", \ @":briefcase:" : @"\U0001F4BC", \ @":broken_heart:" : @"\U0001F494", \ @":bug:" : @"\U0001F41B", \ @":bulb:" : @"\U0001F4A1", \ @":bullettrain_front:" : @"\U0001F685", \ @":bullettrain_side:" : @"\U0001F684", \ @":bus:" : @"\U0001F68C", \ @":busstop:" : @"\U0001F68F", \ @":bust_in_silhouette:" : @"\U0001F464", \ @":busts_in_silhouette:" : @"\U0001F465", \ @":cactus:" : @"\U0001F335", \ @":cake:" : @"\U0001F370", \ @":calendar:" : @"\U0001F4C6", \ @":calling:" : @"\U0001F4F2", \ @":camel:" : @"\U0001F42B", \ @":camera:" : @"\U0001F4F7", \ @":cancer:" : @"\U0000264B", \ @":candy:" : @"\U0001F36C", \ @":capital_abcd:" : @"\U0001F520", \ @":capricorn:" : @"\U00002651", \ @":car:" : @"\U0001F697", \ @":card_index:" : @"\U0001F4C7", \ @":carousel_horse:" : @"\U0001F3A0", \ @":cat:" : @"\U0001F431", \ @":cat2:" : @"\U0001F408", \ @":cd:" : @"\U0001F4BF", \ @":chart:" : @"\U0001F4B9", \ @":chart_with_downwards_trend:" : @"\U0001F4C9", \ @":chart_with_upwards_trend:" : @"\U0001F4C8", \ @":checkered_flag:" : @"\U0001F3C1", \ @":cherries:" : @"\U0001F352", \ @":cherry_blossom:" : @"\U0001F338", \ @":chestnut:" : @"\U0001F330", \ @":chicken:" : @"\U0001F414", \ @":children_crossing:" : @"\U0001F6B8", \ @":chocolate_bar:" : @"\U0001F36B", \ @":christmas_tree:" : @"\U0001F384", \ @":church:" : @"\U000026EA", \ @":cinema:" : @"\U0001F3A6", \ @":circus_tent:" : @"\U0001F3AA", \ @":city_sunrise:" : @"\U0001F307", \ @":city_sunset:" : @"\U0001F306", \ @":cl:" : @"\U0001F191", \ @":clap:" : @"\U0001F44F", \ @":clapper:" : @"\U0001F3AC", \ @":clipboard:" : @"\U0001F4CB", \ @":clock1:" : @"\U0001F550", \ @":clock10:" : @"\U0001F559", \ @":clock1030:" : @"\U0001F565", \ @":clock11:" : @"\U0001F55A", \ @":clock1130:" : @"\U0001F566", \ @":clock12:" : @"\U0001F55B", \ @":clock1230:" : @"\U0001F567", \ @":clock130:" : @"\U0001F55C", \ @":clock2:" : @"\U0001F551", \ @":clock230:" : @"\U0001F55D", \ @":clock3:" : @"\U0001F552", \ @":clock330:" : @"\U0001F55E", \ @":clock4:" : @"\U0001F553", \ @":clock430:" : @"\U0001F55F", \ @":clock5:" : @"\U0001F554", \ @":clock530:" : @"\U0001F560", \ @":clock6:" : @"\U0001F555", \ @":clock630:" : @"\U0001F561", \ @":clock7:" : @"\U0001F556", \ @":clock730:" : @"\U0001F562", \ @":clock8:" : @"\U0001F557", \ @":clock830:" : @"\U0001F563", \ @":clock9:" : @"\U0001F558", \ @":clock930:" : @"\U0001F564", \ @":closed_book:" : @"\U0001F4D5", \ @":closed_lock_with_key:" : @"\U0001F510", \ @":closed_umbrella:" : @"\U0001F302", \ @":cloud:" : @"\U00002601", \ @":clubs:" : @"\U00002663", \ @":cocktail:" : @"\U0001F378", \ @":coffee:" : @"\U00002615", \ @":cold_sweat:" : @"\U0001F630", \ @":collision:" : @"\U0001F4A5", \ @":computer:" : @"\U0001F4BB", \ @":confetti_ball:" : @"\U0001F38A", \ @":confounded:" : @"\U0001F616", \ @":confused:" : @"\U0001F615", \ @":congratulations:" : @"\U00003297", \ @":construction:" : @"\U0001F6A7", \ @":construction_worker:" : @"\U0001F477", \ @":convenience_store:" : @"\U0001F3EA", \ @":cookie:" : @"\U0001F36A", \ @":cool:" : @"\U0001F192", \ @":cop:" : @"\U0001F46E", \ @":copyright:" : @"\U000000A9", \ @":corn:" : @"\U0001F33D", \ @":couple:" : @"\U0001F46B", \ @":couple_with_heart:" : @"\U0001F491", \ @":couplekiss:" : @"\U0001F48F", \ @":cow:" : @"\U0001F42E", \ @":cow2:" : @"\U0001F404", \ @":credit_card:" : @"\U0001F4B3", \ @":crescent_moon:" : @"\U0001F319", \ @":crocodile:" : @"\U0001F40A", \ @":crossed_flags:" : @"\U0001F38C", \ @":crown:" : @"\U0001F451", \ @":cry:" : @"\U0001F622", \ @":crying_cat_face:" : @"\U0001F63F", \ @":crystal_ball:" : @"\U0001F52E", \ @":cupid:" : @"\U0001F498", \ @":curly_loop:" : @"\U000027B0", \ @":currency_exchange:" : @"\U0001F4B1", \ @":curry:" : @"\U0001F35B", \ @":custard:" : @"\U0001F36E", \ @":customs:" : @"\U0001F6C3", \ @":cyclone:" : @"\U0001F300", \ @":dancer:" : @"\U0001F483", \ @":dancers:" : @"\U0001F46F", \ @":dango:" : @"\U0001F361", \ @":dart:" : @"\U0001F3AF", \ @":dash:" : @"\U0001F4A8", \ @":date:" : @"\U0001F4C5", \ @":deciduous_tree:" : @"\U0001F333", \ @":department_store:" : @"\U0001F3EC", \ @":diamond_shape_with_a_dot_inside:" : @"\U0001F4A0", \ @":diamonds:" : @"\U00002666", \ @":disappointed:" : @"\U0001F61E", \ @":disappointed_relieved:" : @"\U0001F625", \ @":dizzy:" : @"\U0001F4AB", \ @":dizzy_face:" : @"\U0001F635", \ @":do_not_litter:" : @"\U0001F6AF", \ @":dog:" : @"\U0001F436", \ @":dog2:" : @"\U0001F415", \ @":dollar:" : @"\U0001F4B5", \ @":dolls:" : @"\U0001F38E", \ @":dolphin:" : @"\U0001F42C", \ @":door:" : @"\U0001F6AA", \ @":doughnut:" : @"\U0001F369", \ @":dragon:" : @"\U0001F409", \ @":dragon_face:" : @"\U0001F432", \ @":dress:" : @"\U0001F457", \ @":dromedary_camel:" : @"\U0001F42A", \ @":droplet:" : @"\U0001F4A7", \ @":dvd:" : @"\U0001F4C0", \ @":e-mail:" : @"\U0001F4E7", \ @":ear:" : @"\U0001F442", \ @":ear_of_rice:" : @"\U0001F33E", \ @":earth_africa:" : @"\U0001F30D", \ @":earth_americas:" : @"\U0001F30E", \ @":earth_asia:" : @"\U0001F30F", \ @":egg:" : @"\U0001F373", \ @":eggplant:" : @"\U0001F346", \ @":eight_pointed_black_star:" : @"\U00002734", \ @":eight_spoked_asterisk:" : @"\U00002733", \ @":electric_plug:" : @"\U0001F50C", \ @":elephant:" : @"\U0001F418", \ @":email:" : @"\U00002709", \ @":end:" : @"\U0001F51A", \ @":envelope:" : @"\U00002709", \ @":envelope_with_arrow:" : @"\U0001F4E9", \ @":euro:" : @"\U0001F4B6", \ @":european_castle:" : @"\U0001F3F0", \ @":european_post_office:" : @"\U0001F3E4", \ @":evergreen_tree:" : @"\U0001F332", \ @":exclamation:" : @"\U00002757", \ @":expressionless:" : @"\U0001F611", \ @":eyeglasses:" : @"\U0001F453", \ @":eyes:" : @"\U0001F440", \ @":facepunch:" : @"\U0001F44A", \ @":factory:" : @"\U0001F3ED", \ @":fallen_leaf:" : @"\U0001F342", \ @":family:" : @"\U0001F46A", \ @":fast_forward:" : @"\U000023E9", \ @":fax:" : @"\U0001F4E0", \ @":fearful:" : @"\U0001F628", \ @":feet:" : @"\U0001F43E", \ @":ferris_wheel:" : @"\U0001F3A1", \ @":file_folder:" : @"\U0001F4C1", \ @":fire:" : @"\U0001F525", \ @":fire_engine:" : @"\U0001F692", \ @":fireworks:" : @"\U0001F386", \ @":first_quarter_moon:" : @"\U0001F313", \ @":first_quarter_moon_with_face:" : @"\U0001F31B", \ @":fish:" : @"\U0001F41F", \ @":fish_cake:" : @"\U0001F365", \ @":fishing_pole_and_fish:" : @"\U0001F3A3", \ @":fist:" : @"\U0000270A", \ @":flags:" : @"\U0001F38F", \ @":flashlight:" : @"\U0001F526", \ @":flipper:" : @"\U0001F42C", \ @":floppy_disk:" : @"\U0001F4BE", \ @":flower_playing_cards:" : @"\U0001F3B4", \ @":flushed:" : @"\U0001F633", \ @":foggy:" : @"\U0001F301", \ @":football:" : @"\U0001F3C8", \ @":footprints:" : @"\U0001F463", \ @":fork_and_knife:" : @"\U0001F374", \ @":fountain:" : @"\U000026F2", \ @":four_leaf_clover:" : @"\U0001F340", \ @":free:" : @"\U0001F193", \ @":fried_shrimp:" : @"\U0001F364", \ @":fries:" : @"\U0001F35F", \ @":frog:" : @"\U0001F438", \ @":frowning:" : @"\U0001F626", \ @":fuelpump:" : @"\U000026FD", \ @":full_moon:" : @"\U0001F315", \ @":full_moon_with_face:" : @"\U0001F31D", \ @":game_die:" : @"\U0001F3B2", \ @":gem:" : @"\U0001F48E", \ @":gemini:" : @"\U0000264A", \ @":ghost:" : @"\U0001F47B", \ @":gift:" : @"\U0001F381", \ @":gift_heart:" : @"\U0001F49D", \ @":girl:" : @"\U0001F467", \ @":globe_with_meridians:" : @"\U0001F310", \ @":goat:" : @"\U0001F410", \ @":golf:" : @"\U000026F3", \ @":grapes:" : @"\U0001F347", \ @":green_apple:" : @"\U0001F34F", \ @":green_book:" : @"\U0001F4D7", \ @":green_heart:" : @"\U0001F49A", \ @":grey_exclamation:" : @"\U00002755", \ @":grey_question:" : @"\U00002754", \ @":grimacing:" : @"\U0001F62C", \ @":grin:" : @"\U0001F601", \ @":grinning:" : @"\U0001F600", \ @":guardsman:" : @"\U0001F482", \ @":guitar:" : @"\U0001F3B8", \ @":gun:" : @"\U0001F52B", \ @":haircut:" : @"\U0001F487", \ @":hamburger:" : @"\U0001F354", \ @":hammer:" : @"\U0001F528", \ @":hamster:" : @"\U0001F439", \ @":hand:" : @"\U0000270B", \ @":handbag:" : @"\U0001F45C", \ @":hankey:" : @"\U0001F4A9", \ @":hatched_chick:" : @"\U0001F425", \ @":hatching_chick:" : @"\U0001F423", \ @":headphones:" : @"\U0001F3A7", \ @":hear_no_evil:" : @"\U0001F649", \ @":heart:" : @"\U00002764", \ @":heart_decoration:" : @"\U0001F49F", \ @":heart_eyes:" : @"\U0001F60D", \ @":heart_eyes_cat:" : @"\U0001F63B", \ @":heartbeat:" : @"\U0001F493", \ @":heartpulse:" : @"\U0001F497", \ @":hearts:" : @"\U00002665", \ @":heavy_check_mark:" : @"\U00002714", \ @":heavy_division_sign:" : @"\U00002797", \ @":heavy_dollar_sign:" : @"\U0001F4B2", \ @":heavy_exclamation_mark:" : @"\U00002757", \ @":heavy_minus_sign:" : @"\U00002796", \ @":heavy_multiplication_x:" : @"\U00002716", \ @":heavy_plus_sign:" : @"\U00002795", \ @":helicopter:" : @"\U0001F681", \ @":herb:" : @"\U0001F33F", \ @":hibiscus:" : @"\U0001F33A", \ @":high_brightness:" : @"\U0001F506", \ @":high_heel:" : @"\U0001F460", \ @":hocho:" : @"\U0001F52A", \ @":honey_pot:" : @"\U0001F36F", \ @":honeybee:" : @"\U0001F41D", \ @":horse:" : @"\U0001F434", \ @":horse_racing:" : @"\U0001F3C7", \ @":hospital:" : @"\U0001F3E5", \ @":hotel:" : @"\U0001F3E8", \ @":hotsprings:" : @"\U00002668", \ @":hourglass:" : @"\U0000231B", \ @":hourglass_flowing_sand:" : @"\U000023F3", \ @":house:" : @"\U0001F3E0", \ @":house_with_garden:" : @"\U0001F3E1", \ @":hushed:" : @"\U0001F62F", \ @":ice_cream:" : @"\U0001F368", \ @":icecream:" : @"\U0001F366", \ @":id:" : @"\U0001F194", \ @":ideograph_advantage:" : @"\U0001F250", \ @":imp:" : @"\U0001F47F", \ @":inbox_tray:" : @"\U0001F4E5", \ @":incoming_envelope:" : @"\U0001F4E8", \ @":information_desk_person:" : @"\U0001F481", \ @":information_source:" : @"\U00002139", \ @":innocent:" : @"\U0001F607", \ @":interrobang:" : @"\U00002049", \ @":iphone:" : @"\U0001F4F1", \ @":izakaya_lantern:" : @"\U0001F3EE", \ @":jack_o_lantern:" : @"\U0001F383", \ @":japan:" : @"\U0001F5FE", \ @":japanese_castle:" : @"\U0001F3EF", \ @":japanese_goblin:" : @"\U0001F47A", \ @":japanese_ogre:" : @"\U0001F479", \ @":jeans:" : @"\U0001F456", \ @":joy:" : @"\U0001F602", \ @":joy_cat:" : @"\U0001F639", \ @":key:" : @"\U0001F511", \ @":keycap_ten:" : @"\U0001F51F", \ @":kimono:" : @"\U0001F458", \ @":kiss:" : @"\U0001F48B", \ @":kissing:" : @"\U0001F617", \ @":kissing_cat:" : @"\U0001F63D", \ @":kissing_closed_eyes:" : @"\U0001F61A", \ @":kissing_heart:" : @"\U0001F618", \ @":kissing_smiling_eyes:" : @"\U0001F619", \ @":koala:" : @"\U0001F428", \ @":koko:" : @"\U0001F201", \ @":lantern:" : @"\U0001F3EE", \ @":large_blue_circle:" : @"\U0001F535", \ @":large_blue_diamond:" : @"\U0001F537", \ @":large_orange_diamond:" : @"\U0001F536", \ @":last_quarter_moon:" : @"\U0001F317", \ @":last_quarter_moon_with_face:" : @"\U0001F31C", \ @":laughing:" : @"\U0001F606", \ @":leaves:" : @"\U0001F343", \ @":ledger:" : @"\U0001F4D2", \ @":left_luggage:" : @"\U0001F6C5", \ @":left_right_arrow:" : @"\U00002194", \ @":leftwards_arrow_with_hook:" : @"\U000021A9", \ @":lemon:" : @"\U0001F34B", \ @":leo:" : @"\U0000264C", \ @":leopard:" : @"\U0001F406", \ @":libra:" : @"\U0000264E", \ @":light_rail:" : @"\U0001F688", \ @":link:" : @"\U0001F517", \ @":lips:" : @"\U0001F444", \ @":lipstick:" : @"\U0001F484", \ @":lock:" : @"\U0001F512", \ @":lock_with_ink_pen:" : @"\U0001F50F", \ @":lollipop:" : @"\U0001F36D", \ @":loop:" : @"\U000027BF", \ @":loudspeaker:" : @"\U0001F4E2", \ @":love_hotel:" : @"\U0001F3E9", \ @":love_letter:" : @"\U0001F48C", \ @":low_brightness:" : @"\U0001F505", \ @":m:" : @"\U000024C2", \ @":mag:" : @"\U0001F50D", \ @":mag_right:" : @"\U0001F50E", \ @":mahjong:" : @"\U0001F004", \ @":mailbox:" : @"\U0001F4EB", \ @":mailbox_closed:" : @"\U0001F4EA", \ @":mailbox_with_mail:" : @"\U0001F4EC", \ @":mailbox_with_no_mail:" : @"\U0001F4ED", \ @":man:" : @"\U0001F468", \ @":man_with_gua_pi_mao:" : @"\U0001F472", \ @":man_with_turban:" : @"\U0001F473", \ @":mans_shoe:" : @"\U0001F45E", \ @":maple_leaf:" : @"\U0001F341", \ @":mask:" : @"\U0001F637", \ @":massage:" : @"\U0001F486", \ @":meat_on_bone:" : @"\U0001F356", \ @":mega:" : @"\U0001F4E3", \ @":melon:" : @"\U0001F348", \ @":memo:" : @"\U0001F4DD", \ @":mens:" : @"\U0001F6B9", \ @":metro:" : @"\U0001F687", \ @":microphone:" : @"\U0001F3A4", \ @":microscope:" : @"\U0001F52C", \ @":milky_way:" : @"\U0001F30C", \ @":minibus:" : @"\U0001F690", \ @":minidisc:" : @"\U0001F4BD", \ @":mobile_phone_off:" : @"\U0001F4F4", \ @":money_with_wings:" : @"\U0001F4B8", \ @":moneybag:" : @"\U0001F4B0", \ @":monkey:" : @"\U0001F412", \ @":monkey_face:" : @"\U0001F435", \ @":monorail:" : @"\U0001F69D", \ @":moon:" : @"\U0001F314", \ @":mortar_board:" : @"\U0001F393", \ @":mount_fuji:" : @"\U0001F5FB", \ @":mountain_bicyclist:" : @"\U0001F6B5", \ @":mountain_cableway:" : @"\U0001F6A0", \ @":mountain_railway:" : @"\U0001F69E", \ @":mouse:" : @"\U0001F42D", \ @":mouse2:" : @"\U0001F401", \ @":movie_camera:" : @"\U0001F3A5", \ @":moyai:" : @"\U0001F5FF", \ @":muscle:" : @"\U0001F4AA", \ @":mushroom:" : @"\U0001F344", \ @":musical_keyboard:" : @"\U0001F3B9", \ @":musical_note:" : @"\U0001F3B5", \ @":musical_score:" : @"\U0001F3BC", \ @":mute:" : @"\U0001F507", \ @":nail_care:" : @"\U0001F485", \ @":name_badge:" : @"\U0001F4DB", \ @":necktie:" : @"\U0001F454", \ @":negative_squared_cross_mark:" : @"\U0000274E", \ @":neutral_face:" : @"\U0001F610", \ @":new:" : @"\U0001F195", \ @":new_moon:" : @"\U0001F311", \ @":new_moon_with_face:" : @"\U0001F31A", \ @":newspaper:" : @"\U0001F4F0", \ @":ng:" : @"\U0001F196", \ @":no_bell:" : @"\U0001F515", \ @":no_bicycles:" : @"\U0001F6B3", \ @":no_entry:" : @"\U000026D4", \ @":no_entry_sign:" : @"\U0001F6AB", \ @":no_good:" : @"\U0001F645", \ @":no_mobile_phones:" : @"\U0001F4F5", \ @":no_mouth:" : @"\U0001F636", \ @":no_pedestrians:" : @"\U0001F6B7", \ @":no_smoking:" : @"\U0001F6AD", \ @":non-potable_water:" : @"\U0001F6B1", \ @":nose:" : @"\U0001F443", \ @":notebook:" : @"\U0001F4D3", \ @":notebook_with_decorative_cover:" : @"\U0001F4D4", \ @":notes:" : @"\U0001F3B6", \ @":nut_and_bolt:" : @"\U0001F529", \ @":o:" : @"\U00002B55", \ @":o2:" : @"\U0001F17E", \ @":ocean:" : @"\U0001F30A", \ @":octopus:" : @"\U0001F419", \ @":oden:" : @"\U0001F362", \ @":office:" : @"\U0001F3E2", \ @":ok:" : @"\U0001F197", \ @":ok_hand:" : @"\U0001F44C", \ @":ok_woman:" : @"\U0001F646", \ @":older_man:" : @"\U0001F474", \ @":older_woman:" : @"\U0001F475", \ @":on:" : @"\U0001F51B", \ @":oncoming_automobile:" : @"\U0001F698", \ @":oncoming_bus:" : @"\U0001F68D", \ @":oncoming_police_car:" : @"\U0001F694", \ @":oncoming_taxi:" : @"\U0001F696", \ @":open_book:" : @"\U0001F4D6", \ @":open_file_folder:" : @"\U0001F4C2", \ @":open_hands:" : @"\U0001F450", \ @":open_mouth:" : @"\U0001F62E", \ @":ophiuchus:" : @"\U000026CE", \ @":orange_book:" : @"\U0001F4D9", \ @":outbox_tray:" : @"\U0001F4E4", \ @":ox:" : @"\U0001F402", \ @":package:" : @"\U0001F4E6", \ @":page_facing_up:" : @"\U0001F4C4", \ @":page_with_curl:" : @"\U0001F4C3", \ @":pager:" : @"\U0001F4DF", \ @":palm_tree:" : @"\U0001F334", \ @":panda_face:" : @"\U0001F43C", \ @":paperclip:" : @"\U0001F4CE", \ @":parking:" : @"\U0001F17F", \ @":part_alternation_mark:" : @"\U0000303D", \ @":partly_sunny:" : @"\U000026C5", \ @":passport_control:" : @"\U0001F6C2", \ @":paw_prints:" : @"\U0001F43E", \ @":peach:" : @"\U0001F351", \ @":pear:" : @"\U0001F350", \ @":pencil:" : @"\U0001F4DD", \ @":pencil2:" : @"\U0000270F", \ @":penguin:" : @"\U0001F427", \ @":pensive:" : @"\U0001F614", \ @":performing_arts:" : @"\U0001F3AD", \ @":persevere:" : @"\U0001F623", \ @":person_frowning:" : @"\U0001F64D", \ @":person_with_blond_hair:" : @"\U0001F471", \ @":person_with_pouting_face:" : @"\U0001F64E", \ @":phone:" : @"\U0000260E", \ @":pig:" : @"\U0001F437", \ @":pig2:" : @"\U0001F416", \ @":pig_nose:" : @"\U0001F43D", \ @":pill:" : @"\U0001F48A", \ @":pineapple:" : @"\U0001F34D", \ @":pisces:" : @"\U00002653", \ @":pizza:" : @"\U0001F355", \ @":point_down:" : @"\U0001F447", \ @":point_left:" : @"\U0001F448", \ @":point_right:" : @"\U0001F449", \ @":point_up:" : @"\U0000261D", \ @":point_up_2:" : @"\U0001F446", \ @":police_car:" : @"\U0001F693", \ @":poodle:" : @"\U0001F429", \ @":poop:" : @"\U0001F4A9", \ @":post_office:" : @"\U0001F3E3", \ @":postal_horn:" : @"\U0001F4EF", \ @":postbox:" : @"\U0001F4EE", \ @":potable_water:" : @"\U0001F6B0", \ @":pouch:" : @"\U0001F45D", \ @":poultry_leg:" : @"\U0001F357", \ @":pound:" : @"\U0001F4B7", \ @":pouting_cat:" : @"\U0001F63E", \ @":pray:" : @"\U0001F64F", \ @":princess:" : @"\U0001F478", \ @":punch:" : @"\U0001F44A", \ @":purple_heart:" : @"\U0001F49C", \ @":purse:" : @"\U0001F45B", \ @":pushpin:" : @"\U0001F4CC", \ @":put_litter_in_its_place:" : @"\U0001F6AE", \ @":question:" : @"\U00002753", \ @":rabbit:" : @"\U0001F430", \ @":rabbit2:" : @"\U0001F407", \ @":racehorse:" : @"\U0001F40E", \ @":radio:" : @"\U0001F4FB", \ @":radio_button:" : @"\U0001F518", \ @":rage:" : @"\U0001F621", \ @":railway_car:" : @"\U0001F683", \ @":rainbow:" : @"\U0001F308", \ @":raised_hand:" : @"\U0000270B", \ @":raised_hands:" : @"\U0001F64C", \ @":raising_hand:" : @"\U0001F64B", \ @":ram:" : @"\U0001F40F", \ @":ramen:" : @"\U0001F35C", \ @":rat:" : @"\U0001F400", \ @":recycle:" : @"\U0000267B", \ @":red_car:" : @"\U0001F697", \ @":red_circle:" : @"\U0001F534", \ @":registered:" : @"\U000000AE", \ @":relaxed:" : @"\U0000263A", \ @":relieved:" : @"\U0001F60C", \ @":repeat:" : @"\U0001F501", \ @":repeat_one:" : @"\U0001F502", \ @":restroom:" : @"\U0001F6BB", \ @":revolving_hearts:" : @"\U0001F49E", \ @":rewind:" : @"\U000023EA", \ @":ribbon:" : @"\U0001F380", \ @":rice:" : @"\U0001F35A", \ @":rice_ball:" : @"\U0001F359", \ @":rice_cracker:" : @"\U0001F358", \ @":rice_scene:" : @"\U0001F391", \ @":ring:" : @"\U0001F48D", \ @":rocket:" : @"\U0001F680", \ @":roller_coaster:" : @"\U0001F3A2", \ @":rooster:" : @"\U0001F413", \ @":rose:" : @"\U0001F339", \ @":rotating_light:" : @"\U0001F6A8", \ @":round_pushpin:" : @"\U0001F4CD", \ @":rowboat:" : @"\U0001F6A3", \ @":rugby_football:" : @"\U0001F3C9", \ @":runner:" : @"\U0001F3C3", \ @":running:" : @"\U0001F3C3", \ @":running_shirt_with_sash:" : @"\U0001F3BD", \ @":sa:" : @"\U0001F202", \ @":sagittarius:" : @"\U00002650", \ @":sailboat:" : @"\U000026F5", \ @":sake:" : @"\U0001F376", \ @":sandal:" : @"\U0001F461", \ @":santa:" : @"\U0001F385", \ @":satellite:" : @"\U0001F4E1", \ @":satisfied:" : @"\U0001F606", \ @":saxophone:" : @"\U0001F3B7", \ @":school:" : @"\U0001F3EB", \ @":school_satchel:" : @"\U0001F392", \ @":scissors:" : @"\U00002702", \ @":scorpius:" : @"\U0000264F", \ @":scream:" : @"\U0001F631", \ @":scream_cat:" : @"\U0001F640", \ @":scroll:" : @"\U0001F4DC", \ @":seat:" : @"\U0001F4BA", \ @":secret:" : @"\U00003299", \ @":see_no_evil:" : @"\U0001F648", \ @":seedling:" : @"\U0001F331", \ @":shaved_ice:" : @"\U0001F367", \ @":sheep:" : @"\U0001F411", \ @":shell:" : @"\U0001F41A", \ @":ship:" : @"\U0001F6A2", \ @":shirt:" : @"\U0001F455", \ @":shit:" : @"\U0001F4A9", \ @":shoe:" : @"\U0001F45E", \ @":shower:" : @"\U0001F6BF", \ @":signal_strength:" : @"\U0001F4F6", \ @":six_pointed_star:" : @"\U0001F52F", \ @":ski:" : @"\U0001F3BF", \ @":skull:" : @"\U0001F480", \ @":sleeping:" : @"\U0001F634", \ @":sleepy:" : @"\U0001F62A", \ @":slot_machine:" : @"\U0001F3B0", \ @":small_blue_diamond:" : @"\U0001F539", \ @":small_orange_diamond:" : @"\U0001F538", \ @":small_red_triangle:" : @"\U0001F53A", \ @":small_red_triangle_down:" : @"\U0001F53B", \ @":smile:" : @"\U0001F604", \ @":smile_cat:" : @"\U0001F638", \ @":smiley:" : @"\U0001F603", \ @":smiley_cat:" : @"\U0001F63A", \ @":smiling_imp:" : @"\U0001F608", \ @":smirk:" : @"\U0001F60F", \ @":smirk_cat:" : @"\U0001F63C", \ @":smoking:" : @"\U0001F6AC", \ @":snail:" : @"\U0001F40C", \ @":snake:" : @"\U0001F40D", \ @":snowboarder:" : @"\U0001F3C2", \ @":snowflake:" : @"\U00002744", \ @":snowman:" : @"\U000026C4", \ @":sob:" : @"\U0001F62D", \ @":soccer:" : @"\U000026BD", \ @":soon:" : @"\U0001F51C", \ @":sos:" : @"\U0001F198", \ @":sound:" : @"\U0001F509", \ @":space_invader:" : @"\U0001F47E", \ @":spades:" : @"\U00002660", \ @":spaghetti:" : @"\U0001F35D", \ @":sparkle:" : @"\U00002747", \ @":sparkler:" : @"\U0001F387", \ @":sparkles:" : @"\U00002728", \ @":sparkling_heart:" : @"\U0001F496", \ @":speak_no_evil:" : @"\U0001F64A", \ @":speaker:" : @"\U0001F50A", \ @":speech_balloon:" : @"\U0001F4AC", \ @":speedboat:" : @"\U0001F6A4", \ @":star:" : @"\U00002B50", \ @":star2:" : @"\U0001F31F", \ @":stars:" : @"\U0001F303", \ @":station:" : @"\U0001F689", \ @":statue_of_liberty:" : @"\U0001F5FD", \ @":steam_locomotive:" : @"\U0001F682", \ @":stew:" : @"\U0001F372", \ @":straight_ruler:" : @"\U0001F4CF", \ @":strawberry:" : @"\U0001F353", \ @":stuck_out_tongue:" : @"\U0001F61B", \ @":stuck_out_tongue_closed_eyes:" : @"\U0001F61D", \ @":stuck_out_tongue_winking_eye:" : @"\U0001F61C", \ @":sun_with_face:" : @"\U0001F31E", \ @":sunflower:" : @"\U0001F33B", \ @":sunglasses:" : @"\U0001F60E", \ @":sunny:" : @"\U00002600", \ @":sunrise:" : @"\U0001F305", \ @":sunrise_over_mountains:" : @"\U0001F304", \ @":surfer:" : @"\U0001F3C4", \ @":sushi:" : @"\U0001F363", \ @":suspension_railway:" : @"\U0001F69F", \ @":sweat:" : @"\U0001F613", \ @":sweat_drops:" : @"\U0001F4A6", \ @":sweat_smile:" : @"\U0001F605", \ @":sweet_potato:" : @"\U0001F360", \ @":swimmer:" : @"\U0001F3CA", \ @":symbols:" : @"\U0001F523", \ @":syringe:" : @"\U0001F489", \ @":tada:" : @"\U0001F389", \ @":tanabata_tree:" : @"\U0001F38B", \ @":tangerine:" : @"\U0001F34A", \ @":taurus:" : @"\U00002649", \ @":taxi:" : @"\U0001F695", \ @":tea:" : @"\U0001F375", \ @":telephone:" : @"\U0000260E", \ @":telephone_receiver:" : @"\U0001F4DE", \ @":telescope:" : @"\U0001F52D", \ @":tennis:" : @"\U0001F3BE", \ @":tent:" : @"\U000026FA", \ @":thought_balloon:" : @"\U0001F4AD", \ @":thumbsdown:" : @"\U0001F44E", \ @":thumbsup:" : @"\U0001F44D", \ @":ticket:" : @"\U0001F3AB", \ @":tiger:" : @"\U0001F42F", \ @":tiger2:" : @"\U0001F405", \ @":tired_face:" : @"\U0001F62B", \ @":tm:" : @"\U00002122", \ @":toilet:" : @"\U0001F6BD", \ @":tokyo_tower:" : @"\U0001F5FC", \ @":tomato:" : @"\U0001F345", \ @":tongue:" : @"\U0001F445", \ @":top:" : @"\U0001F51D", \ @":tophat:" : @"\U0001F3A9", \ @":tractor:" : @"\U0001F69C", \ @":traffic_light:" : @"\U0001F6A5", \ @":train:" : @"\U0001F683", \ @":train2:" : @"\U0001F686", \ @":tram:" : @"\U0001F68A", \ @":triangular_flag_on_post:" : @"\U0001F6A9", \ @":triangular_ruler:" : @"\U0001F4D0", \ @":trident:" : @"\U0001F531", \ @":triumph:" : @"\U0001F624", \ @":trolleybus:" : @"\U0001F68E", \ @":trophy:" : @"\U0001F3C6", \ @":tropical_drink:" : @"\U0001F379", \ @":tropical_fish:" : @"\U0001F420", \ @":truck:" : @"\U0001F69A", \ @":trumpet:" : @"\U0001F3BA", \ @":tshirt:" : @"\U0001F455", \ @":tulip:" : @"\U0001F337", \ @":turtle:" : @"\U0001F422", \ @":tv:" : @"\U0001F4FA", \ @":twisted_rightwards_arrows:" : @"\U0001F500", \ @":two_hearts:" : @"\U0001F495", \ @":two_men_holding_hands:" : @"\U0001F46C", \ @":two_women_holding_hands:" : @"\U0001F46D", \ @":u5272:" : @"\U0001F239", \ @":u5408:" : @"\U0001F234", \ @":u55b6:" : @"\U0001F23A", \ @":u6307:" : @"\U0001F22F", \ @":u6708:" : @"\U0001F237", \ @":u6709:" : @"\U0001F236", \ @":u6e80:" : @"\U0001F235", \ @":u7121:" : @"\U0001F21A", \ @":u7533:" : @"\U0001F238", \ @":u7981:" : @"\U0001F232", \ @":u7a7a:" : @"\U0001F233", \ @":umbrella:" : @"\U00002614", \ @":unamused:" : @"\U0001F612", \ @":underage:" : @"\U0001F51E", \ @":unlock:" : @"\U0001F513", \ @":up:" : @"\U0001F199", \ @":v:" : @"\U0000270C", \ @":vertical_traffic_light:" : @"\U0001F6A6", \ @":vhs:" : @"\U0001F4FC", \ @":vibration_mode:" : @"\U0001F4F3", \ @":video_camera:" : @"\U0001F4F9", \ @":video_game:" : @"\U0001F3AE", \ @":violin:" : @"\U0001F3BB", \ @":virgo:" : @"\U0000264D", \ @":volcano:" : @"\U0001F30B", \ @":vs:" : @"\U0001F19A", \ @":walking:" : @"\U0001F6B6", \ @":waning_crescent_moon:" : @"\U0001F318", \ @":waning_gibbous_moon:" : @"\U0001F316", \ @":warning:" : @"\U000026A0", \ @":watch:" : @"\U0000231A", \ @":water_buffalo:" : @"\U0001F403", \ @":watermelon:" : @"\U0001F349", \ @":wave:" : @"\U0001F44B", \ @":wavy_dash:" : @"\U00003030", \ @":waxing_crescent_moon:" : @"\U0001F312", \ @":waxing_gibbous_moon:" : @"\U0001F314", \ @":wc:" : @"\U0001F6BE", \ @":weary:" : @"\U0001F629", \ @":wedding:" : @"\U0001F492", \ @":whale:" : @"\U0001F433", \ @":whale2:" : @"\U0001F40B", \ @":wheelchair:" : @"\U0000267F", \ @":white_check_mark:" : @"\U00002705", \ @":white_circle:" : @"\U000026AA", \ @":white_flower:" : @"\U0001F4AE", \ @":white_large_square:" : @"\U00002B1C", \ @":white_medium_small_square:" : @"\U000025FD", \ @":white_medium_square:" : @"\U000025FB", \ @":white_small_square:" : @"\U000025AB", \ @":white_square_button:" : @"\U0001F533", \ @":wind_chime:" : @"\U0001F390", \ @":wine_glass:" : @"\U0001F377", \ @":wink:" : @"\U0001F609", \ @":wolf:" : @"\U0001F43A", \ @":woman:" : @"\U0001F469", \ @":womans_clothes:" : @"\U0001F45A", \ @":womans_hat:" : @"\U0001F452", \ @":womens:" : @"\U0001F6BA", \ @":worried:" : @"\U0001F61F", \ @":wrench:" : @"\U0001F527", \ @":x:" : @"\U0000274C", \ @":yellow_heart:" : @"\U0001F49B", \ @":yen:" : @"\U0001F4B4", \ @":yum:" : @"\U0001F60B", \ @":zap:" : @"\U000026A1", \ @":zzz:" : @"\U0001F4A4" \ } ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/NSStringEmojize.podspec ================================================ Pod::Spec.new do |s| s.name = 'NSStringEmojize' s.version = '0.2.0' s.license = 'Apache 2.0' s.summary = 'A category on NSString to turn codes from Emoji Cheat Sheet (http://www.emoji-cheat-sheet.com/) into Unicode emoji characters.' s.homepage = 'https://github.com/diy/nsstringemojize' s.authors = {'Jon Beilin' => 'jon@diy.org'} s.source = { :git => 'https://github.com/diy/NSStringEmojize.git', :tag => 'v0.2.0' } s.platform = :ios s.source_files = 'NSStringEmojize' s.requires_arc = true s.framework = 'UIKit' end ================================================ FILE: archive/bitbar/App/Vendor/NSStringEmojize/README.md ================================================ ## NSString+Emojize #### A category on NSString to turn codes from [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) into Unicode emoji characters. ## Getting Started In order to use NSString+Emojize, you'll want to add the entirety of the `NSString+Emojize` directory to your project. To get started, simply: ```objective-c #import "NSString+Emojize.h" ``` ```objective-c NSString *emojiString = @"This comment has an emoji :mushroom:"; NSLog(@"%@", [emojiString emojizedString]); ``` --- ## Methods ```objective-c - (NSString *)emojizedString; + (NSString *)emojizedStringWithString:(NSString *)aString; ``` --- ## iOS Support NSString+Emojize is tested on iOS 5 and up. Older versions of iOS may work but are not currently supported. ## ARC NSString+Emojize uses ARC. If you are including NSString+Emojize in a project that **does not** use [Automatic Reference Counting (ARC)](http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html), you will need to set the `-fobjc-arc` compiler flag on all of the NSString+Emojize source files. To do this in Xcode, go to your active target and select the "Build Phases" tab. Now select all NSString+Emojize source files, press Enter, insert `-fobjc-arc` and then "Done" to enable ARC for NSString+Emojize. ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/.gitignore ================================================ *.xcuserstate UserInterfaceState.xcuserstate *dsa_priv.pem Platypus.build BuildData .DS_Store *.swp *~.nib *.pbxuser *.perspective *.perspectivev3 *.mode1v3 *.mode2v3 *.xcodeproj/xcuserdata/*.xcuserdatad *.xcodeproj/project.xcworkspace/xcuserdata/*.xcuserdatad xcuserdata/ platypus.man.html *UserInterfaceState.xcuserstate .DS_Store build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 *.xcworkspace !default.xcworkspace xcuserdata profile *.moved-aside DerivedData .idea/ Pods ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/LICENSE.txt ================================================ # BSD License # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Sveinbjorn Thordarson nor that of any other # contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/PrivilegedTaskExample/PrivilegedTaskExample/AppDelegate.h ================================================ /* # PrivilegedTaskExample # Copyright (C) 2015 Sveinbjorn Thordarson # # BSD License # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Sveinbjorn Thordarson nor that of any other # contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import @interface AppDelegate : NSObject @property IBOutlet id commandTextField; @property IBOutlet id outputTextField; - (IBAction)runNSTask:(id)sender; - (IBAction)runSTPrivilegedTask:(id)sender; @end ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/PrivilegedTaskExample/PrivilegedTaskExample/AppDelegate.m ================================================ /* # PrivilegedTaskExample # Copyright (C) 2015 Sveinbjorn Thordarson # # BSD License # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Sveinbjorn Thordarson nor that of any other # contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "AppDelegate.h" #import "STPrivilegedTask.h" @interface AppDelegate () @property (weak) IBOutlet NSWindow *window; @end @implementation AppDelegate - (IBAction)runNSTask:(id)sender { NSTask *task = [[NSTask alloc] init]; NSMutableArray *components = [[[self.commandTextField stringValue] componentsSeparatedByString:@" "] mutableCopy]; task.launchPath = components[0]; [components removeObjectAtIndex:0]; task.arguments = components; NSPipe *outputPipe = [NSPipe pipe]; [task setStandardOutput:outputPipe]; [task setStandardError:outputPipe]; NSFileHandle *readHandle = [outputPipe fileHandleForReading]; [task launch]; [task waitUntilExit]; NSData *outputData = [readHandle readDataToEndOfFile]; NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding]; [self.outputTextField setString:outputString]; } - (IBAction)runSTPrivilegedTask:(id)sender { //initalize task STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init]; NSMutableArray *components = [[[self.commandTextField stringValue] componentsSeparatedByString:@" "] mutableCopy]; NSString *launchPath = components[0]; [components removeObjectAtIndex:0]; [privilegedTask setLaunchPath:launchPath]; [privilegedTask setArguments:components]; [privilegedTask setCurrentDirectoryPath:[[NSBundle mainBundle] resourcePath]]; //set it off OSStatus err = [privilegedTask launch]; if (err != errAuthorizationSuccess) { if (err == errAuthorizationCanceled) { return; } else { NSLog(@"Something went wrong"); } } // Success! Now, start monitoring output file handle for data NSFileHandle *readHandle = [privilegedTask outputFileHandle]; NSData *outputData = [readHandle readDataToEndOfFile]; NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding]; [self.outputTextField setString:outputString]; } @end ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/PrivilegedTaskExample/PrivilegedTaskExample/Base.lproj/MainMenu.xib ================================================ Default Left to Right Right to Left Default Left to Right Right to Left ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/PrivilegedTaskExample/PrivilegedTaskExample/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile lock-icon CFBundleIdentifier org.sveinbjorn.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright Copyright © 2015 Sveinbjorn Thordarson. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass NSApplication ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/PrivilegedTaskExample/PrivilegedTaskExample/main.m ================================================ // // main.m // PrivilegedTaskExample // // Created by Sveinbjorn Thordarson on 11/07/15. // Copyright (c) 2015 Sveinbjorn Thordarson. All rights reserved. // #import int main(int argc, const char * argv[]) { return NSApplicationMain(argc, argv); } ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/PrivilegedTaskExample/PrivilegedTaskExample.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ F4F1035E1B5155A200BCAD40 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F1035D1B5155A200BCAD40 /* AppDelegate.m */; }; F4F103601B5155A200BCAD40 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F1035F1B5155A200BCAD40 /* main.m */; }; F4F103651B5155A200BCAD40 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4F103631B5155A200BCAD40 /* MainMenu.xib */; }; F4F1037C1B515A1C00BCAD40 /* STPrivilegedTask.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F1037B1B515A1C00BCAD40 /* STPrivilegedTask.m */; }; F4F103801B5166CF00BCAD40 /* lock-icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = F4F1037F1B5166CF00BCAD40 /* lock-icon.icns */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ F4F103571B5155A200BCAD40 /* PrivilegedTaskExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PrivilegedTaskExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; F4F1035B1B5155A200BCAD40 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F4F1035C1B5155A200BCAD40 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; F4F1035D1B5155A200BCAD40 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; F4F1035F1B5155A200BCAD40 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; F4F103641B5155A200BCAD40 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; F4F1037A1B515A1C00BCAD40 /* STPrivilegedTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPrivilegedTask.h; path = ../../STPrivilegedTask.h; sourceTree = ""; }; F4F1037B1B515A1C00BCAD40 /* STPrivilegedTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STPrivilegedTask.m; path = ../../STPrivilegedTask.m; sourceTree = ""; }; F4F1037E1B51664C00BCAD40 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../README.md; sourceTree = ""; }; F4F1037F1B5166CF00BCAD40 /* lock-icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "lock-icon.icns"; sourceTree = ""; }; F4F103811B516A3500BCAD40 /* STPrivilegedTask.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; name = STPrivilegedTask.podspec; path = ../../STPrivilegedTask.podspec; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ F4F103541B5155A200BCAD40 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ F4F1034E1B5155A200BCAD40 = { isa = PBXGroup; children = ( F4F1037D1B51663800BCAD40 /* STPrivilegedTask */, F4F103591B5155A200BCAD40 /* PrivilegedTaskExample */, F4F103581B5155A200BCAD40 /* Products */, ); sourceTree = ""; }; F4F103581B5155A200BCAD40 /* Products */ = { isa = PBXGroup; children = ( F4F103571B5155A200BCAD40 /* PrivilegedTaskExample.app */, ); name = Products; sourceTree = ""; }; F4F103591B5155A200BCAD40 /* PrivilegedTaskExample */ = { isa = PBXGroup; children = ( F4F1035F1B5155A200BCAD40 /* main.m */, F4F1035C1B5155A200BCAD40 /* AppDelegate.h */, F4F1035D1B5155A200BCAD40 /* AppDelegate.m */, F4F103631B5155A200BCAD40 /* MainMenu.xib */, F4F1037F1B5166CF00BCAD40 /* lock-icon.icns */, F4F1035B1B5155A200BCAD40 /* Info.plist */, ); path = PrivilegedTaskExample; sourceTree = ""; }; F4F1037D1B51663800BCAD40 /* STPrivilegedTask */ = { isa = PBXGroup; children = ( F4F1037E1B51664C00BCAD40 /* README.md */, F4F103811B516A3500BCAD40 /* STPrivilegedTask.podspec */, F4F1037A1B515A1C00BCAD40 /* STPrivilegedTask.h */, F4F1037B1B515A1C00BCAD40 /* STPrivilegedTask.m */, ); name = STPrivilegedTask; path = PrivilegedTaskExample; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ F4F103561B5155A200BCAD40 /* PrivilegedTaskExample */ = { isa = PBXNativeTarget; buildConfigurationList = F4F103741B5155A200BCAD40 /* Build configuration list for PBXNativeTarget "PrivilegedTaskExample" */; buildPhases = ( F4F103531B5155A200BCAD40 /* Sources */, F4F103541B5155A200BCAD40 /* Frameworks */, F4F103551B5155A200BCAD40 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = PrivilegedTaskExample; productName = PrivilegedTaskExample; productReference = F4F103571B5155A200BCAD40 /* PrivilegedTaskExample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ F4F1034F1B5155A200BCAD40 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0640; ORGANIZATIONNAME = "Sveinbjorn Thordarson"; TargetAttributes = { F4F103561B5155A200BCAD40 = { CreatedOnToolsVersion = 6.4; }; }; }; buildConfigurationList = F4F103521B5155A200BCAD40 /* Build configuration list for PBXProject "PrivilegedTaskExample" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = F4F1034E1B5155A200BCAD40; productRefGroup = F4F103581B5155A200BCAD40 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( F4F103561B5155A200BCAD40 /* PrivilegedTaskExample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ F4F103551B5155A200BCAD40 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( F4F103801B5166CF00BCAD40 /* lock-icon.icns in Resources */, F4F103651B5155A200BCAD40 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ F4F103531B5155A200BCAD40 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F4F103601B5155A200BCAD40 /* main.m in Sources */, F4F1035E1B5155A200BCAD40 /* AppDelegate.m in Sources */, F4F1037C1B515A1C00BCAD40 /* STPrivilegedTask.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ F4F103631B5155A200BCAD40 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( F4F103641B5155A200BCAD40 /* Base */, ); name = MainMenu.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ F4F103721B5155A200BCAD40 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; F4F103731B5155A200BCAD40 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; name = Release; }; F4F103751B5155A200BCAD40 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = PrivilegedTaskExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; F4F103761B5155A200BCAD40 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = PrivilegedTaskExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ F4F103521B5155A200BCAD40 /* Build configuration list for PBXProject "PrivilegedTaskExample" */ = { isa = XCConfigurationList; buildConfigurations = ( F4F103721B5155A200BCAD40 /* Debug */, F4F103731B5155A200BCAD40 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F4F103741B5155A200BCAD40 /* Build configuration list for PBXNativeTarget "PrivilegedTaskExample" */ = { isa = XCConfigurationList; buildConfigurations = ( F4F103751B5155A200BCAD40 /* Debug */, F4F103761B5155A200BCAD40 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = F4F1034F1B5155A200BCAD40 /* Project object */; } ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/README.md ================================================ # STPrivilegedTask - Objective C class An NSTask-like wrapper around AuthorizationExecuteWithPrivileges() in the Security API to run shell commands with root privileges in Mac OS X. Created a long time ago. It has been updated to support ARC and is available via CocoaPods. ## Examples ### Create and launch task ```objective-c STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init]; [privilegedTask setLaunchPath:@"/usr/bin/touch"]; NSArray *args = [NSArray arrayWithObject:@"/etc/my_test_file"]; [privilegedTask setArguments:args]; // this is optional, defaults to / // [privilegedTask setCurrentDirectoryPath:[[NSBundle mainBundle] resourcePath]]; // launch it, user is prompted for password OSStatus err = [privilegedTask launch]; if (err != errAuthorizationSuccess) { if (err == errAuthorizationCanceled) { NSLog(@"User cancelled"); } else { NSLog(@"Something went wrong"); } } else { // task successfully launched } ``` ### Getting task output ```objective-c // ... launch task [privilegedTask waitUntilExit]; // Read output file handle for data NSFileHandle *readHandle = [privilegedTask outputFileHandle]; NSData *outputData = [readHandle readDataToEndOfFile]; NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding]; ``` ### Getting output while task runs in background ```objective-c // ... launch task NSFileHandle *readHandle = [privilegedTask outputFileHandle]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getOutputData:) name:NSFileHandleReadCompletionNotification object:readHandle]; [readHandle readInBackgroundAndNotify]; // ... - (void)getOutputData:(NSNotification *)aNotification { //get data from notification NSData *data = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]; //make sure there's actual data if ([data length]) { // do something with the data // go read more data in the background [[aNotification object] readInBackgroundAndNotify]; } else { // do something else } } ``` ### Notification when task terminates Observe STPrivilegedTaskDidTerminateNotification. ```objective-c [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(privilegedTaskFinished:) name:STPrivilegedTaskDidTerminateNotification object:nil]; - (void)privilegedTaskFinished:(NSNotification *)aNotification { // do something } ``` # BSD License ``` # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Sveinbjorn Thordarson nor that of any other # contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ``` ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/STPrivilegedTask.h ================================================ /* # # STPrivilegedTask - NSTask-like wrapper around AuthorizationExecuteWithPrivileges # Copyright (C) 2009-2015 Sveinbjorn Thordarson # # BSD License # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Sveinbjorn Thordarson nor that of any other # contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import #import #import #import #define STPrivilegedTaskDidTerminateNotification @"STPrivilegedTaskDidTerminateNotification" //#define TMP_STDERR_TEMPLATE @".authStderr.XXXXXX" // Defines error value for when AuthorizationExecuteWithPrivilleges no longer // exists anyplace. Rather than defining a new enum, we just create a global // constant extern const OSStatus errAuthorizationFnNoLongerExists; @interface STPrivilegedTask : NSObject { NSArray *arguments; NSString *cwd; NSString *launchPath; BOOL isRunning; pid_t pid; int terminationStatus; NSFileHandle *outputFileHandle; NSTimer *checkStatusTimer; } -(id)initWithLaunchPath:(NSString *)path; -(id)initWithLaunchPath:(NSString *)path arguments: (NSArray *)args; +(STPrivilegedTask *)launchedPrivilegedTaskWithLaunchPath:(NSString *)path; +(STPrivilegedTask *)launchedPrivilegedTaskWithLaunchPath:(NSString *)path arguments:(NSArray *)arguments; -(NSArray *)arguments; -(NSString *)currentDirectoryPath; -(BOOL)isRunning; -(int)launch; -(NSString *)launchPath; -(int)processIdentifier; -(void)setArguments:(NSArray *)arguments; -(void)setCurrentDirectoryPath:(NSString *)path; -(void)setLaunchPath:(NSString *)path; -(NSFileHandle *)outputFileHandle; -(void)terminate; // doesn't work -(int)terminationStatus; -(void)_checkTaskStatus; -(void)waitUntilExit; @end /*static OSStatus AuthorizationExecuteWithPrivilegesStdErrAndPid ( AuthorizationRef authorization, const char *pathToTool, AuthorizationFlags options, char * const *arguments, FILE **communicationsPipe, FILE **errPipe, pid_t* processid );*/ ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/STPrivilegedTask.m ================================================ /* # # STPrivilegedTask - NSTask-like wrapper around AuthorizationExecuteWithPrivileges # Copyright (C) 2009-2015 Sveinbjorn Thordarson # # BSD License # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Sveinbjorn Thordarson nor that of any other # contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "STPrivilegedTask.h" #import #import #import /* New error code denoting that AuthorizationExecuteWithPrivileges no longer exists */ OSStatus const errAuthorizationFnNoLongerExists = -70001; @implementation STPrivilegedTask - (id)init { if ((self = [super init])) { launchPath = @""; cwd = [[NSString alloc] initWithString:[[NSFileManager defaultManager] currentDirectoryPath]]; arguments = [[NSArray alloc] init]; isRunning = NO; outputFileHandle = nil; } return self; } -(void)dealloc { #if !__has_feature(objc_arc) [launchPath release]; [arguments release]; [cwd release]; if (outputFileHandle != nil) { [outputFileHandle release]; } [super dealloc]; #endif } -(id)initWithLaunchPath:(NSString *)path arguments:(NSArray *)args { if ((self = [self initWithLaunchPath:path])) { [self setArguments:args]; } return self; } -(id)initWithLaunchPath:(NSString *)path { if ((self = [self init])) { [self setLaunchPath:path]; } return self; } #pragma mark - +(STPrivilegedTask *)launchedPrivilegedTaskWithLaunchPath:(NSString *)path arguments:(NSArray *)args { STPrivilegedTask *task = [[STPrivilegedTask alloc] initWithLaunchPath:path arguments:args]; #if !__has_feature(objc_arc) [task autorelease]; #endif [task launch]; [task waitUntilExit]; return task; } +(STPrivilegedTask *)launchedPrivilegedTaskWithLaunchPath:(NSString *)path { STPrivilegedTask *task = [[STPrivilegedTask alloc] initWithLaunchPath:path]; #if !__has_feature(objc_arc) [task autorelease]; #endif [task launch]; [task waitUntilExit]; return task; } #pragma mark - - (NSArray *)arguments { return arguments; } - (NSString *)currentDirectoryPath; { return cwd; } - (BOOL)isRunning { return isRunning; } - (NSString *)launchPath { return launchPath; } - (int)processIdentifier { return pid; } - (int)terminationStatus { return terminationStatus; } - (NSFileHandle *)outputFileHandle; { return outputFileHandle; } #pragma mark - -(void)setArguments:(NSArray *)args { #if !__has_feature(objc_arc) [arguments release]; [args retain]; #endif arguments = args; } -(void)setCurrentDirectoryPath:(NSString *)path { #if !__has_feature(objc_arc) [cwd release]; [path retain]; #endif cwd = path; } -(void)setLaunchPath:(NSString *)path { #if !__has_feature(objc_arc) [launchPath release]; [path retain]; #endif launchPath = path; } # pragma mark - // return 0 for success -(int)launch { OSStatus err = noErr; const char *toolPath = [launchPath fileSystemRepresentation]; AuthorizationRef authorizationRef; AuthorizationItem myItems = {kAuthorizationRightExecute, strlen(toolPath), &toolPath, 0}; AuthorizationRights myRights = {1, &myItems}; AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; NSUInteger numberOfArguments = [arguments count]; char *args[numberOfArguments + 1]; FILE *outputFile; // Create fn pointer to AuthorizationExecuteWithPrivileges in case it doesn't exist // in this version of MacOS static OSStatus (*_AuthExecuteWithPrivsFn)( AuthorizationRef authorization, const char *pathToTool, AuthorizationFlags options, char * const *arguments, FILE **communicationsPipe) = NULL; // Check to see if we have the correct function in our loaded libraries if (!_AuthExecuteWithPrivsFn) { // On 10.7, AuthorizationExecuteWithPrivileges is deprecated. We want // to still use it since there's no good alternative (without requiring // code signing). We'll look up the function through dyld and fail if // it is no longer accessible. If Apple removes the function entirely // this will fail gracefully. If they keep the function and throw some // sort of exception, this won't fail gracefully, but that's a risk // we'll have to take for now. // Pattern by Andy Kim from Potion Factory LLC _AuthExecuteWithPrivsFn = dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges"); if (!_AuthExecuteWithPrivsFn) { // This version of OS X has finally removed this function. Exit with an error. err = errAuthorizationFnNoLongerExists; return err; } } // Use Apple's Authentication Manager APIs to get an Authorization Reference // These Apple APIs are quite possibly the most horrible of the Mac OS X APIs // create authorization reference err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); if (err != errAuthorizationSuccess) { return err; } // pre-authorize the privileged operation err = AuthorizationCopyRights(authorizationRef, &myRights, kAuthorizationEmptyEnvironment, flags, NULL); if (err != errAuthorizationSuccess) { return err; } // OK, at this point we have received authorization for the task. // Let's prepare to launch it // first, construct an array of c strings from NSArray w. arguments for (int i = 0; i < numberOfArguments; i++) { NSString *argString = arguments[i]; NSUInteger stringLength = [argString length]; args[i] = malloc((stringLength + 1) * sizeof(char)); snprintf(args[i], stringLength + 1, "%s", [argString fileSystemRepresentation]); } args[numberOfArguments] = NULL; // change to the current dir specified char *prevCwd = (char *)getcwd(nil, 0); chdir([cwd fileSystemRepresentation]); //use Authorization Reference to execute script with privileges err = _AuthExecuteWithPrivsFn(authorizationRef, [launchPath fileSystemRepresentation], kAuthorizationFlagDefaults, args, &outputFile); // OK, now we're done executing, let's change back to old dir chdir(prevCwd); // free the malloc'd argument strings for (int i = 0; i < numberOfArguments; i++) { free(args[i]); } // free the auth ref AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); // we return err if execution failed if (err != errAuthorizationSuccess) { return err; } else { isRunning = YES; } // get file handle for the command output outputFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileno(outputFile) closeOnDealloc:YES]; pid = fcntl(fileno(outputFile), F_GETOWN, 0); // start monitoring task checkStatusTimer = [NSTimer scheduledTimerWithTimeInterval:0.10 target:self selector:@selector(_checkTaskStatus) userInfo:nil repeats:YES]; return err; } - (void)terminate { // This doesn't work without a PID, and we can't get one. Stupid Security API /* int ret = kill(pid, SIGKILL); if (ret != 0) NSLog(@"Error %d", errno);*/ } // hang until task is done - (void)waitUntilExit { waitpid([self processIdentifier], &terminationStatus, 0); isRunning = NO; } #pragma mark - // check if privileged task is still running - (void)_checkTaskStatus { // see if task has terminated int mypid = waitpid([self processIdentifier], &terminationStatus, WNOHANG); if (mypid != 0) { isRunning = NO; [[NSNotificationCenter defaultCenter] postNotificationName:STPrivilegedTaskDidTerminateNotification object:self]; [checkStatusTimer invalidate]; } } #pragma mark - - (NSString *)description { NSArray *args = [self arguments]; NSString *cmd = [[self launchPath] copy]; for (int i = 0; i < [args count]; i++) { cmd = [cmd stringByAppendingFormat:@" %@", args[i]]; } return [[super description] stringByAppendingFormat:@" %@", cmd]; } @end /* * * Add the Standard err Pipe and Pid support to AuthorizationExecuteWithPrivileges() * method * * @Author: Miklós Fazekas * Modified Aug 10 2010 by Sveinbjorn Thordarson * */ /*static OSStatus AuthorizationExecuteWithPrivilegesStdErrAndPid ( AuthorizationRef authorization, const char *pathToTool, AuthorizationFlags options, char * const *arguments, FILE **communicationsPipe, FILE **errPipe, pid_t* processid ) { // get the Apple-approved secure temp directory NSString *tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent: TMP_STDERR_TEMPLATE]; // copy it into a C string const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation]; char *stderrpath = (char *)malloc(strlen(tempFileTemplateCString) + 1); strcpy(stderrpath, tempFileTemplateCString); printf("%s\n", stderrpath); // this is the command, it echoes pid and directs stderr output to pipe before running the tool w. args const char *commandtemplate = "echo $$; \"$@\" 2>%s"; if (communicationsPipe == errPipe) commandtemplate = "echo $$; \"$@\" 2>1"; else if (errPipe == 0) commandtemplate = "echo $$; \"$@\""; char command[1024]; char **args; OSStatus result; int argcount = 0; int i; int stderrfd = 0; FILE *commPipe = 0; // First, create temporary file for stderr if (errPipe) { // create temp file stderrfd = mkstemp(stderrpath); // close and remove it close(stderrfd); unlink(stderrpath); // create a pipe on the path of the temp file if (mkfifo(stderrpath,S_IRWXU | S_IRWXG) != 0) { fprintf(stderr,"Error mkfifo:%d\n", errno); return errAuthorizationInternal; } if (stderrfd < 0) return errAuthorizationInternal; } // Create command to be executed for (argcount = 0; arguments[argcount] != 0; ++argcount) {} args = (char**)malloc (sizeof(char*)*(argcount + 5)); args[0] = "-c"; snprintf (command, sizeof (command), commandtemplate, stderrpath); args[1] = command; args[2] = ""; args[3] = (char*)pathToTool; for (i = 0; i < argcount; ++i) { args[i+4] = arguments[i]; } args[argcount+4] = 0; // for debugging: log the executed command printf ("Exec:\n%s", "/bin/sh"); for (i = 0; args[i] != 0; ++i) { printf (" \"%s\"", args[i]); } printf ("\n"); // Execute command result = AuthorizationExecuteWithPrivileges(authorization, "/bin/sh", options, args, &commPipe ); if (result != noErr) { unlink (stderrpath); return result; } // Read the first line of stdout => it's the pid { int stdoutfd = fileno (commPipe); char pidnum[1024]; pid_t pid = 0; int i = 0; char ch = 0; while ((read(stdoutfd, &ch, sizeof(ch)) == 1) && (ch != '\n') && (i < sizeof(pidnum))) { pidnum[i++] = ch; } pidnum[i] = 0; if (ch != '\n') { // we shouldn't get there unlink (stderrpath); return errAuthorizationInternal; } sscanf(pidnum, "%d", &pid); if (processid) { *processid = pid; } NSLog(@"Have PID %d", pid); } // if (errPipe) { stderrfd = open(stderrpath, O_RDONLY, 0); // *errPipe = fdopen(stderrfd, "r"); //Now it's safe to unlink the stderr file, as the opened handle will be still valid unlink (stderrpath); } else { unlink(stderrpath); } if (communicationsPipe) *communicationsPipe = commPipe; else fclose (commPipe); NSLog(@"AuthExecNew function over"); return noErr; }*/ ================================================ FILE: archive/bitbar/App/Vendor/STPrivilegedTask/STPrivilegedTask.podspec ================================================ Pod::Spec.new do |s| s.name = "STPrivilegedTask" s.version = "1.0.1" s.summary = "An NSTask-like wrapper around Mac OS X Security Framework's AuthorizationExecuteWithPrivileges()" s.description = "An NSTask-like wrapper around AuthorizationExecuteWithPrivileges() in the Security API to run shell commands with root privileges in Mac OS X." s.homepage = "http://github.com/sveinbjornt/STPrivilegedTask" s.license = { :type => 'BSD' } s.author = { "Sveinbjorn Thordarson" => "sveinbjornt@gmail.com" } s.osx.deployment_target = "10.6" s.source = { :git => "https://github.com/sveinbjornt/STPrivilegedTask.git", :tag => "1.0.1" } s.source_files = "STPrivilegedTask.{h,m}" s.exclude_files = "PrivilegedTaskExample" s.public_header_files = "STPrivilegedTask.h" s.framework = "Security" s.requires_arc = false end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/.clang-format ================================================ AccessModifierOffset: -4 AlignEscapedNewlinesLeft: true AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackParameters: true BreakBeforeBinaryOperators: true BreakBeforeBraces: Linux BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: false #DerivePointerAlignment: false DisableFormat: false #ForEachMacros: foreach,Q_FOREACH IndentCaseLabels: true IndentFunctionDeclarationAfterType: true IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false Language: Cpp MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true #PointerAlignment: Right SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false Standard: Cpp03 TabWidth: 4 UseTab: Never ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/.gitignore ================================================ build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.xcuserstate .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/.travis.yml ================================================ language: objective-c osx_image: xcode7 before_install: - brew update - brew install macmade/tap/xcode-coveralls script: make ci after_success: xcode-coveralls --exclude /Applications --exclude Tests --exclude Vendor build/Build/Intermediates/Sparkle.build/Coverage/Sparkle.build/Objects-normal/x86_64 ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/CHANGELOG ================================================ * A new icon! Thanks to 1024jp * Show alert when an update is sent over insecure HTTP with no DSA key (Zorg) - If you can't use HTTPS, you must at least sign updates with a DSA key. * Clear update caches directory before downloading new update (Zorg) * Check the bundle's parent directory for writability too (Zorg) * Don't follow symbolic links for file operations (Zorg) * Don't bring up an authorized dialog during cleanup (Zorg) * Made Sparkle look for the highest compatible version regardless of timestamps (Zorg) * Fixed compatibility with 10.7 * Fixed crash on 10.7 - subscript operator not available (kleuter) * Fixed warnings caused by -Wpartial-availability (Zorg) * Fixed german l10n. (Sebastian Volland) * Error code for download errors (Kornel Lesiński) * Update last update check date when the update driver finishes (Zorg) * Scale app icon up if needed in Software Update window (#763) (Nicholas Riley) * Improved tests (Zorg) * Don't register for termination notifications more than once. Fixes #757 (Zorg) * Don't terminate the app if we're already terminating (Zorg) * Removed SUEnableAutomaticChecksKeyOld and SUCheckAtStartup constants (Eitot) # 1.14.0 (Mar 11, 2016) * Disable javascript by default and make it opt-in (Zorg) * URL-encoding of appcast URLs is preserved (Kornel Lesiński) * Delegate is asked for fallback updates if delta update fails (Kornel Lesiński) * Fixed crash on 10.7 - subscript operator not available (kleuter) * Fixed check of feed URL before delegate had a chance to set it (Kornel Lesiński) * Re-added support for password-protected dmg images (Andrew K. Boyd) * Added warning about ATS blocking (Kornel Lesiński) * Translation fixes for pt-BR. (vitu) * Add some Japanese lozalized strings (1024jp) * Made test app available in all languages #695 (LIU Dongyuan / 柳东原) * Czech localizations update (Frantisek Erben) * Removed a test resource from the framework bundle (Karl Moskowski) * Test if the updated app is the frontmost one (Zorg) * UI Tests for the Test Application (Zorg) # 1.13.1 (Jan 31, 2016) Important security fixes: * Prevent inclusion of local files via file:// XML entities * Disable redirects to non-HTTP URLs in release notes webview # 1.13.0 (Dec 18, 2015) * Changed framework's bundle ID from `org.andymatuschak.Sparkle` to `org.sparkle-project.Sparkle`. # 1.12.0 (Dec 13, 2015) * Rewritten file operations for updating an app (Zorg) - Ensuring atomic move operations, robust error handling. - Faster. - Using modern APIs where possible (no FSPathMakeRef, FSGetCatalogInfo, FSFindFolder, etc.) - Strong documentation, easier to read code. * Automatic updates won't be installed if the system is about to shut off (Zorg) * Deprecated serving over HTTP without DSA (Zorg) - Note that Apple has deprecated insecure HTTP in macOS 10.11 * Improved Autoupdate application (Zorg) * Do all the installation work after the runloop is set up * TerminationListener only does termination listening now * Handle cases where host path is not installation path and host path is not desired executable path * Don't show Autoupdate dock icon if we shouldn't show UI * Update modification & access time for new update * Added installUpdatesIfAvailable (Ian Langworth) * Removed extensions from shell scripts (Jake Petroules) * Rewritten test app so it works again, and from a local web server (Zorg) * Replaced use of Python with built-in web server (Kevin Wojniak) * Set LD_RUNPATH_SEARCH_PATHS in Podspec (Jake Petroules) * Don't install automatic updates if the system might shut off (Zorg) * Don't show Autoupdate dock icon if we shouldn't show UI (Zorg) * Updated layout constraints when removing release notes (Zorg) * Improved BinaryDelta error handling & logging (Zorg) * Refactored quarantine removal (Zorg) * Fixed German localization (1024jp) * Updated zh_CN translation (LIU Dongyuan / 柳东原) * Updated Mac models list until July 2015 (Gabriel Ulici) * Updated Polish translation (Kornel Lesiński) * Updated Xcode project languages for which we have translations (Jake Petroules) * Updated XIB files (Kornel Lesiński) * Use NSByteCountFormatter if available (Jake Petroules) * Declared protocols on SUUpdateAlert for the 10.11 SDK (Daniel Jalkut) * Silenced warning about casting away const-ness and -Wassign-enum (Daniel Jalkut) * Added script to generate a report comparing the Sparkle.strings files (Kevin Wojniak) * Check for empty strings (as well as nil) in SUHost's -name method (Karl Moskowski) * Don't follow symlinks for checking file existence (Zorg) * Unit tests in Swift (Zorg, Jake Petroules) * Fixed framework imports (Felix Schulze) * Fixed issues with copying files from different mounted drives (Zorg) * Disallowed automatic updates when user can't write to the bundle (Zorg) * Set the task working directories instead of changing the process working directory (Kevin Wojniak) # 1.11.1 (Nov 9, 2015) * Don't install automatic updates when system is about to shut down # 1.11.0 (Aug 24, 2015) * Big improvements to code signing and DSA verification - Sparkle now checks not only whether an update is correctly signed, but also whether the updated version will be able to verify future updates. Updates now must either use DSA keys correctly, or not try to use them at all. Same goes for Apple Code Signing. - Rely on code signing and the DSA key in the new app instead of appcast. If the new app has a public DSA key, then the appcast item must have a DSA signature for the app, even if the app is code signed. (Zorg) * More verbose error message when DSA keys don't match (Kornel Lesiński) * Added delegate methods for pre-download and immediately post-failed-download (Isaac Greenspan) * Fix Lucida Grande is always used for release notes (LIU Dongyuan / 柳东原) * Only remove quarantine with setResourceValue: when it's present. Fixes "Unable to quarantine: 93" messages from showing up in the console. (Zorg) * Fixed const and nullability warnings (Jake Petroules, Kornel Lesiński) * Replaced deprecated NSRunAlertPanel/alertWithMessageText (Kevin Wojniak) * Imported the Foundation umbrella header in all the public headers (C.W. Betts) * pt-BR localization update (Victor Figueiredo) * Reject unsupported code-signing xattrs in binary delta (Zorg) * Fixed crash while applying delta update (antonc27) * Added logging of appcast/download URL on error (Kornel Lesiński) * More robust reading of Autoupdate.app path from Sparkle bundle # 1.10.0 (Apr 26, 2015) * Massive improvements to the BinaryDelta tool (Zorg) - Ability to track file permissions (Zorg) - Nicely formatted log output (Zorg) - Numerous bug fixes in handling of symlinks, empty directories, case-insensitive names, etc. (Zorg) - Refactored and modernized code (Zorg) - libxar is no longer weak-linked (C.W. Betts) * Double-check the code signature of the the app after installation (Isaac Wankerl) * Added headless guided package installation (Graham Miln) * Added ability to inject custom HTTP headers in appcast request (Mattias Gunneras) * Changes to make unarching more reliable (Zorg, Kornel Lesiński) * Have Sparkle build a framework module (C.W. Betts) * Stdout used for non error outputs (JDuquennoy) * French locale update (Kent Sutherland) # 1.9.0 (Jan 26, 2015) * Added SUUpdater delegate method for failures. (Benjamin Gordon) * Make the error definitions public (C.W. Betts) * Add support for lzma compressed tarballs (Kyle Fuller) * Back to SKIP_INSTALL=YES by default (Tony Arnold) * Properly set install names and rpaths for targets (Jake Petroules) * Use Library/Caches rather than app support directory (Kornel Lesiński) * Check for a modal window being onscreen before trying to put up the Sparkle prompt (Alf Watt) * Fixed crashes on 10.7 (Chris Campbell, Ger Teunis) * Fixed Sparkle tags parsing (Tamás Lustyik) * SULog code cleanups (Kevin Wojniak) * Make sure CFBundleVersion is a semantic version number. (Jake Petroules) * Replace typedef enums with typedef NS_ENUM to make Swift happier (C.W. Betts) * Fix warnings under Xcode 6.1 relating the SUUpdateAlert XIB (Tony Arnold) * Prefer string constants to strings (Jake Petroules) * Use Info.plist keys instead of macros (Jake Petroules) * Only export public symbols. (Jake Petroules) * BinaryDelta: avoid crash with bad paths (Jake Petroules) * Fixing Swedish translations (Erik Vikström) * Turkish localization fixes (Emir) * Proofing of Ukrainian localization (Vera Tkachenko) # 1.8.0 (Jul 26, 2014) * New SUDSAVerifier based on up-to-date macOS APIs (Zachary Waldowski) * Detailed error log for failed signature checks (Kornel Lesiński) * Converted Sparkle to ARC (C.W. Betts) * Converted ivars to properties. (Jake Petroules) * Cocoapod support (Xhacker Liu) * Quarantine removal on macOS 10.10 (C.W. Betts) * Updated Japanese localization (1024jp) * Added Greek localization # 1.7.1 (Jul 2, 2014) * Removed option to install unverified updates (Kornel Lesiński) * Added detailed log when code signing verification fails (Sam Deane) * Restored original Sparkle icon. (Jake Petroules) * Switched SUUpdateAlert.xib to AutoLayout (Kornel Lesiński) * Replace references to andymatuschak.org with sparkle-project.org. (Jake Petroules) * Several code cleanups, modernizations, fixed warnings and improved code formatting (Jake Petroules) * Make the repository significantly more organized. (Jake Petroules) * Xcode project: set organization name and class prefix. (Jake Petroules) * Link to Foundation and AppKit instead of Cocoa. (Jake Petroules) * Use new operatingSystemVersion API when available. (Jake Petroules) * Add .clang-format configuration file for source code formatting. (Jake Petroules) * Add a target to build Sparkle API documentation using Doxygen. (Jake Petroules) # 1.7.0 * Dropped support for macOS 10.6. Sparkle now supports 10.7 and newer (including 10.10 Yosemite) on 64-bit Intel Macs (the last 32-bit Mac was released in 2006). * Removed use of deprecated functions (Zachary Waldowski) * Switched to modern Obj-C runtime and new literals syntax * Removed pre-10.7 code. (C.W. Betts) * Use more Blocks/libdispatch code. (C.W. Betts) * Cleaned up and improved security of `generate_keys`/`sign_update` scripts # 1.6.1 * Removed archive password prompt (Kornel Lesiński) * (Re)fixes bug where URLs are naively double escaped (Andrew Madsen) * Fixed typo that caused crashes in BinaryDelta (Tamas Lustyik) * SUStandardVersionComparator.h is public (Vincent CARLIER) * Remove pre-10.6-specific code. (C.W. Betts) * Objective C 2 getters and setters. (C.W. Betts) * Define correct dependencies on locale scripts (Antonin Hildebrand) # 1.6.0 * Cleaned up and deleted redundant strings files (Kornel Lesiński) * Modern Objective C syntax, properties where possible. (C.W. Betts) * Make SUAppcastDelegate a formal protocol. (C.W. Betts) * Fixed default font in release notes WebView (Kornel Lesiński) * Configurable name for finish_installation.app (Kornel Lesiński) * Removed code for 10.4 (Kornel Lesiński) * Convert all strings files to UTF-8 (UTF-16 must die) (Kornel Lesiński) * Removing GC target (Matt Thomas) * finish_installation.app and pkg files will not removed when we use *.pkg installer and restart system in the installer (Takayama Fumihiko) * Select Korean and Slovak for Sparkle.strings localization (Shon Frazier) * Updated the Romanian translation (Gabe) * pt-BR localization polishing (BR Lingo) * update zh_CN (61) * Shut up some warnings & make build with newer Xcode (Uli Kusterer) * Less unsafety with format strings (Uli Kusterer) * New icon (Rick Fillion) * fixed a 'content rectangle not entirely onscreen' warning (Simone Manganelli) * updated sends system profile to use info.plist if user defaults key isn't present (Jamie Pinkham) * Support for notifications on some updater events (Doug Russell) * Allow the delegate to trigger a silent install and relaunch (Matt Stevens) * Support silent relaunches (Matt Stevens) * Increment the sudden termination counter if installing on quit (Matt Stevens) * Prompts the user to update after a week (rather than a day) if he doesn't quit the app (Andy Matuschak) * Adding appcast item element, tag (Andy Matuschak) * We have this check box that says "Automatically download and install updates in the future." But we only download them automatically. We still ask permission again before installing them. (Andy Matuschak) # 1.5.0-beta6 * Important Changes * Sparkle now requires DSA signatures on your updates. Check the documentation for more information on how to set that up if you don't already sign your updates. You can bypass this requirement if you deliver both your appcast and your updates over SSL. * Sparkle will no longer display release notes located at file:// URLs, since Javascript on such a page would be able to read files on your file system. * For security reasons, Sparkle will refuse to install updates which appear to "downgrade" the app. * SUUpdater now implements new keys: "automaticallyDownloadsUpdates", "lastUpdateCheckDate", and "sendsSystemProfile." * Fixed a bug that could prevent SUProbingUpdateDriver from working. * Fixed a bug that prevented the updaterWillRelaunchApplication: delegate method from getting called. * Fixed displaying release notes transmitted "loose" in the key. * Fixed Sparkle compilation on 10.4 systems. * Fixed a bug that could cause window confusion if an app changed its LSUIElement at runtime. * Added support for Sparkle 1.1's behavior of disabling updates when the check interval is 0. * Sparkle can now handle appending parameters to URLs which already have parameters. * If an update's sparkle:shortVersionString is the same as the host's CFBundleShortVersionString, the sparkle:version and CFBundleVersion will be presented in parentheticals. # 1.5.0-beta5 * Important Changes! * Made every Sparkle class private except for SUUpdater, SUAppcast, SUAppcastItem, and the SUVersionComparisonProtocol. * There is now a single SUUpdater singleton for every host bundle; instead of -[SUUpdater setHostBundle], you can use +[SUUpdater updaterForBundle]. * Redefined the (entire) delegate protocol accordingly. * Renamed -[SUUpdater updatePreferencesChanged] to -[SUUpdater resetUpdateCycle]. This provides better semantics for non-apps, which need to start the update cycle manually. * -[SUUpdater checkForUpdatesWithDriver] is private. If you were using SUProbingUpdateDriver, you can now use -[SUUpdater checkForUpdateInformation] for a similar effect. * All the user defaults keys are now private; instead, SUUpdater is KVC-compliant for automaticallyChecksForUpdates, updateCheckInterval, and feedURL. * Reduced the size of the English-only framework by 25%. * System profiling information is now only submitted to the server once per week; this will help normalize your statistics across users with different interval preferences. * The feedParamatersForUpdater: delegate method now requires "displayKey" and "displayVersion" keys so that it can inform the user what's being sent. * Added a delegate method called pathToRelaunchForUpdater: which can be used for plugins to provide the path which should be used when relaunching the client after installing an update. * Added support for xml:lang to pick localized nodes in appcasts (for release notes, etc). * Fixed a bug which would cause the "checking for updates" window to not disappear in certain extraordinary error conditions. * Fixed a DSA signature checking bug for .tar.gz archives. * Sparkle now refuses to update on any read-only volume, not just dmgs. * Sparkle will clean up the host app's name and version before sending it as the user agent string; some non-ASCII characters were causing problems. * Added an Italian localization courtesy Michele Longhi. * Added a Swedish localization courtesy Daniel Bergman. * Fixes to the French localization courtesy Ronald Leroux and Yann Ricqueberg. * Fixes to the German localization courtesy Sven-S. Porst. * Fixes to the Russian localization courtesy Alexander Bykov and Anton Sotkov. * Fixed a number of issues related to archive format detection: I reverted back to extensions from UTIs. * Focus behavior fixes for LSUIElement apps. * The status window progress bar now animates even when indeterminate. * Major refactorings to improve functionality for non-app bundles. # 1.5.0-beta4 * Fixed a critical bug which prevented non-.dmgs from unarchiving properly. * Added reporting of 64-bit capability to the profiling system. # 1.5.0-beta3 * Added a new delegate method to SUUpdater.h to allow delegates to specify custom version comparators. * Added a German localization, courtesy the Camino localizer team: Dominik Tobschall, Tobias Stohr, and Friedemann Bochow. * Bug fixes: * Fixed a serious bug which could cause a server to be DDoS'd (or the host app to crash!) if an appcast fails to be parsed. * Fixed .tbz extraction if the archive was made with Stuffit. * Fixed support for .tar.bz2 and .tar.gz; Sparkle has to assume the archive is a tar when it sees "bz2" and "gz"; don't use those without tarring. * Fixed a typo which caused the shouldPromptForPermissionToCheckForUpdatesToHostBundle: method to not work in 1.5b2. * Fixed .zip extraction on Tiger (Apple changed the UTI between releases) * Fixed a crasher on Tiger. * Fixed display of the default app icon when the host app doesn't have an icon. * Sparkle now displays a sensible progress string and uses an indeterminate progress bar when the server doesn't report a file size. * Fixed some memory leaks. # 1.5.0-beta2 * Compatibility Issues: * Most of the delegate method selectors have changed to scale better. See SUUpdater.h for changes; you'll likely have to make changes if you implement any delegate methods. * If you're using .tar.gz or .tar.bz2 archives, name them ".tbz" or ".tgz" instead; Sparkle now uses UTIs for archive detection, and it's not smart about double extensions. * I'm no longer supporting 10.3. This may or may not work on Panther—probably not. * Sparkle's no longer built for ppc64 by default. If you want to ship that, feel free to build your own, but this saves a few hundred k. * Enhancements: * Sparkle now detects if the preferences for automatic update checks or the time interval change mid-cycle. * If your product is a non-.app, you need to clue Sparkle in on the change by calling [[SUUpdater sharedUpdater] updatePreferencesChanged]. * Added a cancel to the "checking for updates..." dialog. * Sparkle now cleans up all its litter in /tmp. * Made SUUpdater's delegate an IBOutlet so you can hook it up in IB. * Bug fixes: * Sparkle no longer crashes on non-GC hosts when the user cancels an update's downloads. * Sparkle no longer gets stuck in an inconsistent state or crashes when it can't parse the appcast on scheduled updates. * Added the sharedUpdater method to SUUpdater, as it should have been. * Fixed a bug where the "checking for updates..." window wouldn't go away if an error occurs while checking for updates. * Made the dual-mode build configuration actually use the .xcconfig which builds it with GC support. (oops!) * Fixed relaunching for prefpanes. * Sparkle no longer fails to install updates on Snow Leopard (though there's still an issue with trashing the old version of the app, but it seems to be a 10.6 bug) * Sparkle now handles redirects correctly under Tiger. * Fixed the installation path for non-.app bundles. * Fixed a bug which could crash Sparkle under non-English locales. * Fixed a weird race condition which could cause the relaunch tool to never notice that its target relaunched. * Fixed a bug where if the host app is inactive when an update occurs, the update alert sometimes doesn't become key. * Minor textual fixes. * Localizations: * Dutch: Maarten Van Coile * French: Yann Ricquebourg * Spanish: Ernesto Gomez Cereijo # 1.5.0-beta1 * The most important things to know: * The 10.3 support is untested at best; sketchy at worst. Test with it thoroughly before you use it. * Sparkle now asks for permission to update on second launch; don't be surprised at that. You can change that behavior with a delegate method; read SUUpdater.h for more info. * We no longer distinguish between "check on startup" and "scheduled updates"; everything is scheduled, with the default being every day. * The test application is using the new profiling features, but that's only for demonstration: these are off by default. More on this later. * There are no localizations yet. * New features: * Sparkle now supports .pkgs. Just name the .pkg the name of the app and put in the update archive. * Sparkle now sends optional demographic profiling information; set SUEnableSystemProfiling to YES in your Info.plist and check out the GET data sent to your webserver when fetching the appcast. More on this in the documentation. The test application has this on so you can see the behavior. * Sparkle now supports updating non-.apps. Just call -setHostBundle: on the global SUUpdater to let it know what you're trying to update. * Sparkle now supports garbage collection in the host app. Use "Sparkle-with-GC.framework" for that, but be aware it's 10.5-only. * Sparkle is now 64-bit compatible, compiling both ppc64 and x86_64. * Sparkle now supports a sparkle:minimumSystemVersion key you can set on appcast items. It does what you think it does. * Sparkle now checks to see if the host app is running from a disk image and refuses to update if it is. (10.4+ only) * Added support for entities in enclosure paths. * The file size output is now formatted prettily. * Sparkle now gives visual indication that it's checking for updates when the update's user initiated. ie: it pops up a status controller saying "checking for updates..." * Added support for an SUPublicDSAKeyFile, so people don't have to copy/paste their entire key into their Info.plist. Set this key in your Info.plist to the filename of the key in your Resources directory. * Added an actually maintainable codebase. * Changes: * Sparkle version comparison is now dramatically less stupid and verified by a bunch of unit tests. If something doesn't work the way you think it should, add a test to SUVersionComparisonTest.m * Added a minimum to the check interval so that developers don't accidentally release their apps into the wild with 60-second test check intervals and have DOS-attack-like results. It's an hour now for release mode; feel free to change it. * The relaunching process now uses a separate helper app, which is a much more robust method. * Changed CFBundleShortVersionString behavior: Sparkle no longer uses Apple's about box style of displaying ShortVersionString (CFBundleVersion) when the latter is available. * No more MD5 checking. Use DSA: it's actually secure. * The abomination that was SUStatusChecker is dead. Use SUProbingUpdateDriver instead. * Bugfixes: * Fixed a huge bug with fully-automatic updating: before, if the user chose to relaunch later, the app would be running from the trash for a while. Now the buttons are "install and relaunch" or "install later." * Sparkle forces Spotlight to reindex the updated app so that it won't keep pointing to the one in the trash. * Sparkle trims whitespace from around DSA signatures; this could cause crashes before. * Fixed a bug where the user choosing to skip a version would inhibit future automatic updates until the next launch. * Fixed a bug that could occur when the app has a localized CFBundleName. * .dmgs now work on Leopard. * The status controller's button now sizes appropriately to the localization. * Sparkle now works correctly with LSUIElement apps: it focuses them before displaying the update alert. * Sparkle now deletes failed partial downloads. * The update alert no longer floats above everything in the app. * Fixed varied and sundry memory leaks. * A ton of other things that I've forgotten or were too small to mention! # 1.1 * Optimized framework size: now only 1.4mb with all localizations and 384kb with only English (an English-only version is in the Extras folder). * Added a new SUStatusChecker class for programmatically determining if a new version is available (see the docs); thanks, Evan Schoenberg! * Added support for apps using SIGCHLD; thanks, Augie Fackler! * Added a zh_CN update from JT Lee * Added a Polish update from Piotr Chylinski * Fixed DMG support for images with /Applications symlinks. * Fixed a really stupid interval-checking bug that could cause repeated hits to the appcast. * Fixed a bug where the check interval would be inconsistent if a value of 0 was stored in the user defaults. # 1.0 * Additions: * Added real version comparison courtesy Kevin Ballard: Sparkle now knows that 0.89 < 1.0a3 < 1.0. * Added many localizations courtesy David Kocher's localization team. * Added a much better installation mechanism courtesy Allan Odgaard. * Added a user agent string to the RSS fetch request. * Added support for CFBundleShortVersionString in addition to CFBundleVersion, and support for a sparkle:shortVersionString attribute on the enclosure. * Added support for CFBundleDisplayName if available. * Changes: * Automatic updating is now allowed by default, but only if DSA signing is on. * Pressing Escape or closing the update alert now reminds the user later. * Now when there's a stored check interval, Sparkle doesn't check immediately on startup the first time the app is launched because the user hasn't consented to it yet. * The update alert now remembers its size and floats. * Bug Fixes: * Fixed installation of DMGs with multiple files enclosed. * Fixed a nasty memory leak. * Fixed a bug wherein having no value for allowing automatic updates would display a checkbox for the updates but would not honor it. * Fixed a bug in zip extraction that occurred in Panther. * Fixed release notes caching. * Fixed a bug wherein Sparkle refused to authenticate the installation if the user had cancelled authentication previously in that session. * Fixed a weird bug that would cause a second help menu to appear on first launch. * Fixed a bug that could occur when changing the scheduled check interval. * Fixed a bug wherein the host app could crash if the user clicked Remind Me Later before the release notes finished loading. * Fixed a bug wherein the behavior was undefined if the user manually initiated a check when an automatic one was already taking place. * Fixed wrapping on the description field in the update alert. # 1.0-beta3 * Fixed a nasty crasher that occurred often when the user was not connected to the internet. # 1.0-beta2 * Major Improvements: * Fully automatic updating! (see the Documentation: this is beta and off by default) * Added support for DSA signatures (see the Documentation). * Added support for MD5 sum verification. * Added Security.framework-based authentication for installing to privileged directories. * Huge refactoring of the codebase: there's now a Sparkle Xcode project, Sparkle is now a framework, and everything is modular / abstracted. And no more code-generated interface. * Minor Improvements: * A SUUpdaterWillRestartNotification is sent out before restarting now. * Added key equivalents to alert panel buttons. * Error handling is much prettier now: technical messages are not presented to the user anymore. * There's now a test app for developers to see what Sparkle's like before using it. * Wrote new, pretty, extremely thorough documentation. * Bug Fixes: * Relaunch behavior is much improved and shouldn't fail in huge apps anymore. * Fixed a bug wherein a failing tar command could crash the host app. * Sparkle now looks at InfoPlist.strings in addition to Info.plist. * Fixed some stupid typos. # 1.0-beta1 * Major New Features: * Sparkle now supports scheduled periodic updates—read the Readme for information on how to use it. * Sparkle now supports WebKit-based release notes (for CSS and full HTML), which it displays in the main update alert, not a separate panel. The Readme has much more information. Sparkle will, of course, fall back on NSTextView if the host app does not include WebKit. * Minor New Features: * Added support for .zip update archives. * Added support for .dmg update archives. * Implemented Remind Me Later to replace simple update cancellation. * Implemented Skip This Version functionality. * Added support for multiple feeds via the user defaults SUFeedURL key taking precedent over the one in Info.plist. * Added support for Sparkle's custom XML namespace, which is optional but may prove useful. See the Readme for more information. * Bug Fixes: * Sparkle will no longer enter an inconsistent state if the user tries to update again while one is already in progress. * Sparkle now uses CFBundleName to determine the application's name instead of the app's filename. * Sparkle no longer crashes if the user cancels during extraction. * Lots of code refactoring. # 0.1 * Initial Release ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigBinaryDelta.xcconfig ================================================ // BinaryDelta tool only ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigBinaryDeltaDebug.xcconfig ================================================ #include "ConfigBinaryDelta.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigBinaryDeltaRelease.xcconfig ================================================ #include "ConfigBinaryDelta.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigCommon.xcconfig ================================================ // Common SPARKLE_BUNDLE_IDENTIFIER = org.sparkle-project.Sparkle SPARKLE_RELAUNCH_TOOL_NAME = Autoupdate SPARKLE_FILEOP_TOOL_NAME = fileop SPARKLE_APPEND_VERSION_NUMBER = 1 // Sparkle usually doesn't allow downgrades as they're usually accidental, but // if your app has a downgrade function or URL handler, turn this on SPARKLE_AUTOMATED_DOWNGRADES = 0 // If your app file on disk is named "MyApp 1.1b4", Sparkle usually updates it // in place, giving you an app named 1.1b4 that is actually 1.2. Turn the // following on to always reset the name back to "MyApp": SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME = 0 SPARKLE_VERSION_MAJOR = 1 SPARKLE_VERSION_MINOR = 14 SPARKLE_VERSION_PATCH = 0 SPARKLE_VERSION = $(SPARKLE_VERSION_MAJOR).$(SPARKLE_VERSION_MINOR).$(SPARKLE_VERSION_PATCH) CURRENT_PROJECT_VERSION = $(SPARKLE_VERSION) ALWAYS_SEARCH_USER_PATHS = NO ENABLE_STRICT_OBJC_MSGSEND = YES GCC_PRECOMPILE_PREFIX_HEADER = YES GCC_PREFIX_HEADER = Sparkle/Sparkle.pch GCC_SYMBOLS_PRIVATE_EXTERN = YES GCC_INLINES_ARE_PRIVATE_EXTERN = YES ARCHS = $(ARCHS_STANDARD) MACOSX_DEPLOYMENT_TARGET = 10.7 PRODUCT_NAME = ${TARGET_NAME} PRODUCT_BUNDLE_IDENTIFIER = org.sparkle-project.Sparkle.${PRODUCT_NAME:rfc1034identifier} GCC_PREPROCESSOR_DEFINITIONS_COMMON = SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME=$(SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME) SPARKLE_AUTOMATED_DOWNGRADES=$(SPARKLE_AUTOMATED_DOWNGRADES) SPARKLE_APPEND_VERSION_NUMBER=$(SPARKLE_APPEND_VERSION_NUMBER) SPARKLE_BUNDLE_IDENTIFIER=\"$(SPARKLE_BUNDLE_IDENTIFIER)\" SPARKLE_RELAUNCH_TOOL_NAME=\"$(SPARKLE_RELAUNCH_TOOL_NAME)\" SPARKLE_FILEOP_TOOL_NAME=\"$(SPARKLE_FILEOP_TOOL_NAME)\" CLANG_ENABLE_OBJC_ARC = YES GCC_ENABLE_PASCAL_STRINGS = NO // Enable warnings CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES CLANG_ENABLE_MODULES = YES CLANG_WARN__DUPLICATE_METHOD_MATCH = YES CLANG_WARN_ASSIGN_ENUM = YES CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES CLANG_WARN_DOCUMENTATION_COMMENTS = YES CLANG_WARN_EMPTY_BODY = YES CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES CLANG_WARN_UNREACHABLE_CODE = YES GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES GCC_WARN_64_TO_32_BIT_CONVERSION = YES GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES GCC_WARN_ABOUT_MISSING_NEWLINE = YES GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES GCC_WARN_ABOUT_RETURN_TYPE = YES GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES GCC_WARN_PEDANTIC = YES GCC_WARN_SHADOW = YES GCC_WARN_SIGN_COMPARE = YES GCC_WARN_STRICT_SELECTOR_MATCH = YES GCC_WARN_UNDECLARED_SELECTOR = YES GCC_WARN_UNKNOWN_PRAGMAS = YES GCC_WARN_UNUSED_FUNCTION = YES GCC_WARN_UNUSED_LABEL = YES GCC_WARN_UNUSED_PARAMETER = YES GCC_WARN_UNUSED_VARIABLE = YES // Fixes a strange issue where the compiler thinks references to some enums are ambiguous CLANG_ENABLE_MODULES[sdk=macosx10.7] = NO CLANG_ENABLE_MODULES[sdk=macosx10.8] = NO // These should be removed once the conversion to ARC is complete WARNING_CFLAGS_EXTRA = -Wno-custom-atomic-properties -Wno-implicit-atomic-properties // Turn on all warnings, then disable a few which are almost impossible to avoid WARNING_CFLAGS = -Wall -Weverything -Wno-unused-macros -Wno-gnu-statement-expression -Wno-arc-repeated-use-of-weak -Wno-auto-import $(WARNING_CFLAGS_EXTRA) ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigCommonCoverage.xcconfig ================================================ #include "ConfigCommonDebug.xcconfig" GCC_GENERATE_TEST_COVERAGE_FILES = YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigCommonDebug.xcconfig ================================================ #include "ConfigCommon.xcconfig" // Debug only GCC_OPTIMIZATION_LEVEL = 0 SWIFT_OPTIMIZATION_LEVEL = -Onone DEBUG_INFORMATION_FORMAT = dwarf ENABLE_TESTABILITY = YES GCC_GENERATE_DEBUGGING_SYMBOLS = YES GCC_PREPROCESSOR_DEFINITIONS = $(GCC_PREPROCESSOR_DEFINITIONS_COMMON) DEBUG=1 ONLY_ACTIVE_ARCH = YES COPY_PHASE_STRIP = NO ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigCommonRelease.xcconfig ================================================ #include "ConfigCommon.xcconfig" // Release only GCC_OPTIMIZATION_LEVEL = s SWIFT_OPTIMIZATION_LEVEL = -Ofast DEBUG_INFORMATION_FORMAT = dwarf-with-dsym GCC_GENERATE_DEBUGGING_SYMBOLS = YES GCC_PREPROCESSOR_DEFINITIONS = $(GCC_PREPROCESSOR_DEFINITIONS_COMMON) DEAD_CODE_STRIPPING = YES GCC_TREAT_WARNINGS_AS_ERRORS = NO GCC_WARN_UNINITIALIZED_AUTOS = YES ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigFileop.xcconfig ================================================ // Fileop tool only PRODUCT_NAME = $(SPARKLE_FILEOP_TOOL_NAME) SKIP_INSTALL = YES CLANG_ENABLE_MODULES = NO ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigFramework.xcconfig ================================================ // Framework only DYLIB_INSTALL_NAME_BASE = @rpath DYLIB_COMPATIBILITY_VERSION = 1.6 DYLIB_CURRENT_VERSION = $(SPARKLE_VERSION_MAJOR).$(SPARKLE_VERSION_MINOR).$(SPARKLE_VERSION_PATCH) WRAPPER_EXTENSION = framework FRAMEWORK_VERSION = A INFOPLIST_FILE = Sparkle/Sparkle-Info.plist GCC_PREPROCESSOR_DEFINITIONS = $(inherited) BUILDING_SPARKLE=1 OTHER_LDFLAGS = -Wl,-U,_NSURLQuarantinePropertiesKey SKIP_INSTALL = YES DEFINES_MODULE = YES PRODUCT_BUNDLE_IDENTIFIER = ${SPARKLE_BUNDLE_IDENTIFIER} ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigFrameworkDebug.xcconfig ================================================ #include "ConfigFramework.xcconfig" // Unit tests need access to non-public classes GCC_SYMBOLS_PRIVATE_EXTERN = NO ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigFrameworkRelease.xcconfig ================================================ #include "ConfigFramework.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigRelaunch.xcconfig ================================================ // Relaunch Tool only INFOPLIST_FILE = Sparkle/Autoupdate/Autoupdate-Info.plist PRODUCT_NAME = $(SPARKLE_RELAUNCH_TOOL_NAME) SKIP_INSTALL = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon CLANG_ENABLE_MODULES = NO OTHER_LDFLAGS = -Wl,-U,_NSURLQuarantinePropertiesKey ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigRelaunchDebug.xcconfig ================================================ #include "ConfigRelaunch.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigRelaunchRelease.xcconfig ================================================ #include "ConfigRelaunch.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigTestApp.xcconfig ================================================ // Test Application only INFOPLIST_FILE = TestApplication/TestApplication-Info.plist WRAPPER_EXTENSION = app ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon LD_RUNPATH_SEARCH_PATHS = @executable_path/../Frameworks PRODUCT_BUNDLE_IDENTIFIER = org.sparkle-project.SparkleTestApp ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigTestAppDebug.xcconfig ================================================ #include "ConfigTestApp.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigTestAppRelease.xcconfig ================================================ #include "ConfigTestApp.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUITest.xcconfig ================================================ // UI Test only INFOPLIST_FILE = UITests/UITests-Info.plist WRAPPER_EXTENSION = xctest MACOSX_DEPLOYMENT_TARGET = 10.11 FRAMEWORK_SEARCH_PATHS = $(inherited) $(DEVELOPER_FRAMEWORKS_DIR) LD_RUNPATH_SEARCH_PATHS = @loader_path/../Frameworks TEST_TARGET_NAME = Sparkle Test App USES_XCTRUNNER = YES ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUITestCoverage.xcconfig ================================================ #include "ConfigUITest.xcconfig" GCC_GENERATE_TEST_COVERAGE_FILES = NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUITestDebug.xcconfig ================================================ #include "ConfigUITest.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUITestRelease.xcconfig ================================================ #include "ConfigUITest.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUnitTest.xcconfig ================================================ // Unit Test only INFOPLIST_FILE = Tests/SparkleTests-Info.plist WRAPPER_EXTENSION = xctest OTHER_CFLAGS = $(inherited) -iframework"$(DEVELOPER_FRAMEWORKS_DIR)" -iframework"$(PLATFORM_DIR)/Developer/Library/Frameworks" GCC_SYMBOLS_PRIVATE_EXTERN = NO WARNING_CFLAGS = $(inherited) -Wno-variadic-macros -Wno-gnu-zero-variadic-macro-arguments FRAMEWORK_SEARCH_PATHS = $(inherited) $(DEVELOPER_FRAMEWORKS_DIR) LD_RUNPATH_SEARCH_PATHS = @loader_path/../Frameworks MACOSX_DEPLOYMENT_TARGET = 10.9 CLANG_ENABLE_MODULES = YES SWIFT_OBJC_BRIDGING_HEADER = Tests/Sparkle Unit Tests-Bridging-Header.h ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUnitTestCoverage.xcconfig ================================================ #include "ConfigUnitTest.xcconfig" GCC_GENERATE_TEST_COVERAGE_FILES = NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUnitTestDebug.xcconfig ================================================ #include "ConfigUnitTest.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/ConfigUnitTestRelease.xcconfig ================================================ #include "ConfigUnitTest.xcconfig" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/make-release-package.sh ================================================ #!/bin/bash set -e if [ "$ACTION" = "" ] ; then # Sanity check that the Podspec version matches the Sparkle version spec_version=$(printf "require 'cocoapods'\nspec = %s\nprint spec.version" "$(cat "$SRCROOT/Sparkle.podspec")" | LANG=en_US.UTF-8 ruby) if [ "$spec_version" != "$CURRENT_PROJECT_VERSION" ] ; then echo "podspec version '$spec_version' does not match the current project version '$CURRENT_PROJECT_VERSION'" >&2 exit 1 fi rm -rf "$CONFIGURATION_BUILD_DIR/staging" rm -f "Sparkle-$CURRENT_PROJECT_VERSION.tar.bz2" mkdir -p "$CONFIGURATION_BUILD_DIR/staging" cp "$SRCROOT/CHANGELOG" "$SRCROOT/LICENSE" "$SRCROOT/Resources/SampleAppcast.xml" "$CONFIGURATION_BUILD_DIR/staging" cp -R "$SRCROOT/bin" "$CONFIGURATION_BUILD_DIR/staging" cp "$CONFIGURATION_BUILD_DIR/BinaryDelta" "$CONFIGURATION_BUILD_DIR/staging/bin" cp -R "$CONFIGURATION_BUILD_DIR/Sparkle Test App.app" "$CONFIGURATION_BUILD_DIR/staging" cp -R "$CONFIGURATION_BUILD_DIR/Sparkle.framework" "$CONFIGURATION_BUILD_DIR/staging" # Only copy dSYMs for Release builds, but don't check for the presence of the actual files # because missing dSYMs in a release build SHOULD trigger a build failure if [ "$CONFIGURATION" = "Release" ] ; then cp -R "$CONFIGURATION_BUILD_DIR/BinaryDelta.dSYM" "$CONFIGURATION_BUILD_DIR/staging/bin" cp -R "$CONFIGURATION_BUILD_DIR/Sparkle Test App.app.dSYM" "$CONFIGURATION_BUILD_DIR/staging" cp -R "$CONFIGURATION_BUILD_DIR/Sparkle.framework.dSYM" "$CONFIGURATION_BUILD_DIR/staging" fi cd "$CONFIGURATION_BUILD_DIR/staging" # Sorted file list groups similar files together, which improves tar compression find . \! -type d | rev | sort | rev | tar cjvf "../Sparkle-$CURRENT_PROJECT_VERSION.tar.bz2" --files-from=- rm -rf "$CONFIGURATION_BUILD_DIR/staging" fi ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Configurations/set-git-version-info.sh ================================================ #!/bin/sh set -e if ! which -s git ; then exit 0 fi if [ -z "$SRCROOT" ] || \ [ -z "$BUILT_PRODUCTS_DIR" ] || \ [ -z "$INFOPLIST_PATH" ] || \ [ -z "$CURRENT_PROJECT_VERSION" ]; then echo "$0: Must be run from Xcode!" 1>&2 exit 1 fi # Get the current Git master hash version=$(cd "$SRCROOT" ; git show-ref --abbrev heads/master | awk '{print $1}') if [ -z "$version" ] ; then echo "$0: Can't find a Git hash!" 1>&2 exit 0 fi version="$CURRENT_PROJECT_VERSION git-$version" # and use it to set the CFBundleShortVersionString value export PATH="$PATH:/usr/libexec" PlistBuddy -c "Set :CFBundleShortVersionString '$version'" \ "$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Documentation/.gitignore ================================================ html ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Documentation/Doxyfile ================================================ PROJECT_NAME = Sparkle PROJECT_NUMBER = 1.14.0 PROJECT_BRIEF = "A software update framework for macOS" PROJECT_LOGO = Resources/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png RECURSIVE = YES INPUT = Sparkle OUTPUT_DIRECTORY = Documentation GENERATE_HTML = YES GENERATE_LATEX = NO GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST = YES GENERATE_TODOLIST = YES ALPHABETICAL_INDEX = YES QT_AUTOBRIEF = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Documentation/build-docs.sh ================================================ #!/bin/bash if [ "$ACTION" = "" ] ; then if which -s doxygen ; then doxygen Documentation/Doxyfile else echo "warning: Doxygen not found in PATH" fi elif [ "$ACTION" = "clean" ] ; then rm -rf "$SRCROOT/Documentation/html" fi ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/LICENSE ================================================ Copyright (c) 2006-2013 Andy Matuschak. Copyright (c) 2009-2013 Elgato Systems GmbH. Copyright (c) 2011-2014 Kornel Lesiński. Copyright (c) 2014 C.W. Betts. Copyright (c) 2014 Petroules Corporation. Copyright (c) 2014 Big Nerd Ranch. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================= EXTERNAL LICENSES ================= bspatch.c and bsdiff.c, from bsdiff 4.3 : Copyright (c) 2003-2005 Colin Percival. sais.c and sais.c, from sais-lite (2010/08/07) : Copyright (c) 2008-2010 Yuta Mori. SUDSAVerifier.m: Copyright (c) 2011 Mark Hamlin. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted providing that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Makefile ================================================ .PHONY: all localizable-strings release build test ci ifndef BUILDDIR BUILDDIR := $(shell mktemp -d "$(TMPDIR)/Sparkle.XXXXXX") endif localizable-strings: rm -f Sparkle/en.lproj/Sparkle.strings genstrings -o Sparkle/en.lproj -s SULocalizedString Sparkle/*.m Sparkle/*.h iconv -f UTF-16 -t UTF-8 < Sparkle/en.lproj/Localizable.strings > Sparkle/en.lproj/Sparkle.strings rm Sparkle/en.lproj/Localizable.strings release: xcodebuild -scheme Distribution -configuration Release -derivedDataPath "$(BUILDDIR)" build open -R "$(BUILDDIR)/Build/Products/Release/Sparkle-"*.tar.bz2 cat Sparkle.podspec @echo "Don't forget to update CocoaPods!" build: xcodebuild clean build test: xcodebuild -scheme Distribution -configuration Debug test uitest: xcodebuild -scheme UITests -configuration Debug test ci: for i in {7..9} ; do \ if xcrun --sdk "macosx10.$$i" --show-sdk-path 2> /dev/null ; then \ ( rm -rf build && xcodebuild -sdk "macosx10.$$i" -scheme Distribution -configuration Coverage -derivedDataPath build ) || exit 1 ; \ fi ; \ done for i in {10..11} ; do \ if xcrun --sdk "macosx10.$$i" --show-sdk-path 2> /dev/null ; then \ ( rm -rf build && xcodebuild -sdk "macosx10.$$i" -scheme Distribution -configuration Coverage -derivedDataPath build test ) || exit 1 ; \ fi ; \ done check-localizations: ./Sparkle/CheckLocalizations.swift -root . -htmlPath "$(TMPDIR)/LocalizationsReport.htm" open "$(TMPDIR)/LocalizationsReport.htm" ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/README.markdown ================================================ # Sparkle [![Build Status](https://travis-ci.org/sparkle-project/Sparkle.svg?branch=master)](https://travis-ci.org/sparkle-project/Sparkle) [![Coverage Status](https://coveralls.io/repos/sparkle-project/Sparkle/badge.svg?branch=master&service=github)](https://coveralls.io/github/sparkle-project/Sparkle?branch=master) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods](https://img.shields.io/cocoapods/v/Sparkle.svg?maxAge=2592000)]() sponsored by: MaxCDN An easy-to-use software update framework for Cocoa developers. Sparkle shows familiar update window with release notes ## Changes since 1.5b * Up-to-date with 10.11 SDK and Xcode 7. Supports macOS 10.7+. * Cleaned up and modernized code, using ARC and Autolayout. * Merged bugfixes, security fixes and some features from multiple Sparkle forks. * Truly automatic background updates (no UI at all) when user agreed to "Automatically download and install updates in the future." * Ability to mark updates as critical. * Progress and status notifications for the host app. * Name of finish_installation.app can be configured to match your app's name. * Upgraded and more reliable binary delta and code signing verification. ## Features * True self-updating—the user can choose to automatically download and install all updates. * Displays a detailed progress window to the user. * Supports authentication for installing in secure locations. * Supports Apple Code Signing and DSA signatures for ultra-secure updates. * Easy to install. Sparkle requires no code in your app, so it's trivial to upgrade or remove the framework. * Uses appcasts for release information. Appcasts are supported by 3rd party update-tracking programs and websites. * Displays release notes to the user via WebKit. * Sparkle doesn't bug the user until second launch for better first impressions. * Seamless integration—there's no mention of Sparkle; your icons and app name are used. * Deep delegate support to make Sparkle work exactly as you need. * Optionally sends system information to the server when checking for updates. * Supports bundles, preference panes, plugins, and other non-.app software. Can install .pkg files for more complicated products. * Supports branches due to minimum OS version requirements. ## Requirements * Runtime: macOS 10.7 or greater * Build: Xcode 5 and 10.8 SDK or greater * HTTPS server for serving updates (see [App Transport Security](http://sparkle-project.org/documentation/app-transport-security/)) ## API Sparkle is built with `-fvisibility=hidden -fvisibility-inlines-hidden` which means no symbols are exported by default. If you are adding a symbol to the public API you must decorate the declaration with the `SU_EXPORT` macro (grep the source code for examples). ## Building the distribution package `cd` to the root of the Sparkle source tree and run `make release`. Sparkle-*VERSION*.tar.bz2 will be created in a temporary directory and revealed in Finder after the build has completed. Alternatively, build the Distribution scheme in the Xcode UI. ## Project Sponsor [MaxCDN](https://www.maxcdn.com/?utm_source=sparkle-github&utm_medium=link&utm_campaign=readme-footer) ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Resources/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "16x16", "idiom" : "mac", "filename" : "icon_16x16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", "filename" : "icon_16x16@2x.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "icon_32x32.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "icon_32x32@2x.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "icon_128x128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "icon_128x128@2x.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "icon_256x256.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "icon_256x256@2x.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "icon_512x512.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "icon_512x512@2x.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Resources/SUModelTranslation.plist ================================================ ADP2,1 Developer Transition Kit iMac1,1 iMac G3 (Rev A-D) iMac4,1 iMac (Core Duo) iMac4,2 iMac for Education (17 inch, Core Duo) iMac5,1 iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) iMac5,2 iMac (Core 2 Duo, 17 inch, Combo Drive) iMac6,1 iMac (Core 2 Duo, 24 inch, SuperDrive) iMac7,1 iMac Intel Core 2 Duo (aluminum enclosure) iMac8,1 iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) iMac9,1 iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 ) iMac10,1 iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 ) iMac11,1 iMac (Core i5 or i7, 27 inch Late 2009) iMac11,2 21.5" iMac (mid 2010) iMac11,3 iMac (Core i5 or i7, 27 inch Mid 2010) iMac12,1 iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011) iMac12,2 iMac (Core i5 or i7, 27 inch Mid 2011) iMac13,1 iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013) iMac13,2 iMac (Core i5 or i7, 27 inch Late 2012) iMac14,1 iMac (Core i5, 21.5 inch Late 2013) iMac14,2 iMac (Core i5 or i7, 27 inch Late 2013) iMac14,3 iMac (Core i5 or i7, 21.5 inch Late 2013) iMac14,4 iMac (Core i5, 21.5 inch Mid 2014) iMac15,1 iMac (Retina 5K Core i5 or i7, 27 inch Late 2014 or Mid 2015) iMac16,1 iMac (Core i5, 21,5 inch Late 2015) iMac16,2 iMac (Retina 4K Core i5 or i7, 21.5 inch Late 2015) iMac17,1 iMac (Retina 5K Core i5 or i7, 27 inch Late 2015) MacBook1,1 MacBook (Core Duo) MacBook2,1 MacBook (Core 2 Duo) MacBook4,1 MacBook (Core 2 Duo Feb 2008) MacBook5,1 MacBook (Core 2 Duo, Late 2008, Unibody) MacBook5,2 MacBook (Core 2 Duo, Early 2009, White) MacBook6,1 MacBook (Core 2 Duo, Late 2009, Unibody) MacBook7,1 MacBook (Core 2 Duo, Mid 2010, White) MacBook8,1 MacBook (Core M, 12 inch, Early 2015) MacBookAir1,1 MacBook Air (Core 2 Duo, 13 inch, Early 2008) MacBookAir2,1 MacBook Air (Core 2 Duo, 13 inch, Mid 2009) MacBookAir3,1 MacBook Air (Core 2 Duo, 11 inch, Late 2010) MacBookAir3,2 MacBook Air (Core 2 Duo, 13 inch, Late 2010) MacBookAir4,1 MacBook Air (Core i5 or i7, 11 inch, Mid 2011) MacBookAir4,2 MacBook Air (Core i5 or i7, 13 inch, Mid 2011) MacBookAir5,1 MacBook Air (Core i5 or i7, 11 inch, Mid 2012) MacBookAir5,2 MacBook Air (Core i5 or i7, 13 inch, Mid 2012) MacBookAir6,1 MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) MacBookAir6,2 MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) MacBookAir7,1 MacBook Air (Core i5 or i7, 11 inch, Early 2015) MacBookAir7,2 MacBook Air (Core i5 or i7, 13 inch, Early 2015) MacBookPro1,1 MacBook Pro Core Duo (15-inch) MacBookPro1,2 MacBook Pro Core Duo (17-inch) MacBookPro2,1 MacBook Pro Core 2 Duo (17-inch) MacBookPro2,2 MacBook Pro Core 2 Duo (15-inch) MacBookPro3,1 MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) MacBookPro3,2 MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) MacBookPro4,1 MacBook Pro (Core 2 Duo Feb 2008) MacBookPro5,1 MacBook Pro Intel Core 2 Duo (aluminum unibody) MacBookPro5,2 MacBook Pro Intel Core 2 Duo (aluminum unibody) MacBookPro5,3 MacBook Pro Intel Core 2 Duo (aluminum unibody) MacBookPro5,4 MacBook Pro Intel Core 2 Duo (aluminum unibody) MacBookPro5,5 MacBook Pro Intel Core 2 Duo (aluminum unibody) MacBookPro6,1 MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) MacBookPro6,2 MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) MacBookPro7,1 MacBook Pro Intel Core 2 Duo (mid 2010) MacBookPro8,1 MacBook Pro Intel Core i5, Intel Core i7, 13" (early 2011) MacBookPro8,2 MacBook Pro Intel Core i7, 15" (early 2011) MacBookPro8,3 MacBook Pro Intel Core i7, 17" (early 2011) MacBookPro9,1 MacBook Pro (15-inch, Mid 2012) MacBookPro9,2 MacBook Pro (13-inch, Mid 2012) MacBookPro10,1 MacBook Pro (Retina, Mid 2012) MacBookPro10,2 MacBook Pro (Retina, 13-inch, Late 2012) MacBookPro11,1 MacBook Pro (Retina, 13-inch, Late 2013) MacBookPro11,2 MacBook Pro (Retina, 15-inch, Late 2013) MacBookPro11,3 MacBook Pro (Retina, 15-inch, Late 2013) MacbookPro11,4 MacBook Pro (Retina, 15-inch, Mid 2015) MacbookPro11,5 MacBook Pro (Retina, 15-inch, Mid 2015) MacbookPro12,1  MacBook Pro (Retina, 13-inch, Early 2015) Macmini1,1 Mac Mini (Core Solo/Duo) Macmini2,1 Mac mini Intel Core Macmini3,1 Mac mini Intel Core Macmini4,1 Mac mini Intel Core (Mid 2010) Macmini5,1 Mac mini (Core i5, Mid 2011) Macmini5,2 Mac mini (Core i5 or Core i7, Mid 2011) Macmini5,3 Mac mini (Core i7, Server, Mid 2011) Macmini6,1 Mac mini (Core i5, Late 2012) Macmini6,2 Mac mini (Core i7, Normal or Server, Late 2012) Macmini7,1 Mac mini (Core i5 or Core i7, Late 2014) MacPro1,1,Quad Mac Pro MacPro1,1 Mac Pro (four-core) MacPro2,1 Mac Pro (eight-core) MacPro3,1 Mac Pro (January 2008 4- or 8- core "Harpertown") MacPro4,1 Mac Pro (March 2009) MacPro5,1 Mac Pro (2010 or 2012) MacPro6,1 Mac Pro (Late 2013) PowerBook1,1 PowerBook G3 PowerBook2,1 iBook G3 PowerBook2,2 iBook G3 (FireWire) PowerBook2,3 iBook G3 PowerBook2,4 iBook G3 PowerBook3,1 PowerBook G3 (FireWire) PowerBook3,2 PowerBook G4 PowerBook3,3 PowerBook G4 (Gigabit Ethernet) PowerBook3,4 PowerBook G4 (DVI) PowerBook3,5 PowerBook G4 (1GHz / 867MHz) PowerBook4,1 iBook G3 (Dual USB, Late 2001) PowerBook4,2 iBook G3 (16MB VRAM) PowerBook4,3 iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) PowerBook5,1 PowerBook G4 (17 inch) PowerBook5,2 PowerBook G4 (15 inch FW 800) PowerBook5,3 PowerBook G4 (17-inch 1.33GHz) PowerBook5,4 PowerBook G4 (15 inch 1.5/1.33GHz) PowerBook5,5 PowerBook G4 (17-inch 1.5GHz) PowerBook5,6 PowerBook G4 (15 inch 1.67GHz/1.5GHz) PowerBook5,7 PowerBook G4 (17-inch 1.67GHz) PowerBook5,8 PowerBook G4 (Double layer SD, 15 inch) PowerBook5,9 PowerBook G4 (Double layer SD, 17 inch) PowerBook6,1 PowerBook G4 (12 inch) PowerBook6,2 PowerBook G4 (12 inch, DVI) PowerBook6,3 iBook G4 PowerBook6,4 PowerBook G4 (12 inch 1.33GHz) PowerBook6,5 iBook G4 (Early-Late 2004) PowerBook6,7 iBook G4 (Mid 2005) PowerBook6,8 PowerBook G4 (12 inch 1.5GHz) PowerMac1,1 Power Macintosh G3 (Blue & White) PowerMac1,2 Power Macintosh G4 (PCI Graphics) PowerMac2,1 iMac G3 (Slot-loading CD-ROM) PowerMac2,2 iMac G3 (Summer 2000) PowerMac3,1 Power Macintosh G4 (AGP Graphics) PowerMac3,2 Power Macintosh G4 (AGP Graphics) PowerMac3,3 Power Macintosh G4 (Gigabit Ethernet) PowerMac3,4 Power Macintosh G4 (Digital Audio) PowerMac3,5 Power Macintosh G4 (Quick Silver) PowerMac3,6 Power Macintosh G4 (Mirrored Drive Door) PowerMac4,1 iMac G3 (Early/Summer 2001) PowerMac4,2 iMac G4 (Flat Panel) PowerMac4,4 eMac PowerMac4,5 iMac G4 (17-inch Flat Panel) PowerMac5,1 Power Macintosh G4 Cube PowerMac5,2 Power Mac G4 Cube PowerMac6,1 iMac G4 (USB 2.0) PowerMac6,3 iMac G4 (20-inch Flat Panel) PowerMac6,4 eMac (USB 2.0, 2005) PowerMac7,2 Power Macintosh G5 PowerMac7,3 Power Macintosh G5 PowerMac8,1 iMac G5 PowerMac8,2 iMac G5 (Ambient Light Sensor) PowerMac9,1 Power Macintosh G5 (Late 2005) PowerMac10,1 Mac Mini G4 PowerMac10,2 Mac Mini (Late 2005) PowerMac11,2 Power Macintosh G5 (Late 2005) PowerMac12,1 iMac G5 (iSight) RackMac1,1 Xserve G4 RackMac1,2 Xserve G4 (slot-loading, cluster node) RackMac3,1 Xserve G5 Xserve1,1 Xserve (Intel Xeon) Xserve2,1 Xserve (January 2008 quad-core) Xserve3,1 Xserve (early 2009) ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Resources/SampleAppcast.xml ================================================ Your Great App's Changelog http://you.com/app/appcast.xml Most recent changes with links to updates. en Version 2.0 (2 bugs fixed; 3 new features) http://you.com/app/2.0.html Wed, 09 Jan 2006 19:20:11 +0000 10.7 Version 1.5 (8 bugs fixed; 2 new features) http://you.com/app/1.5.html Wed, 01 Jan 2006 12:20:11 +0000 10.7 Version 1.4 (5 bugs fixed; 2 new features) http://you.com/app/1.4.html Wed, 25 Dec 2005 12:20:11 +0000 10.7 ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/Autoupdate/Autoupdate-Info.plist ================================================ CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString ${CURRENT_PROJECT_VERSION} CFBundleSignature ???? CFBundleVersion ${CURRENT_PROJECT_VERSION} LSBackgroundOnly 1 LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} LSUIElement 1 NSMainNibFile MainMenu NSPrincipalClass NSApplication ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/Autoupdate/Autoupdate.m ================================================ #import #import "SUInstaller.h" #import "SUHost.h" #import "SUStandardVersionComparator.h" #import "SUStatusController.h" #import "SULog.h" #include /*! * Time this app uses to recheck if the parent has already died. */ static const NSTimeInterval SUParentQuitCheckInterval = .25; @interface TerminationListener : NSObject - (instancetype)initWithProcessId:(pid_t)pid; @end @interface TerminationListener () @property (nonatomic, assign) pid_t processIdentifier; @property (nonatomic, strong) NSTimer *watchdogTimer; @end @implementation TerminationListener @synthesize processIdentifier = _processIdentifier; @synthesize watchdogTimer = _watchdogTimer; - (instancetype)initWithProcessId:(pid_t)pid { if (!(self = [super init])) { return nil; } self.processIdentifier = pid; return self; } - (void)cleanupWithCompletion:(void (^)(void))completionBlock { [self.watchdogTimer invalidate]; completionBlock(); } - (void)startListeningWithCompletion:(void (^)(void))completionBlock { BOOL alreadyTerminated = (getppid() == 1); // ppid is launchd (1) => parent terminated already if (alreadyTerminated) [self cleanupWithCompletion:completionBlock]; else self.watchdogTimer = [NSTimer scheduledTimerWithTimeInterval:SUParentQuitCheckInterval target:self selector:@selector(watchdog:) userInfo:completionBlock repeats:YES]; } - (void)watchdog:(NSTimer *)timer { if (![NSRunningApplication runningApplicationWithProcessIdentifier:self.processIdentifier]) { [self cleanupWithCompletion:timer.userInfo]; } } @end /*! * If the Installation takes longer than this time the Application Icon is shown in the Dock so that the user has some feedback. */ static const NSTimeInterval SUInstallationTimeLimit = 5; /*! * Terminate the application after a delay from launching the new update to avoid OS activation issues * This delay should be be high enough to increase the likelihood that our updated app will be launched up front, * but should be low enough so that the user doesn't ponder why the updater hasn't finished terminating yet */ static const NSTimeInterval SUTerminationTimeDelay = 0.5; @interface AppInstaller : NSObject /* * hostPath - path to host (original) application * relaunchPath - path to what the host wants to relaunch (default is same as hostPath) * parentProcessId - process identifier of the host before launching us * updateFolderPath - path to update folder (i.e, temporary directory containing the new update) * shouldRelaunch - indicates if the new installed app should re-launched * shouldShowUI - indicates if we should show the status window when installing the update */ - (instancetype)initWithHostPath:(NSString *)hostPath relaunchPath:(NSString *)relaunchPath parentProcessId:(pid_t)parentProcessId updateFolderPath:(NSString *)updateFolderPath shouldRelaunch:(BOOL)shouldRelaunch shouldShowUI:(BOOL)shouldShowUI; @end @interface AppInstaller () @property (nonatomic, strong) TerminationListener *terminationListener; @property (nonatomic, strong) SUStatusController *statusController; @property (nonatomic, copy) NSString *updateFolderPath; @property (nonatomic, copy) NSString *hostPath; @property (nonatomic, copy) NSString *relaunchPath; @property (nonatomic, assign) BOOL shouldRelaunch; @property (nonatomic, assign) BOOL shouldShowUI; @property (nonatomic, assign) BOOL isTerminating; @end @implementation AppInstaller @synthesize terminationListener = _terminationListener; @synthesize statusController = _statusController; @synthesize updateFolderPath = _updateFolderPath; @synthesize hostPath = _hostPath; @synthesize relaunchPath = _relaunchPath; @synthesize shouldRelaunch = _shouldRelaunch; @synthesize shouldShowUI = _shouldShowUI; @synthesize isTerminating = _isTerminating; - (instancetype)initWithHostPath:(NSString *)hostPath relaunchPath:(NSString *)relaunchPath parentProcessId:(pid_t)parentProcessId updateFolderPath:(NSString *)updateFolderPath shouldRelaunch:(BOOL)shouldRelaunch shouldShowUI:(BOOL)shouldShowUI { if (!(self = [super init])) { return nil; } self.hostPath = hostPath; self.relaunchPath = relaunchPath; self.terminationListener = [[TerminationListener alloc] initWithProcessId:parentProcessId]; self.updateFolderPath = updateFolderPath; self.shouldRelaunch = shouldRelaunch; self.shouldShowUI = shouldShowUI; return self; } - (void)applicationDidFinishLaunching:(NSNotification __unused *)notification { [self.terminationListener startListeningWithCompletion:^{ self.terminationListener = nil; if (self.shouldShowUI) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SUInstallationTimeLimit * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (!self.isTerminating) { // Show app icon in the dock ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); } }); } [self install]; }]; } - (void)install { NSBundle *theBundle = [NSBundle bundleWithPath:self.hostPath]; SUHost *host = [[SUHost alloc] initWithBundle:theBundle]; NSString *installationPath = [[host installationPath] copy]; if (self.shouldShowUI) { self.statusController = [[SUStatusController alloc] initWithHost:host]; [self.statusController setButtonTitle:SULocalizedString(@"Cancel Update", @"") target:nil action:Nil isDefault:NO]; [self.statusController beginActionWithTitle:SULocalizedString(@"Installing update...", @"") maxProgressValue: 0 statusText: @""]; [self.statusController showWindow:self]; } NSString *fileOperationToolPath = [[[[NSBundle mainBundle] executablePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@""SPARKLE_FILEOP_TOOL_NAME]; if (![[NSFileManager defaultManager] fileExistsAtPath:fileOperationToolPath]) { SULog(@"Potential Installation Error: File operation tool path %@ is not found", fileOperationToolPath); } [SUInstaller installFromUpdateFolder:self.updateFolderPath overHost:host installationPath:installationPath fileOperationToolPath:fileOperationToolPath versionComparator:[SUStandardVersionComparator defaultComparator] completionHandler:^(NSError *error) { if (error) { NSError *underlyingError = [error.userInfo objectForKey:NSUnderlyingErrorKey]; if (underlyingError == nil || underlyingError.code != SUInstallationCancelledError) { SULog(@"Installation Error: %@", error); if (self.shouldShowUI) { NSAlert *alert = [[NSAlert alloc] init]; alert.messageText = @""; alert.informativeText = [NSString stringWithFormat:@"%@", [error localizedDescription]]; [alert runModal]; } } exit(EXIT_FAILURE); } else { NSString *pathToRelaunch = nil; // If the installation path differs from the host path, we give higher precedence for it than // if the desired relaunch path differs from the host path if (![installationPath.pathComponents isEqualToArray:self.hostPath.pathComponents] || [self.relaunchPath.pathComponents isEqualToArray:self.hostPath.pathComponents]) { pathToRelaunch = installationPath; } else { pathToRelaunch = self.relaunchPath; } [self cleanupAndTerminateWithPathToRelaunch:pathToRelaunch]; } }]; } - (void)cleanupAndTerminateWithPathToRelaunch:(NSString *)relaunchPath { self.isTerminating = YES; dispatch_block_t cleanupAndExit = ^{ NSError *theError = nil; if (![[NSFileManager defaultManager] removeItemAtPath:self.updateFolderPath error:&theError]) { SULog(@"Couldn't remove update folder: %@.", theError); } [[NSFileManager defaultManager] removeItemAtPath:[[NSBundle mainBundle] bundlePath] error:NULL]; exit(EXIT_SUCCESS); }; if (self.shouldRelaunch) { // The auto updater can terminate before the newly updated app is finished launching // If that happens, the OS may not make the updated app active and frontmost // (Or it does become frontmost, but the OS backgrounds it afterwards.. It's some kind of timing/activation issue that doesn't occur all the time) // The only remedy I've been able to find is waiting an arbitrary delay before exiting our application // Don't use -launchApplication: because we may not be launching an application. Eg: it could be a system prefpane if (![[NSWorkspace sharedWorkspace] openFile:relaunchPath]) { SULog(@"Failed to launch %@", relaunchPath); } [self.statusController close]; // Don't even think about hiding the app icon from the dock if we've already shown it // Transforming the app back to a background one has a backfiring effect, decreasing the likelihood // that the updated app will be brought up front dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SUTerminationTimeDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ cleanupAndExit(); }); } else { cleanupAndExit(); } } @end int main(int __unused argc, const char __unused *argv[]) { @autoreleasepool { NSArray *args = [[NSProcessInfo processInfo] arguments]; if (args.count < 5 || args.count > 7) { return EXIT_FAILURE; } NSApplication *application = [NSApplication sharedApplication]; BOOL shouldShowUI = (args.count > 6) ? [[args objectAtIndex:6] boolValue] : YES; if (shouldShowUI) { [application activateIgnoringOtherApps:YES]; } AppInstaller *appInstaller = [[AppInstaller alloc] initWithHostPath:[args objectAtIndex:1] relaunchPath:[args objectAtIndex:2] parentProcessId:[[args objectAtIndex:3] intValue] updateFolderPath:[args objectAtIndex:4] shouldRelaunch:(args.count > 5) ? [[args objectAtIndex:5] boolValue] : YES shouldShowUI:shouldShowUI]; [application setDelegate:appInstaller]; [application run]; } return EXIT_SUCCESS; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/CheckLocalizations.swift ================================================ #!/usr/bin/xcrun swift import Foundation func die(msg: String) { print("ERROR: \(msg)") exit(1) } extension NSXMLElement { convenience init(name: String, attributes: [String: String], stringValue string: String? = nil) { self.init(name: name, stringValue: string) setAttributesWithDictionary(attributes) } } let ud = NSUserDefaults.standardUserDefaults() let sparkleRoot = ud.objectForKey("root") as? String let htmlPath = ud.objectForKey("htmlPath") as? String if sparkleRoot == nil || htmlPath == nil { die("Missing arguments") } let enStringsPath = sparkleRoot! + "/Sparkle/en.lproj/Sparkle.strings" let enStringsDict = NSDictionary(contentsOfFile: enStringsPath) if enStringsDict == nil { die("Invalid English strings") } let enStringsDictKeys = enStringsDict!.allKeys let dirPath = NSString(string: sparkleRoot! + "/Sparkle") let dirContents = try! NSFileManager.defaultManager().contentsOfDirectoryAtPath(dirPath as String) let css = "body { font-family: sans-serif; font-size: 10pt; }" + "h1 { font-size: 12pt; }" + ".missing { background-color: #FFBABA; color: #D6010E; white-space: pre; }" + ".unused { background-color: #BDE5F8; color: #00529B; white-space: pre; }" + ".unlocalized { background-color: #FEEFB3; color: #9F6000; white-space: pre; }" var html = NSXMLDocument(rootElement: NSXMLElement(name: "html")) html.DTD = NSXMLDTD() html.DTD!.name = html.rootElement()!.name html.characterEncoding = "UTF-8" html.documentContentKind = NSXMLDocumentContentKind.XHTMLKind var body = NSXMLElement(name: "body") var head = NSXMLElement(name: "head") html.rootElement()!.addChild(head) html.rootElement()!.addChild(body) head.addChild(NSXMLElement(name: "meta", attributes: ["charset": html.characterEncoding!])) head.addChild(NSXMLElement(name: "title", stringValue: "Sparkle Localizations Report")) head.addChild(NSXMLElement(name: "style", stringValue: css)) let locale = NSLocale.currentLocale() for dirEntry in dirContents { if NSString(string: dirEntry).pathExtension != "lproj" || dirEntry == "en.lproj" { continue } let lang = locale.displayNameForKey(NSLocaleLanguageCode, value: NSString(string: dirEntry).stringByDeletingPathExtension) body.addChild(NSXMLElement(name: "h1", stringValue: "\(dirEntry) (\(lang!))")) let stringsPath = NSString(string: dirPath.stringByAppendingPathComponent(dirEntry)).stringByAppendingPathComponent("Sparkle.strings") let stringsDict = NSDictionary(contentsOfFile: stringsPath) if stringsDict == nil { die("Invalid strings file \(dirEntry)") continue } var missing: [String] = [] var unlocalized: [String] = [] var unused: [String] = [] for key in enStringsDictKeys { let str = stringsDict?.objectForKey(key) as? String if str == nil { missing.append(key as! String) } else if let enStr = enStringsDict?.objectForKey(key) as? String { if enStr == str { unlocalized.append(key as! String) } } } let stringsDictKeys = stringsDict!.allKeys for key in stringsDictKeys { if enStringsDict?.objectForKey(key) == nil { unused.append(key as! String) } } let sorter = { (s1: String, s2: String) -> Bool in return s1 < s2 } missing.sortInPlace(sorter) unlocalized.sortInPlace(sorter) unused.sortInPlace(sorter) let addRow = { (prefix: String, cssClass: String, key: String) -> Void in body.addChild(NSXMLElement(name: "span", attributes: ["class": cssClass], stringValue: [prefix, key].joinWithSeparator(" ") + "\n")) } for key in missing { addRow("Missing", "missing", key) } for key in unlocalized { addRow("Unlocalized", "unlocalized", key) } for key in unused { addRow("Unused", "unused", key) } } var err: NSError? if !html.XMLData.writeToFile(htmlPath!, atomically: true) { die("Can't write report: \(err)") } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAppcast.h ================================================ // // SUAppcast.h // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUAPPCAST_H #define SUAPPCAST_H #if __has_feature(modules) @import Foundation; #else #import #endif #import "SUExport.h" @class SUAppcastItem; SU_EXPORT @interface SUAppcast : NSObject @property (copy) NSString *userAgentString; @property (copy) NSDictionary *httpHeaders; - (void)fetchAppcastFromURL:(NSURL *)url completionBlock:(void (^)(NSError *))err; - (SUAppcast *)copyWithoutDeltaUpdates; @property (readonly, copy) NSArray *items; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAppcast.m ================================================ // // SUAppcast.m // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUAppcast.h" #import "SUConstants.h" #import "SULog.h" @interface NSXMLElement (SUAppcastExtensions) @property (readonly, copy) NSDictionary *attributesAsDictionary; @end @implementation NSXMLElement (SUAppcastExtensions) - (NSDictionary *)attributesAsDictionary { NSEnumerator *attributeEnum = [[self attributes] objectEnumerator]; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; for (NSXMLNode *attribute in attributeEnum) { NSString *attrName = [attribute name]; if (!attrName) { continue; } NSString *attributeStringValue = [attribute stringValue]; if (attributeStringValue != nil) { [dictionary setObject:attributeStringValue forKey:attrName]; } } return dictionary; } @end @interface SUAppcast () @property (strong) void (^completionBlock)(NSError *); @property (copy) NSString *downloadFilename; @property (strong) NSURLDownload *download; @property (copy) NSArray *items; - (void)reportError:(NSError *)error; - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes; @end @implementation SUAppcast @synthesize downloadFilename; @synthesize completionBlock; @synthesize userAgentString; @synthesize httpHeaders; @synthesize download; @synthesize items; - (void)fetchAppcastFromURL:(NSURL *)url completionBlock:(void (^)(NSError *))block { self.completionBlock = block; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0]; if (self.userAgentString) { [request setValue:self.userAgentString forHTTPHeaderField:@"User-Agent"]; } if (self.httpHeaders) { for (NSString *key in self.httpHeaders) { id value = [self.httpHeaders objectForKey:key]; [request setValue:value forHTTPHeaderField:key]; } } [request setValue:@"application/rss+xml,*/*;q=0.1" forHTTPHeaderField:@"Accept"]; self.download = [[NSURLDownload alloc] initWithRequest:request delegate:self]; } - (void)download:(NSURLDownload *)__unused aDownload decideDestinationWithSuggestedFilename:(NSString *)filename { NSString *destinationFilename = NSTemporaryDirectory(); if (destinationFilename) { destinationFilename = [destinationFilename stringByAppendingPathComponent:filename]; [self.download setDestination:destinationFilename allowOverwrite:NO]; } } - (void)download:(NSURLDownload *)__unused aDownload didCreateDestination:(NSString *)path { self.downloadFilename = path; } - (void)downloadDidFinish:(NSURLDownload *)__unused aDownload { NSError *error = nil; NSArray *appcastItems = [self parseAppcastItemsFromXMLFile:[NSURL fileURLWithPath:self.downloadFilename] error:&error]; [[NSFileManager defaultManager] removeItemAtPath:self.downloadFilename error:nil]; self.downloadFilename = nil; if (appcastItems) { self.items = appcastItems; self.completionBlock(nil); self.completionBlock = nil; } else { NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject: SULocalizedString(@"An error occurred while parsing the update feed.", nil) forKey: NSLocalizedDescriptionKey]; if (error) { [userInfo setObject:error forKey:NSUnderlyingErrorKey]; } [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:userInfo]]; } } - (NSDictionary *)attributesOfNode:(NSXMLElement *)node { NSEnumerator *attributeEnum = [[node attributes] objectEnumerator]; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; for (NSXMLNode *attribute in attributeEnum) { NSString *attrName = [self sparkleNamespacedNameOfNode:attribute]; if (!attrName) { continue; } NSString *stringValue = [attribute stringValue]; if (stringValue) { [dictionary setObject:stringValue forKey:attrName]; } } return dictionary; } -(NSString *)sparkleNamespacedNameOfNode:(NSXMLNode *)node { // XML namespace prefix is semantically meaningless, so compare namespace URI // NS URI isn't used to fetch anything, and must match exactly, so we look for http:// not https:// if ([[node URI] isEqualToString:@"http://www.andymatuschak.org/xml-namespaces/sparkle"]) { NSString *localName = [node localName]; assert(localName); return [@"sparkle:" stringByAppendingString:localName]; } else { return [node name]; // Backwards compatibility } } -(NSArray *)parseAppcastItemsFromXMLFile:(NSURL *)appcastFile error:(NSError *__autoreleasing*)errorp { if (errorp) { *errorp = nil; } if (!appcastFile) { return nil; } NSUInteger options = NSXMLNodeLoadExternalEntitiesNever; // Prevent inclusion from file:// NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:appcastFile options:options error:errorp]; if (nil == document) { return nil; } NSArray *xmlItems = [document nodesForXPath:@"/rss/channel/item" error:errorp]; if (nil == xmlItems) { return nil; } NSMutableArray *appcastItems = [NSMutableArray array]; NSEnumerator *nodeEnum = [xmlItems objectEnumerator]; NSXMLNode *node; while((node = [nodeEnum nextObject])) { NSMutableDictionary *nodesDict = [NSMutableDictionary dictionary]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; // First, we'll "index" all the first-level children of this appcast item so we can pick them out by language later. if ([[node children] count]) { node = [node childAtIndex:0]; while (nil != node) { NSString *name = [self sparkleNamespacedNameOfNode:node]; if (name) { NSMutableArray *nodes = [nodesDict objectForKey:name]; if (nodes == nil) { nodes = [NSMutableArray array]; [nodesDict setObject:nodes forKey:name]; } [nodes addObject:node]; } node = [node nextSibling]; } } for (NSString *name in nodesDict) { node = [self bestNodeInNodes:[nodesDict objectForKey:name]]; if ([name isEqualToString:SURSSElementEnclosure]) { // enclosure is flattened as a separate dictionary for some reason NSDictionary *encDict = [self attributesOfNode:(NSXMLElement *)node]; [dict setObject:encDict forKey:name]; } else if ([name isEqualToString:SURSSElementPubDate]) { // We don't want to parse and create a NSDate instance - // that's a risk we can avoid. We don't use the date anywhere other // than it being accessible from SUAppcastItem NSString *dateString = node.stringValue; if (dateString) { [dict setObject:dateString forKey:name]; } } else if ([name isEqualToString:SUAppcastElementDeltas]) { NSMutableArray *deltas = [NSMutableArray array]; NSEnumerator *childEnum = [[node children] objectEnumerator]; for (NSXMLNode *child in childEnum) { if ([[child name] isEqualToString:SURSSElementEnclosure]) { [deltas addObject:[self attributesOfNode:(NSXMLElement *)child]]; } } [dict setObject:deltas forKey:name]; } else if ([name isEqualToString:SUAppcastElementTags]) { NSMutableArray *tags = [NSMutableArray array]; NSEnumerator *childEnum = [[node children] objectEnumerator]; for (NSXMLNode *child in childEnum) { NSString *childName = child.name; if (childName) { [tags addObject:childName]; } } [dict setObject:tags forKey:name]; } else if (name != nil) { // add all other values as strings NSString *theValue = [[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (theValue != nil) { [dict setObject:theValue forKey:name]; } } } NSString *errString; SUAppcastItem *anItem = [[SUAppcastItem alloc] initWithDictionary:dict failureReason:&errString]; if (anItem) { [appcastItems addObject:anItem]; } else { SULog(@"Sparkle Updater: Failed to parse appcast item: %@.\nAppcast dictionary was: %@", errString, dict); if (errorp) *errorp = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:@{NSLocalizedDescriptionKey: errString}]; return nil; } } self.items = appcastItems; return appcastItems; } - (void)download:(NSURLDownload *)__unused aDownload didFailWithError:(NSError *)error { if (self.downloadFilename) { [[NSFileManager defaultManager] removeItemAtPath:self.downloadFilename error:nil]; } self.downloadFilename = nil; [self reportError:error]; } - (NSURLRequest *)download:(NSURLDownload *)__unused aDownload willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)__unused redirectResponse { return request; } - (void)reportError:(NSError *)error { NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred in retrieving update information. Please try again later.", nil), NSLocalizedFailureReasonErrorKey: [error localizedDescription], NSUnderlyingErrorKey: error, }]; NSURL *failingUrl = [error.userInfo objectForKey:NSURLErrorFailingURLErrorKey]; if (failingUrl) { [userInfo setObject:failingUrl forKey:NSURLErrorFailingURLErrorKey]; } self.completionBlock([NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastError userInfo:userInfo]); self.completionBlock = nil; } - (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes { // We use this method to pick out the localized version of a node when one's available. if ([nodes count] == 1) return [nodes objectAtIndex:0]; else if ([nodes count] == 0) return nil; NSMutableArray *languages = [NSMutableArray array]; NSString *lang; NSUInteger i; for (NSXMLElement *node in nodes) { lang = [[node attributeForName:@"xml:lang"] stringValue]; [languages addObject:(lang ? lang : @"")]; } lang = [[NSBundle preferredLocalizationsFromArray:languages] objectAtIndex:0]; i = [languages indexOfObject:([languages containsObject:lang] ? lang : @"")]; if (i == NSNotFound) { i = 0; } return [nodes objectAtIndex:i]; } - (SUAppcast *)copyWithoutDeltaUpdates { SUAppcast *other = [SUAppcast new]; NSMutableArray *nonDeltaItems = [NSMutableArray new]; for(SUAppcastItem *item in self.items) { if (![item isDeltaUpdate]) [nonDeltaItems addObject:item]; } other.items = nonDeltaItems; return other; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAppcastItem.h ================================================ // // SUAppcastItem.h // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUAPPCASTITEM_H #define SUAPPCASTITEM_H #if __has_feature(modules) @import Foundation; #else #import #endif #import "SUExport.h" SU_EXPORT @interface SUAppcastItem : NSObject @property (copy, readonly) NSString *title; @property (copy, readonly) NSString *dateString; @property (copy, readonly) NSString *itemDescription; @property (strong, readonly) NSURL *releaseNotesURL; @property (copy, readonly) NSString *DSASignature; @property (copy, readonly) NSString *minimumSystemVersion; @property (copy, readonly) NSString *maximumSystemVersion; @property (strong, readonly) NSURL *fileURL; @property (copy, readonly) NSString *versionString; @property (copy, readonly) NSString *displayVersionString; @property (copy, readonly) NSDictionary *deltaUpdates; @property (strong, readonly) NSURL *infoURL; // Initializes with data from a dictionary provided by the RSS class. - (instancetype)initWithDictionary:(NSDictionary *)dict; - (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error; @property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate; @property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate; @property (getter=isInformationOnlyUpdate, readonly) BOOL informationOnlyUpdate; // Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. @property (readonly, copy) NSDictionary *propertiesDictionary; - (NSURL *)infoURL; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAppcastItem.m ================================================ // // SUAppcastItem.m // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUAppcastItem.h" #import "SULog.h" @interface SUAppcastItem () @property (copy, readwrite) NSString *title; @property (copy, readwrite) NSString *dateString; @property (copy, readwrite) NSString *itemDescription; @property (strong, readwrite) NSURL *releaseNotesURL; @property (copy, readwrite) NSString *DSASignature; @property (copy, readwrite) NSString *minimumSystemVersion; @property (copy, readwrite) NSString *maximumSystemVersion; @property (strong, readwrite) NSURL *fileURL; @property (copy, readwrite) NSString *versionString; @property (copy, readwrite) NSString *displayVersionString; @property (copy, readwrite) NSDictionary *deltaUpdates; @property (strong, readwrite) NSURL *infoURL; @property (readwrite, copy) NSDictionary *propertiesDictionary; @end @implementation SUAppcastItem @synthesize dateString; @synthesize deltaUpdates; @synthesize displayVersionString; @synthesize DSASignature; @synthesize fileURL; @synthesize infoURL; @synthesize itemDescription; @synthesize maximumSystemVersion; @synthesize minimumSystemVersion; @synthesize releaseNotesURL; @synthesize title; @synthesize versionString; @synthesize propertiesDictionary; - (BOOL)isDeltaUpdate { NSDictionary *rssElementEnclosure = [self.propertiesDictionary objectForKey:SURSSElementEnclosure]; return [rssElementEnclosure objectForKey:SUAppcastAttributeDeltaFrom] != nil; } - (BOOL)isCriticalUpdate { return [[self.propertiesDictionary objectForKey:SUAppcastElementTags] containsObject:SUAppcastElementCriticalUpdate]; } - (BOOL)isInformationOnlyUpdate { return self.infoURL && !self.fileURL; } - (instancetype)initWithDictionary:(NSDictionary *)dict { return [self initWithDictionary:dict failureReason:nil]; } - (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString *__autoreleasing *)error { self = [super init]; if (self) { NSDictionary *enclosure = [dict objectForKey:SURSSElementEnclosure]; // Try to find a version string. // Finding the new version number from the RSS feed is a little bit hacky. There are two ways: // 1. A "sparkle:version" attribute on the enclosure tag, an extension from the RSS spec. // 2. If there isn't a version attribute, Sparkle will parse the path in the enclosure, expecting // that it will look like this: http://something.com/YourApp_0.5.zip. It'll read whatever's between the last // underscore and the last period as the version number. So name your packages like this: APPNAME_VERSION.extension. // The big caveat with this is that you can't have underscores in your version strings, as that'll confuse Sparkle. // Feel free to change the separator string to a hyphen or something more suited to your needs if you like. NSString *newVersion = [enclosure objectForKey:SUAppcastAttributeVersion]; if (newVersion == nil) { newVersion = [dict objectForKey:SUAppcastAttributeVersion]; // Get version from the item, in case it's a download-less item (i.e. paid upgrade). } if (newVersion == nil) // no sparkle:version attribute anywhere? { SULog(@"warning: <%@> for URL '%@' is missing %@ attribute. Version comparison may be unreliable. Please always specify %@", SURSSElementEnclosure, [enclosure objectForKey:SURSSAttributeURL], SUAppcastAttributeVersion, SUAppcastAttributeVersion); // Separate the url by underscores and take the last component, as that'll be closest to the end, // then we remove the extension. Hopefully, this will be the version. NSArray *fileComponents = [[enclosure objectForKey:SURSSAttributeURL] componentsSeparatedByString:@"_"]; if ([fileComponents count] > 1) { newVersion = [[fileComponents lastObject] stringByDeletingPathExtension]; } } if (!newVersion) { if (error) { *error = [NSString stringWithFormat:@"Feed item lacks %@ attribute, and version couldn't be deduced from file name (would have used last component of a file name like AppName_1.3.4.zip)", SUAppcastAttributeVersion]; } return nil; } propertiesDictionary = [[NSMutableDictionary alloc] initWithDictionary:dict]; self.title = [dict objectForKey:SURSSElementTitle]; self.dateString = [dict objectForKey:SURSSElementPubDate]; self.itemDescription = [dict objectForKey:SURSSElementDescription]; NSString *theInfoURL = [dict objectForKey:SURSSElementLink]; if (theInfoURL) { if (![theInfoURL isKindOfClass:[NSString class]]) { SULog(@"%@ -%@ Info URL is not of valid type.", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); } else { self.infoURL = [NSURL URLWithString:theInfoURL]; } } // Need an info URL or an enclosure URL. Former to show "More Info" // page, latter to download & install: if (!enclosure && !theInfoURL) { if (error) { *error = @"No enclosure in feed item"; } return nil; } NSString *enclosureURLString = [enclosure objectForKey:SURSSAttributeURL]; if (!enclosureURLString && !theInfoURL) { if (error) { *error = @"Feed item's enclosure lacks URL"; } return nil; } if (enclosureURLString) { // Sparkle used to always URL-encode, so for backwards compatibility spaces in URLs must be forgiven. NSString *fileURLString = [enclosureURLString stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; self.fileURL = [NSURL URLWithString:fileURLString]; } if (enclosure) { self.DSASignature = [enclosure objectForKey:SUAppcastAttributeDSASignature]; } self.versionString = newVersion; self.minimumSystemVersion = [dict objectForKey:SUAppcastElementMinimumSystemVersion]; self.maximumSystemVersion = [dict objectForKey:SUAppcastElementMaximumSystemVersion]; NSString *shortVersionString = [enclosure objectForKey:SUAppcastAttributeShortVersionString]; if (nil == shortVersionString) { shortVersionString = [dict objectForKey:SUAppcastAttributeShortVersionString]; // fall back on the } if (shortVersionString) { self.displayVersionString = shortVersionString; } else { self.displayVersionString = self.versionString; } // Find the appropriate release notes URL. NSString *releaseNotesString = [dict objectForKey:SUAppcastElementReleaseNotesLink]; if (releaseNotesString) { NSURL *url = [NSURL URLWithString:releaseNotesString]; if ([url isFileURL]) { SULog(@"Release notes with file:// URLs are not supported"); } else { self.releaseNotesURL = url; } } else if ([self.itemDescription hasPrefix:@"http://"] || [self.itemDescription hasPrefix:@"https://"]) { // if the description starts with http:// or https:// use that. self.releaseNotesURL = [NSURL URLWithString:self.itemDescription]; } else { self.releaseNotesURL = nil; } NSArray *deltaDictionaries = [dict objectForKey:SUAppcastElementDeltas]; if (deltaDictionaries) { NSMutableDictionary *deltas = [NSMutableDictionary dictionary]; for (NSDictionary *deltaDictionary in deltaDictionaries) { NSString *deltaFrom = [deltaDictionary objectForKey:SUAppcastAttributeDeltaFrom]; if (!deltaFrom) continue; NSMutableDictionary *fakeAppCastDict = [dict mutableCopy]; [fakeAppCastDict removeObjectForKey:SUAppcastElementDeltas]; [fakeAppCastDict setObject:deltaDictionary forKey:SURSSElementEnclosure]; SUAppcastItem *deltaItem = [[SUAppcastItem alloc] initWithDictionary:fakeAppCastDict]; [deltas setObject:deltaItem forKey:deltaFrom]; } self.deltaUpdates = deltas; } } return self; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAutomaticUpdateAlert.h ================================================ // // SUAutomaticUpdateAlert.h // Sparkle // // Created by Andy Matuschak on 3/18/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUAUTOMATICUPDATEALERT_H #define SUAUTOMATICUPDATEALERT_H #import "SUWindowController.h" typedef NS_ENUM(NSInteger, SUAutomaticInstallationChoice) { SUInstallNowChoice, SUInstallLaterChoice, SUDoNotInstallChoice }; @class SUAppcastItem, SUHost; @interface SUAutomaticUpdateAlert : SUWindowController - (instancetype)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)hostBundle completionBlock:(void (^)(SUAutomaticInstallationChoice))c; - (IBAction)installNow:sender; - (IBAction)installLater:sender; - (IBAction)doNotInstall:sender; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAutomaticUpdateAlert.m ================================================ // // SUAutomaticUpdateAlert.m // Sparkle // // Created by Andy Matuschak on 3/18/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUAutomaticUpdateAlert.h" #import "SUHost.h" @interface SUAutomaticUpdateAlert () @property (strong) void(^completionBlock)(SUAutomaticInstallationChoice); @property (strong) SUAppcastItem *updateItem; @property (strong) SUHost *host; @end @implementation SUAutomaticUpdateAlert @synthesize host; @synthesize updateItem; @synthesize completionBlock; - (instancetype)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)aHost completionBlock:(void (^)(SUAutomaticInstallationChoice))block { self = [super initWithWindowNibName:@"SUAutomaticUpdateAlert"]; if (self) { self.updateItem = item; self.completionBlock = block; self.host = aHost; [self setShouldCascadeWindows:NO]; [[self window] center]; } return self; } - (NSString *__nonnull)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self.host bundlePath], [self.host installationPath]]; } - (IBAction)installNow:(id)__unused sender { [self close]; self.completionBlock(SUInstallNowChoice); self.completionBlock = nil; } - (IBAction)installLater:(id)__unused sender { [self close]; self.completionBlock(SUInstallLaterChoice); self.completionBlock = nil; } - (IBAction)doNotInstall:(id)__unused sender { [self close]; self.completionBlock(SUDoNotInstallChoice); self.completionBlock = nil; } - (NSImage *__nonnull)applicationIcon { return [self.host icon]; } - (NSString *__nonnull)titleText { if ([self.updateItem isCriticalUpdate]) { return [NSString stringWithFormat:SULocalizedString(@"An important update to %@ is ready to install", nil), [self.host name]]; } else { return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is ready to install!", nil), [self.host name]]; } } - (NSString *)descriptionText { if ([self.updateItem isCriticalUpdate]) { return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?", nil), [self.host name], [self.updateItem displayVersionString]]; } else { return [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?", nil), [self.host name], [self.updateItem displayVersionString]]; } } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAutomaticUpdateDriver.h ================================================ // // SUAutomaticUpdateDriver.h // Sparkle // // Created by Andy Matuschak on 5/6/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUAUTOMATICUPDATEDRIVER_H #define SUAUTOMATICUPDATEDRIVER_H #import #import "SUBasicUpdateDriver.h" #import "SUAutomaticUpdateAlert.h" @interface SUAutomaticUpdateDriver : SUBasicUpdateDriver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUAutomaticUpdateDriver.m ================================================ // // SUAutomaticUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 5/6/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUAutomaticUpdateDriver.h" #import "SUAutomaticUpdateAlert.h" #import "SUHost.h" #import "SUConstants.h" // If the user hasn't quit in a week, ask them if they want to relaunch to get the latest bits. It doesn't matter that this measure of "one day" is imprecise. static const NSTimeInterval SUAutomaticUpdatePromptImpatienceTimer = 60 * 60 * 24 * 7; @interface SUUpdateDriver () @property (getter=isInterruptible) BOOL interruptible; @end @interface SUAutomaticUpdateDriver () @property (assign) BOOL postponingInstallation; @property (assign) BOOL showErrors; @property (assign) BOOL willUpdateOnTermination; @property (assign) BOOL isTerminating; @property (strong) SUAutomaticUpdateAlert *alert; @property (strong) NSTimer *showUpdateAlertTimer; @end @implementation SUAutomaticUpdateDriver @synthesize postponingInstallation; @synthesize showErrors; @synthesize willUpdateOnTermination; @synthesize isTerminating; @synthesize alert; @synthesize showUpdateAlertTimer; - (void)showUpdateAlert { self.interruptible = NO; self.alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUAutomaticInstallationChoice choice) { [self automaticUpdateAlertFinishedWithChoice:choice]; }]; // If the app is a menubar app or the like, we need to focus it first and alter the // update prompt to behave like a normal window. Otherwise if the window were hidden // there may be no way for the application to be activated to make it visible again. if ([self.host isBackgroundApplication]) { [[self.alert window] setHidesOnDeactivate:NO]; [NSApp activateIgnoringOtherApps:YES]; } if ([NSApp isActive]) [[self.alert window] makeKeyAndOrderFront:self]; else [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp]; } - (void)unarchiverDidFinish:(SUUnarchiver *)__unused ua { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil]; [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(systemWillPowerOff:) name:NSWorkspaceWillPowerOffNotification object:nil]; // Sudden termination is available on 10.6+ NSProcessInfo *processInfo = [NSProcessInfo processInfo]; [processInfo disableSuddenTermination]; self.willUpdateOnTermination = YES; id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updater:willInstallUpdateOnQuit:immediateInstallationInvocation:)]) { BOOL relaunch = YES; BOOL showUI = NO; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(installWithToolAndRelaunch:displayingUserInterface:)]]; [invocation setSelector:@selector(installWithToolAndRelaunch:displayingUserInterface:)]; [invocation setArgument:&relaunch atIndex:2]; [invocation setArgument:&showUI atIndex:3]; [invocation setTarget:self]; [updaterDelegate updater:self.updater willInstallUpdateOnQuit:self.updateItem immediateInstallationInvocation:invocation]; } // If this is marked as a critical update, we'll prompt the user to install it right away. if ([self.updateItem isCriticalUpdate]) { [self showUpdateAlert]; } else { self.showUpdateAlertTimer = [NSTimer scheduledTimerWithTimeInterval:SUAutomaticUpdatePromptImpatienceTimer target:self selector:@selector(showUpdateAlert) userInfo:nil repeats:NO]; // At this point the driver is idle, allow it to be interrupted for user-initiated update checks. self.interruptible = YES; } } - (void)stopUpdatingOnTermination { if (self.willUpdateOnTermination) { [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:nil]; [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceWillPowerOffNotification object:nil]; NSProcessInfo *processInfo = [NSProcessInfo processInfo]; [processInfo enableSuddenTermination]; self.willUpdateOnTermination = NO; id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updater:didCancelInstallUpdateOnQuit:)]) [updaterDelegate updater:self.updater didCancelInstallUpdateOnQuit:self.updateItem]; } } - (void)invalidateShowUpdateAlertTimer { [self.showUpdateAlertTimer invalidate]; self.showUpdateAlertTimer = nil; } - (void)dealloc { [self stopUpdatingOnTermination]; [self invalidateShowUpdateAlertTimer]; } - (void)abortUpdate { self.isTerminating = NO; [self stopUpdatingOnTermination]; [self invalidateShowUpdateAlertTimer]; [super abortUpdate]; } - (void)applicationDidBecomeActive:(NSNotification *)__unused aNotification { [[self.alert window] makeKeyAndOrderFront:self]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:NSApp]; } - (void)automaticUpdateAlertFinishedWithChoice:(SUAutomaticInstallationChoice)choice { switch (choice) { case SUInstallNowChoice: [self stopUpdatingOnTermination]; [self installWithToolAndRelaunch:YES]; break; case SUInstallLaterChoice: self.postponingInstallation = YES; // We're already waiting on quit, just indicate that we're idle. self.interruptible = YES; break; case SUDoNotInstallChoice: [self.host setObject:[self.updateItem versionString] forUserDefaultsKey:SUSkippedVersionKey]; [self abortUpdate]; break; } } - (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL)showUI { if (relaunch) { [self stopUpdatingOnTermination]; } self.showErrors = YES; [super installWithToolAndRelaunch:relaunch displayingUserInterface:showUI]; } - (void)systemWillPowerOff:(NSNotification *)__unused note { [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUSystemPowerOffError userInfo:@{ NSLocalizedDescriptionKey: SULocalizedString(@"The update will not be installed because the user requested for the system to power off", nil) }]]; } - (void)applicationWillTerminate:(NSNotification *)__unused note { // We don't want to terminate the app if the user or someone else initiated a termination // Use a property instead of passing an argument to installWithToolAndRelaunch: // because we give the delegate an invocation to our install methods and // this code was added later :| self.isTerminating = YES; [self installWithToolAndRelaunch:NO]; } - (void)terminateApp { if (!self.isTerminating) { [super terminateApp]; } } - (void)abortUpdateWithError:(NSError *)error { if (self.showErrors) { [super abortUpdateWithError:error]; } else { // Call delegate separately here because otherwise it won't know we stopped. // Normally this gets called by the superclass id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updater:didAbortWithError:)]) { [updaterDelegate updater:self.updater didAbortWithError:error]; } [self abortUpdate]; } } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBasicUpdateDriver.h ================================================ // // SUBasicUpdateDriver.h // Sparkle, // // Created by Andy Matuschak on 4/23/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUBASICUPDATEDRIVER_H #define SUBASICUPDATEDRIVER_H #import #import "SUUpdateDriver.h" #import "SUUnarchiver.h" #import "SUAppcast.h" @class SUAppcastItem, SUHost; @interface SUBasicUpdateDriver : SUUpdateDriver @property (strong, readonly) SUAppcastItem *updateItem; @property (strong, readonly) NSURLDownload *download; @property (copy, readonly) NSString *downloadPath; - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)host; - (BOOL)isItemNewer:(SUAppcastItem *)ui; + (BOOL)hostSupportsItem:(SUAppcastItem *)ui; - (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui; - (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui; - (void)appcastDidFinishLoading:(SUAppcast *)ac; - (void)didFindValidUpdate; - (void)didNotFindUpdate; - (void)downloadUpdate; - (void)download:(NSURLDownload *)d decideDestinationWithSuggestedFilename:(NSString *)name; - (void)downloadDidFinish:(NSURLDownload *)d; - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error; - (void)extractUpdate; - (void)unarchiverDidFinish:(SUUnarchiver *)ua; - (void)unarchiverDidFail:(SUUnarchiver *)ua; - (void)failedToApplyDeltaUpdate; - (void)installWithToolAndRelaunch:(BOOL)relaunch; - (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL)showUI; - (void)installerForHost:(SUHost *)host failedWithError:(NSError *)error; - (void)cleanUpDownload; - (void)abortUpdate; - (void)abortUpdateWithError:(NSError *)error; - (void)terminateApp; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBasicUpdateDriver.m ================================================ // // SUBasicUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 4/23/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUBasicUpdateDriver.h" #import "SUHost.h" #import "SUOperatingSystem.h" #import "SUDSAVerifier.h" #import "SUInstaller.h" #import "SUStandardVersionComparator.h" #import "SUUnarchiver.h" #import "SUConstants.h" #import "SULog.h" #import "SUBinaryDeltaCommon.h" #import "SUCodeSigningVerifier.h" #import "SUUpdater_Private.h" #import "SUFileManager.h" @interface SUBasicUpdateDriver () @property (strong) SUAppcastItem *updateItem; @property (strong) NSURLDownload *download; @property (copy) NSString *downloadPath; @property (strong) SUAppcastItem *nonDeltaUpdateItem; @property (copy) NSString *tempDir; @property (copy) NSString *relaunchPath; @end @implementation SUBasicUpdateDriver @synthesize updateItem; @synthesize download; @synthesize downloadPath; @synthesize nonDeltaUpdateItem; @synthesize tempDir; @synthesize relaunchPath; - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)aHost { [super checkForUpdatesAtURL:URL host:aHost]; if ([aHost isRunningOnReadOnlyVolume]) { [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURunningFromDiskImageError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again.", nil), [aHost name]] }]]; return; } SUAppcast *appcast = [[SUAppcast alloc] init]; [appcast setUserAgentString:[self.updater userAgentString]]; [appcast setHttpHeaders:[self.updater httpHeaders]]; [appcast fetchAppcastFromURL:URL completionBlock:^(NSError *error) { if (error) { [self abortUpdateWithError:error]; } else { [self appcastDidFinishLoading:appcast]; } }]; } - (id)versionComparator { id comparator = nil; // Give the delegate a chance to provide a custom version comparator if ([[self.updater delegate] respondsToSelector:@selector(versionComparatorForUpdater:)]) { comparator = [[self.updater delegate] versionComparatorForUpdater:self.updater]; } // If we don't get a comparator from the delegate, use the default comparator if (!comparator) { comparator = [SUStandardVersionComparator defaultComparator]; } return comparator; } + (SUAppcastItem *)bestItemFromAppcastItems:(NSArray *)appcastItems getDeltaItem:(SUAppcastItem * __autoreleasing *)deltaItem withHostVersion:(NSString *)hostVersion comparator:(id)comparator { SUAppcastItem *item = nil; for(SUAppcastItem *candidate in appcastItems) { if ([[self class] hostSupportsItem:candidate]) { if (!item || [comparator compareVersion:item.versionString toVersion:candidate.versionString] == NSOrderedAscending) { item = candidate; } } } if (item && deltaItem) { SUAppcastItem *deltaUpdateItem = [[item deltaUpdates] objectForKey:hostVersion]; if (deltaUpdateItem && [[self class] hostSupportsItem:deltaUpdateItem]) { *deltaItem = deltaUpdateItem; } } return item; } + (BOOL)hostSupportsItem:(SUAppcastItem *)ui { if (([ui minimumSystemVersion] == nil || [[ui minimumSystemVersion] isEqualToString:@""]) && ([ui maximumSystemVersion] == nil || [[ui maximumSystemVersion] isEqualToString:@""])) { return YES; } BOOL minimumVersionOK = TRUE; BOOL maximumVersionOK = TRUE; // Check minimum and maximum System Version if ([ui minimumSystemVersion] != nil && ![[ui minimumSystemVersion] isEqualToString:@""]) { minimumVersionOK = [[SUStandardVersionComparator defaultComparator] compareVersion:[ui minimumSystemVersion] toVersion:[SUOperatingSystem systemVersionString]] != NSOrderedDescending; } if ([ui maximumSystemVersion] != nil && ![[ui maximumSystemVersion] isEqualToString:@""]) { maximumVersionOK = [[SUStandardVersionComparator defaultComparator] compareVersion:[ui maximumSystemVersion] toVersion:[SUOperatingSystem systemVersionString]] != NSOrderedAscending; } return minimumVersionOK && maximumVersionOK; } - (BOOL)isItemNewer:(SUAppcastItem *)ui { return [[self versionComparator] compareVersion:[self.host version] toVersion:[ui versionString]] == NSOrderedAscending; } - (BOOL)itemContainsSkippedVersion:(SUAppcastItem *)ui { NSString *skippedVersion = [self.host objectForUserDefaultsKey:SUSkippedVersionKey]; if (skippedVersion == nil) { return NO; } return [[self versionComparator] compareVersion:[ui versionString] toVersion:skippedVersion] != NSOrderedDescending; } - (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui { return ui && [[self class] hostSupportsItem:ui] && [self isItemNewer:ui] && ![self itemContainsSkippedVersion:ui]; } - (void)appcastDidFinishLoading:(SUAppcast *)ac { if ([[self.updater delegate] respondsToSelector:@selector(updater:didFinishLoadingAppcast:)]) { [[self.updater delegate] updater:self.updater didFinishLoadingAppcast:ac]; } NSDictionary *userInfo = (ac != nil) ? @{ SUUpdaterAppcastNotificationKey: ac } : nil; [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidFinishLoadingAppCastNotification object:self.updater userInfo:userInfo]; SUAppcastItem *item = nil; // Now we have to find the best valid update in the appcast. if ([[self.updater delegate] respondsToSelector:@selector(bestValidUpdateInAppcast:forUpdater:)]) // Does the delegate want to handle it? { item = [[self.updater delegate] bestValidUpdateInAppcast:ac forUpdater:self.updater]; if ([item isDeltaUpdate]) { self.nonDeltaUpdateItem = [[self.updater delegate] bestValidUpdateInAppcast:[ac copyWithoutDeltaUpdates] forUpdater:self.updater]; } } else // If not, we'll take care of it ourselves. { // Find the best supported update SUAppcastItem *deltaUpdateItem = nil; item = [[self class] bestItemFromAppcastItems:ac.items getDeltaItem:&deltaUpdateItem withHostVersion:self.host.version comparator:[self versionComparator]]; if (item && deltaUpdateItem) { self.nonDeltaUpdateItem = item; item = deltaUpdateItem; } } if ([self itemContainsValidUpdate:item]) { self.updateItem = item; [self didFindValidUpdate]; } else { self.updateItem = nil; [self didNotFindUpdate]; } } - (void)didFindValidUpdate { assert(self.updateItem); if ([[self.updater delegate] respondsToSelector:@selector(updater:didFindValidUpdate:)]) { [[self.updater delegate] updater:self.updater didFindValidUpdate:self.updateItem]; } [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidFindValidUpdateNotification object:self.updater userInfo:@{ SUUpdaterAppcastItemNotificationKey: self.updateItem }]; [self downloadUpdate]; } - (void)didNotFindUpdate { if ([[self.updater delegate] respondsToSelector:@selector(updaterDidNotFindUpdate:)]) { [[self.updater delegate] updaterDidNotFindUpdate:self.updater]; } [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater]; [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUNoUpdateError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"You already have the newest version of %@.", "'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI)"), self.host.name] }]]; } - (NSString *)appCachePath { NSArray *cachePaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *cachePath = nil; if ([cachePaths count]) { cachePath = [cachePaths objectAtIndex:0]; } if (!cachePath) { SULog(@"Failed to find user's cache directory! Using system default"); cachePath = NSTemporaryDirectory(); } NSString *name = [self.host.bundle bundleIdentifier]; if (!name) { name = [self.host name]; } cachePath = [cachePath stringByAppendingPathComponent:name]; cachePath = [cachePath stringByAppendingPathComponent:@SPARKLE_BUNDLE_IDENTIFIER]; return cachePath; } - (void)downloadUpdate { // Clear cache directory so that downloads can't possibly accumulate inside NSString *appCachePath = [self appCachePath]; if ([[NSFileManager defaultManager] fileExistsAtPath:appCachePath]) { [[NSFileManager defaultManager] removeItemAtPath:appCachePath error:NULL]; } NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self.updateItem fileURL]]; [request setValue:[self.updater userAgentString] forHTTPHeaderField:@"User-Agent"]; if ([[self.updater delegate] respondsToSelector:@selector(updater:willDownloadUpdate:withRequest:)]) { [[self.updater delegate] updater:self.updater willDownloadUpdate:self.updateItem withRequest:request]; } self.download = [[NSURLDownload alloc] initWithRequest:request delegate:self]; } - (void)download:(NSURLDownload *)__unused d decideDestinationWithSuggestedFilename:(NSString *)name { NSString *downloadFileName = [NSString stringWithFormat:@"%@ %@", [self.host name], [self.updateItem versionString]]; NSString *appCachePath = [self appCachePath]; self.tempDir = [appCachePath stringByAppendingPathComponent:downloadFileName]; int cnt = 1; while ([[NSFileManager defaultManager] fileExistsAtPath:self.tempDir] && cnt <= 999) { self.tempDir = [appCachePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@ %d", downloadFileName, cnt++]]; } // Create the temporary directory if necessary. BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:self.tempDir withIntermediateDirectories:YES attributes:nil error:NULL]; if (!success) { // Okay, something's really broken with this user's file structure. [self.download cancel]; [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUTemporaryDirectoryError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Can't make a temporary directory for the update download at %@.", self.tempDir] }]]; } self.downloadPath = [self.tempDir stringByAppendingPathComponent:name]; [self.download setDestination:self.downloadPath allowOverwrite:YES]; } /** * If the update is a package, then it must be signed using DSA. No other verification is done. * * If the update is a bundle, then it must meet any one of: * * * old and new DSA public keys are the same and valid (it allows change of Code Signing identity), or * * * old and new Code Signing identity are the same and valid * */ - (BOOL)validateUpdateDownloadedToPath:(NSString *)downloadedPath extractedToPath:(NSString *)extractedPath DSASignature:(NSString *)DSASignature publicDSAKey:(NSString *)publicDSAKey { BOOL isPackage = NO; NSString *installSourcePath = [SUInstaller installSourcePathInUpdateFolder:extractedPath forHost:self.host isPackage:&isPackage isGuided:NULL]; if (installSourcePath == nil) { SULog(@"No suitable install is found in the update. The update will be rejected."); return NO; } // Modern packages are not distributed as bundles and are code signed differently than regular applications if (isPackage) { if (nil == publicDSAKey) { SULog(@"The existing app bundle does not have a DSA key, so it can't verify installer packages."); } BOOL packageValidated = [SUDSAVerifier validatePath:downloadedPath withEncodedDSASignature:DSASignature withPublicDSAKey:publicDSAKey]; if (!packageValidated) { SULog(@"DSA signature validation of the package failed. The update contains an installer package, and valid DSA signatures are mandatory for all installer packages. The update will be rejected. Sign the installer with a valid DSA key or use an .app bundle update instead."); } return packageValidated; } NSBundle *newBundle = [NSBundle bundleWithPath:installSourcePath]; if (newBundle == nil) { SULog(@"No suitable bundle is found in the update. The update will be rejected."); return NO; } SUHost *newHost = [[SUHost alloc] initWithBundle:newBundle]; NSString *newPublicDSAKey = newHost.publicDSAKey; BOOL dsaKeysMatch = (publicDSAKey == nil || newPublicDSAKey == nil) ? NO : [publicDSAKey isEqualToString:newPublicDSAKey]; // If the new DSA key differs from the old, then this check is not a security measure, because the new key is not trusted. // In that case, the check ensures that the app author has correctly used DSA keys, so that the app will be updateable in the next version. // However if the new and old DSA keys are the same, then this is a security measure. if (newPublicDSAKey != nil) { if (![SUDSAVerifier validatePath:downloadedPath withEncodedDSASignature:DSASignature withPublicDSAKey:newPublicDSAKey]) { SULog(@"DSA signature validation failed. The update has a public DSA key and is signed with a DSA key, but the %@ doesn't match the signature. The update will be rejected.", dsaKeysMatch ? @"public key" : @"new public key shipped with the update"); return NO; } } BOOL updateIsCodeSigned = [SUCodeSigningVerifier applicationAtPathIsCodeSigned:installSourcePath]; if (dsaKeysMatch) { NSError *error = nil; if (updateIsCodeSigned && ![SUCodeSigningVerifier codeSignatureIsValidAtPath:installSourcePath error:&error]) { SULog(@"The update archive has a valid DSA signature, but the app is also signed with Code Signing, which is corrupted: %@. The update will be rejected.", error); return NO; } } else { BOOL hostIsCodeSigned = [SUCodeSigningVerifier hostApplicationIsCodeSigned]; NSString *dsaStatus = newPublicDSAKey ? @"has a new DSA key that doesn't match the previous one" : (publicDSAKey ? @"removes the DSA key" : @"isn't signed with a DSA key"); if (!hostIsCodeSigned || !updateIsCodeSigned) { NSString *acsStatus = !hostIsCodeSigned ? @"old app hasn't been signed with app Code Signing" : @"new app isn't signed with app Code Signing"; SULog(@"The update archive %@, and the %@. At least one method of signature verification must be valid. The update will be rejected.", dsaStatus, acsStatus); return NO; } NSError *error = nil; if (![SUCodeSigningVerifier codeSignatureMatchesHostAndIsValidAtPath:installSourcePath error:&error]) { SULog(@"The update archive %@, and the app is signed with a new Code Signing identity that doesn't match code signing of the original app: %@. At least one method of signature verification must be valid. The update will be rejected.", dsaStatus, error); return NO; } } return YES; } - (void)downloadDidFinish:(NSURLDownload *)__unused d { assert(self.updateItem); [self extractUpdate]; } - (void)download:(NSURLDownload *)__unused download didFailWithError:(NSError *)error { NSURL *failingUrl = [error.userInfo objectForKey:NSURLErrorFailingURLErrorKey]; if (!failingUrl) { failingUrl = [self.updateItem fileURL]; } if ([[self.updater delegate] respondsToSelector:@selector(updater:failedToDownloadUpdate:error:)]) { [[self.updater delegate] updater:self.updater failedToDownloadUpdate:self.updateItem error:error]; } NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while downloading the update. Please try again later.", nil), NSUnderlyingErrorKey: error, }]; if (failingUrl) { [userInfo setObject:failingUrl forKey:NSURLErrorFailingURLErrorKey]; } [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUDownloadError userInfo:userInfo]]; } - (BOOL)download:(NSURLDownload *)__unused download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType { // We don't want the download system to extract our gzips. // Note that we use a substring matching here instead of direct comparison because the docs say "application/gzip" but the system *uses* "application/x-gzip". This is a documentation bug. return ([encodingType rangeOfString:@"gzip"].location == NSNotFound); } - (void)extractUpdate { SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:self.downloadPath updatingHostBundlePath:[[self.host bundle] bundlePath] withPassword:self.updater.decryptionPassword]; if (!unarchiver) { SULog(@"Error: No valid unarchiver for %@!", self.downloadPath); [self unarchiverDidFail:nil]; return; } unarchiver.delegate = self; [unarchiver start]; } - (void)failedToApplyDeltaUpdate { // When a delta update fails to apply we fall back on updating via a full install. self.updateItem = self.nonDeltaUpdateItem; self.nonDeltaUpdateItem = nil; [self downloadUpdate]; } - (void)unarchiverDidFinish:(SUUnarchiver *)__unused ua { assert(self.updateItem); [self installWithToolAndRelaunch:YES]; } - (void)unarchiverDidFail:(SUUnarchiver *)__unused ua { if ([self.updateItem isDeltaUpdate]) { [self failedToApplyDeltaUpdate]; return; } [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil) }]]; } - (void)installWithToolAndRelaunch:(BOOL)relaunch { // Perhaps a poor assumption but: if we're not relaunching, we assume we shouldn't be showing any UI either. Because non-relaunching installations are kicked off without any user interaction, we shouldn't be interrupting them. [self installWithToolAndRelaunch:relaunch displayingUserInterface:relaunch]; } // Creates intermediate directories up until targetPath if they don't already exist, // and removes the directory at targetPath if one already exists there - (BOOL)preparePathForRelaunchTool:(NSString *)targetPath error:(NSError * __autoreleasing *)error { NSFileManager *fileManager = [[NSFileManager alloc] init]; if ([fileManager fileExistsAtPath:targetPath]) { NSError *removeError = nil; if (![fileManager removeItemAtPath:targetPath error:&removeError]) { if (error != NULL) { *error = removeError; } return NO; } } else { NSError *createDirectoryError = nil; if (![fileManager createDirectoryAtPath:[targetPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:@{} error:&createDirectoryError]) { if (error != NULL) { *error = createDirectoryError; } return NO; } } return YES; } - (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL)showUI { assert(self.updateItem); if (![self validateUpdateDownloadedToPath:self.downloadPath extractedToPath:self.tempDir DSASignature:self.updateItem.DSASignature publicDSAKey:self.host.publicDSAKey]) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil), NSLocalizedFailureReasonErrorKey: SULocalizedString(@"The update is improperly signed.", nil), }; [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUSignatureError userInfo:userInfo]]; return; } if (![self.updater mayUpdateAndRestart]) { [self abortUpdate]; return; } // Give the host app an opportunity to postpone the install and relaunch. static BOOL postponedOnce = NO; id updaterDelegate = [self.updater delegate]; if (!postponedOnce && [updaterDelegate respondsToSelector:@selector(updater:shouldPostponeRelaunchForUpdate:untilInvoking:)]) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(installWithToolAndRelaunch:)]]; [invocation setSelector:@selector(installWithToolAndRelaunch:)]; [invocation setArgument:&relaunch atIndex:2]; [invocation setTarget:self]; postponedOnce = YES; if ([updaterDelegate updater:self.updater shouldPostponeRelaunchForUpdate:self.updateItem untilInvoking:invocation]) { return; } } if ([updaterDelegate respondsToSelector:@selector(updater:willInstallUpdate:)]) { [updaterDelegate updater:self.updater willInstallUpdate:self.updateItem]; } NSBundle *sparkleBundle = self.updater.sparkleBundle; // Copy the relauncher into a temporary directory so we can get to it after the new version's installed. // Only the paranoid survive: if there's already a stray copy of relaunch there, we would have problems. NSString *const relaunchToolName = @"" SPARKLE_RELAUNCH_TOOL_NAME; NSString *const relaunchPathToCopy = [sparkleBundle pathForResource:relaunchToolName ofType:@"app"]; if (relaunchPathToCopy != nil) { NSString *targetPath = [[self appCachePath] stringByAppendingPathComponent:[relaunchPathToCopy lastPathComponent]]; SUFileManager *fileManager = [SUFileManager defaultManager]; NSError *error = nil; NSURL *relaunchURLToCopy = [NSURL fileURLWithPath:relaunchPathToCopy]; NSURL *targetURL = [NSURL fileURLWithPath:targetPath]; // We only need to run our copy of the app by spawning a task // Since we are copying the app to a directory that is write-accessible, we don't need to muck with owner/group IDs if ([self preparePathForRelaunchTool:targetPath error:&error] && [fileManager copyItemAtURL:relaunchURLToCopy toURL:targetURL error:&error]) { // We probably don't need to release the quarantine, but we'll do it just in case it's necessary. // Perhaps in a sandboxed environment this matters more. Note that this may not be a fatal error. NSError *quarantineError = nil; if (![fileManager releaseItemFromQuarantineAtRootURL:targetURL error:&quarantineError]) { SULog(@"Failed to release quarantine on %@ with error %@", targetPath, quarantineError); } self.relaunchPath = targetPath; } else { [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Couldn't copy relauncher (%@) to temporary path (%@)! %@", relaunchPathToCopy, targetPath, (error ? [error localizedDescription] : @"")] }]]; } } [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterWillRestartNotification object:self]; if ([updaterDelegate respondsToSelector:@selector(updaterWillRelaunchApplication:)]) [updaterDelegate updaterWillRelaunchApplication:self.updater]; NSString *relaunchToolPath = [[NSBundle bundleWithPath:self.relaunchPath] executablePath]; if (!relaunchToolPath || ![[NSFileManager defaultManager] fileExistsAtPath:self.relaunchPath]) { // Note that we explicitly use the host app's name here, since updating plugin for Mail relaunches Mail, not just the plugin. [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURelaunchError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@.", nil), [self.host name]], NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Couldn't find the relauncher (expected to find it at %@)", self.relaunchPath] }]]; // We intentionally don't abandon the update here so that the host won't initiate another. return; } NSString *pathToRelaunch = [self.host bundlePath]; if ([updaterDelegate respondsToSelector:@selector(pathToRelaunchForUpdater:)]) { pathToRelaunch = [updaterDelegate pathToRelaunchForUpdater:self.updater]; } [NSTask launchedTaskWithLaunchPath:relaunchToolPath arguments:@[[self.host bundlePath], pathToRelaunch, [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]], self.tempDir, relaunch ? @"1" : @"0", showUI ? @"1" : @"0"]]; [self terminateApp]; } // Note: this is overridden by the automatic update driver to not terminate in some cases - (void)terminateApp { [NSApp terminate:self]; } - (void)cleanUpDownload { if (self.tempDir != nil) // tempDir contains downloadPath, so we implicitly delete both here. { BOOL success = NO; NSError *error = nil; success = [[NSFileManager defaultManager] removeItemAtPath:self.tempDir error:&error]; // Clean up the copied relauncher if (!success) [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[self.tempDir stringByDeletingLastPathComponent] destination:@"" files:@[[self.tempDir lastPathComponent]] tag:NULL]; } } - (void)installerForHost:(SUHost *)aHost failedWithError:(NSError *)error { if (aHost != self.host) { return; } NSError *dontThrow = nil; [[NSFileManager defaultManager] removeItemAtPath:self.relaunchPath error:&dontThrow]; // Clean up the copied relauncher [self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:@{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while installing the update. Please try again later.", nil), NSLocalizedFailureReasonErrorKey: [error localizedDescription], NSUnderlyingErrorKey: error, }]]; } - (void)abortUpdate { [self cleanUpDownload]; [[NSNotificationCenter defaultCenter] removeObserver:self]; self.updateItem = nil; [super abortUpdate]; } - (void)abortUpdateWithError:(NSError *)error { if ([error code] != SUNoUpdateError) { // Let's not bother logging this. NSError *errorToDisplay = error; int finiteRecursion=5; do { SULog(@"Error: %@ %@ (URL %@)", errorToDisplay.localizedDescription, errorToDisplay.localizedFailureReason, [errorToDisplay.userInfo objectForKey:NSURLErrorFailingURLErrorKey]); errorToDisplay = [errorToDisplay.userInfo objectForKey:NSUnderlyingErrorKey]; } while(--finiteRecursion && errorToDisplay); } if (self.download) { [self.download cancel]; } // Notify host app that update has aborted id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updater:didAbortWithError:)]) { [updaterDelegate updater:self.updater didAbortWithError:error]; } [self abortUpdate]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaApply.h ================================================ // // SUBinaryDeltaApply.h // Sparkle // // Created by Mark Rowe on 2009-06-01. // Copyright 2009 Mark Rowe. All rights reserved. // #ifndef SUBINARYDELTAAPPLY_H #define SUBINARYDELTAAPPLY_H @class NSString; BOOL applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, BOOL verbose, NSError * __autoreleasing *error); #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaApply.m ================================================ // // SUBinaryDeltaApply.m // Sparkle // // Created by Mark Rowe on 2009-06-01. // Copyright 2009 Mark Rowe. All rights reserved. // #import "SUBinaryDeltaApply.h" #import "SUBinaryDeltaCommon.h" #include #import #include "bspatch.h" #include #include #include static BOOL applyBinaryDeltaToFile(xar_t x, xar_file_t file, NSString *sourceFilePath, NSString *destinationFilePath) { NSString *patchFile = temporaryFilename(@"apply-binary-delta"); xar_extract_tofile(x, file, [patchFile fileSystemRepresentation]); const char *argv[] = {"/usr/bin/bspatch", [sourceFilePath fileSystemRepresentation], [destinationFilePath fileSystemRepresentation], [patchFile fileSystemRepresentation]}; BOOL success = (bspatch(4, argv) == 0); unlink([patchFile fileSystemRepresentation]); return success; } BOOL applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, BOOL verbose, NSError * __autoreleasing *error) { xar_t x = xar_open([patchFile fileSystemRepresentation], READ); if (!x) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to open %@. Giving up.", patchFile] }]; } return NO; } SUBinaryDeltaMajorVersion majorDiffVersion = FIRST_DELTA_DIFF_MAJOR_VERSION; SUBinaryDeltaMinorVersion minorDiffVersion = FIRST_DELTA_DIFF_MINOR_VERSION; NSString *expectedBeforeHashv1 = nil; NSString *expectedAfterHashv1 = nil; NSString *expectedNewBeforeHash = nil; NSString *expectedNewAfterHash = nil; xar_subdoc_t subdoc; for (subdoc = xar_subdoc_first(x); subdoc; subdoc = xar_subdoc_next(subdoc)) { if (!strcmp(xar_subdoc_name(subdoc), BINARY_DELTA_ATTRIBUTES_KEY)) { const char *value = 0; // available in version 2.0 or later xar_subdoc_prop_get(subdoc, MAJOR_DIFF_VERSION_KEY, &value); if (value) majorDiffVersion = (SUBinaryDeltaMajorVersion)[@(value) intValue]; // available in version 2.0 or later xar_subdoc_prop_get(subdoc, MINOR_DIFF_VERSION_KEY, &value); if (value) minorDiffVersion = (SUBinaryDeltaMinorVersion)[@(value) intValue]; // available in version 2.0 or later xar_subdoc_prop_get(subdoc, BEFORE_TREE_SHA1_KEY, &value); if (value) expectedNewBeforeHash = @(value); // available in version 2.0 or later xar_subdoc_prop_get(subdoc, AFTER_TREE_SHA1_KEY, &value); if (value) expectedNewAfterHash = @(value); // only available in version 1.0 xar_subdoc_prop_get(subdoc, BEFORE_TREE_SHA1_OLD_KEY, &value); if (value) expectedBeforeHashv1 = @(value); // only available in version 1.0 xar_subdoc_prop_get(subdoc, AFTER_TREE_SHA1_OLD_KEY, &value); if (value) expectedAfterHashv1 = @(value); } } if (majorDiffVersion < FIRST_DELTA_DIFF_MAJOR_VERSION) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadCorruptFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to identify diff-version %u in delta. Giving up.", majorDiffVersion] }]; } return NO; } if (majorDiffVersion > LATEST_DELTA_DIFF_MAJOR_VERSION) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"A later version is needed to apply this patch (on major version %u, but patch requests version %u).", LATEST_DELTA_DIFF_MAJOR_VERSION, majorDiffVersion] }]; } return NO; } BOOL usesNewTreeHash = MAJOR_VERSION_IS_AT_LEAST(majorDiffVersion, SUBeigeMajorVersion); NSString *expectedBeforeHash = usesNewTreeHash ? expectedNewBeforeHash : expectedBeforeHashv1; NSString *expectedAfterHash = usesNewTreeHash ? expectedNewAfterHash : expectedAfterHashv1; if (!expectedBeforeHash || !expectedAfterHash) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadCorruptFileError userInfo:@{ NSLocalizedDescriptionKey: @"Unable to find before-sha1 or after-sha1 metadata in delta. Giving up." }]; } return NO; } if (verbose) { fprintf(stderr, "Applying version %u.%u patch...\n", majorDiffVersion, minorDiffVersion); fprintf(stderr, "Verifying source..."); } NSString *beforeHash = hashOfTreeWithVersion(source, majorDiffVersion); if (!beforeHash) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to calculate hash of tree %@", source] }]; } return NO; } if (![beforeHash isEqualToString:expectedBeforeHash]) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Source doesn't have expected hash (%@ != %@). Giving up.", expectedBeforeHash, beforeHash] }]; } return NO; } if (verbose) { fprintf(stderr, "\nCopying files..."); } if (!removeTree(destination)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to remove %@", destination] }]; } return NO; } if (!copyTree(source, destination)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to copy %@ to %@", source, destination] }]; } return NO; } BOOL hasExtractKeyAvailable = MAJOR_VERSION_IS_AT_LEAST(majorDiffVersion, SUBeigeMajorVersion); if (verbose) { fprintf(stderr, "\nPatching..."); } NSFileManager *fileManager = [[NSFileManager alloc] init]; xar_file_t file; xar_iter_t iter = xar_iter_new(); for (file = xar_file_first(x, iter); file; file = xar_file_next(iter)) { NSString *path = @(xar_get_path(file)); NSString *sourceFilePath = [source stringByAppendingPathComponent:path]; NSString *destinationFilePath = [destination stringByAppendingPathComponent:path]; // Don't use -[NSFileManager fileExistsAtPath:] because it will follow symbolic links BOOL fileExisted = verbose && [fileManager attributesOfItemAtPath:destinationFilePath error:nil]; BOOL removedFile = NO; const char *value; if (!xar_prop_get(file, DELETE_KEY, &value) || (!hasExtractKeyAvailable && !xar_prop_get(file, DELETE_THEN_EXTRACT_OLD_KEY, &value))) { if (!removeTree(destinationFilePath)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%@ or %@: failed to remove %@", @DELETE_KEY, @DELETE_THEN_EXTRACT_OLD_KEY, destination] }]; } return NO; } if (!hasExtractKeyAvailable && !xar_prop_get(file, DELETE_KEY, &value)) { if (verbose) { fprintf(stderr, "\n❌ %s %s", VERBOSE_DELETED, [path fileSystemRepresentation]); } continue; } removedFile = YES; } if (!xar_prop_get(file, BINARY_DELTA_KEY, &value)) { if (!applyBinaryDeltaToFile(x, file, sourceFilePath, destinationFilePath)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to patch %@ to destination %@", sourceFilePath, destinationFilePath] }]; } return NO; } if (verbose) { fprintf(stderr, "\n🔨 %s %s", VERBOSE_PATCHED, [path fileSystemRepresentation]); } } else if ((hasExtractKeyAvailable && !xar_prop_get(file, EXTRACT_KEY, &value)) || (!hasExtractKeyAvailable && xar_prop_get(file, MODIFY_PERMISSIONS_KEY, &value))) { // extract and permission modifications don't coexist if (xar_extract_tofile(x, file, [destinationFilePath fileSystemRepresentation]) != 0) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to extract file to %@", destinationFilePath] }]; } return NO; } if (verbose) { if (fileExisted) { fprintf(stderr, "\n✏️ %s %s", VERBOSE_UPDATED, [path fileSystemRepresentation]); } else { fprintf(stderr, "\n✅ %s %s", VERBOSE_ADDED, [path fileSystemRepresentation]); } } } else if (verbose && removedFile) { fprintf(stderr, "\n❌ %s %s", VERBOSE_DELETED, [path fileSystemRepresentation]); } if (!xar_prop_get(file, MODIFY_PERMISSIONS_KEY, &value)) { mode_t mode = (mode_t)[[NSString stringWithUTF8String:value] intValue]; if (!modifyPermissions(destinationFilePath, mode)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteNoPermissionError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to modify permissions (%@) on file %@", @(value), destinationFilePath] }]; } return NO; } if (verbose) { fprintf(stderr, "\n👮 %s %s (0%o)", VERBOSE_MODIFIED, [path fileSystemRepresentation], mode); } } } xar_close(x); if (verbose) { fprintf(stderr, "\nVerifying destination..."); } NSString *afterHash = hashOfTreeWithVersion(destination, majorDiffVersion); if (!afterHash) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unable to calculate hash of tree %@", destination] }]; } return NO; } if (![afterHash isEqualToString:expectedAfterHash]) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination doesn't have expected hash (%@ != %@). Giving up.", expectedAfterHash, afterHash] }]; } removeTree(destination); return NO; } if (verbose) { fprintf(stderr, "\nDone!\n"); } return YES; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaCommon.h ================================================ // // SUBinaryDeltaCommon.h // Sparkle // // Created by Mark Rowe on 2009-06-01. // Copyright 2009 Mark Rowe. All rights reserved. // #ifndef SUBINARYDELTACOMMON_H #define SUBINARYDELTACOMMON_H #import #include #define PERMISSION_FLAGS (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) #define IS_VALID_PERMISSIONS(mode) \ (((mode & PERMISSION_FLAGS) == 0755) || ((mode & PERMISSION_FLAGS) == 0644)) #define APPLE_CODE_SIGN_XATTR_CODE_DIRECTORY_KEY "com.apple.cs.CodeDirectory" #define APPLE_CODE_SIGN_XATTR_CODE_REQUIREMENTS_KEY "com.apple.cs.CodeRequirements" #define APPLE_CODE_SIGN_XATTR_CODE_SIGNATURE_KEY "com.apple.cs.CodeSignature" #define BINARY_DELTA_ATTRIBUTES_KEY "binary-delta-attributes" #define MAJOR_DIFF_VERSION_KEY "major-version" #define MINOR_DIFF_VERSION_KEY "minor-version" #define BEFORE_TREE_SHA1_KEY "before-tree-sha1" #define AFTER_TREE_SHA1_KEY "after-tree-sha1" #define DELETE_KEY "delete" #define EXTRACT_KEY "extract" #define BINARY_DELTA_KEY "binary-delta" #define MODIFY_PERMISSIONS_KEY "mod-permissions" // Properties no longer used in new patches #define DELETE_THEN_EXTRACT_OLD_KEY "delete-then-extract" #define BEFORE_TREE_SHA1_OLD_KEY "before-sha1" #define AFTER_TREE_SHA1_OLD_KEY "after-sha1" #define VERBOSE_DELETED "Deleted" // file is deleted from the file system when applying a patch #define VERBOSE_REMOVED "Removed" // file is set to be removed when creating a patch #define VERBOSE_ADDED "Added" // file is added to the patch or file system #define VERBOSE_DIFFED "Diffed" // file is diffed when creating a patch #define VERBOSE_PATCHED "Patched" // file is patched when applying a patch #define VERBOSE_UPDATED "Updated" // file's contents are updated #define VERBOSE_MODIFIED "Modified" // file's metadata is modified #define MAJOR_VERSION_IS_AT_LEAST(actualMajor, expectedMajor) (actualMajor >= expectedMajor) // Each major version will be assigned a name of a color // Changes that break backwards compatibility will have different major versions // Changes that affect creating but not applying patches will have different minor versions typedef NS_ENUM(uint16_t, SUBinaryDeltaMajorVersion) { SUAzureMajorVersion = 1, SUBeigeMajorVersion = 2 }; // Only keep track of the latest minor version for each major version typedef NS_ENUM(uint16_t, SUBinaryDeltaMinorVersion) { SUAzureMinorVersion = 1, SUBeigeMinorVersion = 1, }; #define FIRST_DELTA_DIFF_MAJOR_VERSION SUAzureMajorVersion #define FIRST_DELTA_DIFF_MINOR_VERSION ((SUBinaryDeltaMinorVersion)0) #define LATEST_DELTA_DIFF_MAJOR_VERSION SUBeigeMajorVersion extern int compareFiles(const FTSENT **a, const FTSENT **b); extern NSData *hashOfFileContents(FTSENT *ent); extern NSString *hashOfTreeWithVersion(NSString *path, uint16_t majorVersion); extern NSString *hashOfTree(NSString *path); extern BOOL removeTree(NSString *path); extern BOOL copyTree(NSString *source, NSString *dest); extern BOOL modifyPermissions(NSString *path, mode_t desiredPermissions); extern NSString *pathRelativeToDirectory(NSString *directory, NSString *path); NSString *temporaryFilename(NSString *base); NSString *temporaryDirectory(NSString *base); NSString *stringWithFileSystemRepresentation(const char*); SUBinaryDeltaMinorVersion latestMinorVersionForMajorVersion(SUBinaryDeltaMajorVersion majorVersion); #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaCommon.m ================================================ // // SUBinaryDeltaCommon.m // Sparkle // // Created by Mark Rowe on 2009-06-01. // Copyright 2009 Mark Rowe. All rights reserved. // #include "SUBinaryDeltaCommon.h" #import "SUFileManager.h" #include #include #include #include #include #include #include #include #include int compareFiles(const FTSENT **a, const FTSENT **b) { return strcoll((*a)->fts_name, (*b)->fts_name); } NSString *pathRelativeToDirectory(NSString *directory, NSString *path) { NSUInteger directoryLength = [directory length]; if ([path hasPrefix:directory]) return [path substringFromIndex:directoryLength]; return path; } NSString *stringWithFileSystemRepresentation(const char *input) { return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:input length:strlen(input)]; } SUBinaryDeltaMinorVersion latestMinorVersionForMajorVersion(SUBinaryDeltaMajorVersion majorVersion) { switch (majorVersion) { case SUAzureMajorVersion: return SUAzureMinorVersion; case SUBeigeMajorVersion: return SUBeigeMinorVersion; } return (SUBinaryDeltaMinorVersion)0; } NSString *temporaryFilename(NSString *base) { NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]]; NSMutableData *data = [NSMutableData data]; [data appendBytes:template.fileSystemRepresentation length:strlen(template.fileSystemRepresentation) + 1]; char *buffer = data.mutableBytes; int fd = mkstemp(buffer); if (fd == -1) { perror("mkstemp"); return nil; } if (close(fd) != 0) { perror("close"); return nil; } return stringWithFileSystemRepresentation(buffer); } NSString *temporaryDirectory(NSString *base) { NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]]; NSMutableData *data = [NSMutableData data]; [data appendBytes:template.fileSystemRepresentation length:strlen(template.fileSystemRepresentation) + 1]; char *buffer = data.mutableBytes; char *templateResult = mkdtemp(buffer); if (templateResult == NULL) { perror("mkdtemp"); return nil; } return stringWithFileSystemRepresentation(templateResult); } static void _hashOfBuffer(unsigned char *hash, const char* buffer, ssize_t bufferLength) { assert(bufferLength >= 0 && bufferLength <= UINT32_MAX); CC_SHA1_CTX hashContext; CC_SHA1_Init(&hashContext); CC_SHA1_Update(&hashContext, buffer, (CC_LONG)bufferLength); CC_SHA1_Final(hash, &hashContext); } static BOOL _hashOfFileContents(unsigned char* hash, FTSENT *ent) { if (ent->fts_info == FTS_SL) { char linkDestination[MAXPATHLEN + 1]; ssize_t linkDestinationLength = readlink(ent->fts_path, linkDestination, MAXPATHLEN); if (linkDestinationLength < 0) { perror("readlink"); return NO; } _hashOfBuffer(hash, linkDestination, linkDestinationLength); } else if (ent->fts_info == FTS_F) { int fileDescriptor = open(ent->fts_path, O_RDONLY); if (fileDescriptor == -1) { perror("open"); return NO; } ssize_t fileSize = ent->fts_statp->st_size; if (fileSize == 0) { _hashOfBuffer(hash, NULL, 0); } else { void *buffer = mmap(0, (size_t)fileSize, PROT_READ, MAP_FILE | MAP_PRIVATE, fileDescriptor, 0); if (buffer == (void*)-1) { close(fileDescriptor); perror("mmap"); return NO; } _hashOfBuffer(hash, buffer, fileSize); munmap(buffer, (size_t)fileSize); } close(fileDescriptor); } else if (ent->fts_info == FTS_D) { memset(hash, 0xdd, CC_SHA1_DIGEST_LENGTH); } else { return NO; } return YES; } NSData *hashOfFileContents(FTSENT *ent) { unsigned char fileHash[CC_SHA1_DIGEST_LENGTH]; if (!_hashOfFileContents(fileHash, ent)) { return nil; } return [NSData dataWithBytes:fileHash length:CC_SHA1_DIGEST_LENGTH]; } NSString *hashOfTreeWithVersion(NSString *path, uint16_t majorVersion) { char pathBuffer[PATH_MAX] = {0}; if (![path getFileSystemRepresentation:pathBuffer maxLength:sizeof(pathBuffer)]) { return nil; } char * const sourcePaths[] = {pathBuffer, 0}; FTS *fts = fts_open(sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles); if (!fts) { perror("fts_open"); return nil; } CC_SHA1_CTX hashContext; CC_SHA1_Init(&hashContext); FTSENT *ent = 0; while ((ent = fts_read(fts))) { if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D) continue; if (ent->fts_info == FTS_D && !MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) { continue; } NSString *relativePath = pathRelativeToDirectory(path, stringWithFileSystemRepresentation(ent->fts_path)); if (relativePath.length == 0) continue; unsigned char fileHash[CC_SHA1_DIGEST_LENGTH]; if (!_hashOfFileContents(fileHash, ent)) { return nil; } CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash)); const char *relativePathBytes = [relativePath fileSystemRepresentation]; CC_SHA1_Update(&hashContext, relativePathBytes, (CC_LONG)strlen(relativePathBytes)); if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) { uint16_t mode = ent->fts_statp->st_mode; uint16_t type = ent->fts_info; uint16_t permissions = mode & PERMISSION_FLAGS; CC_SHA1_Update(&hashContext, &type, sizeof(type)); CC_SHA1_Update(&hashContext, &permissions, sizeof(permissions)); } } fts_close(fts); unsigned char hash[CC_SHA1_DIGEST_LENGTH]; CC_SHA1_Final(hash, &hashContext); char hexHash[CC_SHA1_DIGEST_LENGTH * 2 + 1]; size_t i; for (i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) sprintf(hexHash + i * 2, "%02x", hash[i]); return @(hexHash); } extern NSString *hashOfTree(NSString *path) { return hashOfTreeWithVersion(path, LATEST_DELTA_DIFF_MAJOR_VERSION); } BOOL removeTree(NSString *path) { NSFileManager *fileManager = [NSFileManager defaultManager]; // Don't use fileExistsForPath: because it will try to follow symbolic links if (![fileManager attributesOfItemAtPath:path error:nil]) { return YES; } return [fileManager removeItemAtPath:path error:nil]; } BOOL copyTree(NSString *source, NSString *dest) { return [[SUFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:source] toURL:[NSURL fileURLWithPath:dest] error:NULL]; } BOOL modifyPermissions(NSString *path, mode_t desiredPermissions) { NSFileManager *fileManager = [NSFileManager defaultManager]; NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:nil]; if (!attributes) { return NO; } NSNumber *permissions = [attributes objectForKey:NSFilePosixPermissions]; if (!permissions) { return NO; } mode_t newMode = ([permissions unsignedShortValue] & ~PERMISSION_FLAGS) | desiredPermissions; int (*changeModeFunc)(const char *, mode_t) = [[attributes objectForKey:NSFileType] isEqualToString:NSFileTypeSymbolicLink] ? lchmod : chmod; if (changeModeFunc([path fileSystemRepresentation], newMode) != 0) { return NO; } return YES; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaCreate.h ================================================ // // SUBinaryDeltaCreate.m // Sparkle // // Created by Mayur Pawashe on 4/9/15. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #ifndef SUBINARYDELTACREATE_H #define SUBINARYDELTACREATE_H #import "SUBinaryDeltaCommon.h" @class NSString; BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, SUBinaryDeltaMajorVersion majorVersion, BOOL verbose, NSError * __autoreleasing *error); #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaCreate.m ================================================ // // SUBinaryDeltaCreate.m // Sparkle // // Created by Mayur Pawashe on 4/9/15. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import "SUBinaryDeltaCreate.h" #import #include "SUBinaryDeltaCommon.h" #import #include #include #include #include #include #include #include #include #include #include extern int bsdiff(int argc, const char **argv); @interface CreateBinaryDeltaOperation : NSOperation @property (copy) NSString *relativePath; @property (strong) NSString *resultPath; @property (strong) NSNumber *oldPermissions; @property (strong) NSNumber *permissions; @property (strong) NSString *_fromPath; @property (strong) NSString *_toPath; - (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree oldPermissions:(NSNumber *)oldPermissions newPermissions:(NSNumber *)permissions; @end @implementation CreateBinaryDeltaOperation @synthesize relativePath = _relativePath; @synthesize resultPath = _resultPath; @synthesize oldPermissions = _oldPermissions; @synthesize permissions = _permissions; @synthesize _fromPath = _fromPath; @synthesize _toPath = _toPath; - (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree oldPermissions:(NSNumber *)oldPermissions newPermissions:(NSNumber *)permissions { if ((self = [super init])) { self.relativePath = relativePath; self.oldPermissions = oldPermissions; self.permissions = permissions; self._fromPath = [oldTree stringByAppendingPathComponent:relativePath]; self._toPath = [newTree stringByAppendingPathComponent:relativePath]; } return self; } - (void)main { NSString *temporaryFile = temporaryFilename(@"BinaryDelta"); const char *argv[] = {"/usr/bin/bsdiff", [self._fromPath fileSystemRepresentation], [self._toPath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]}; int result = bsdiff(4, argv); if (!result) self.resultPath = temporaryFile; } @end #define INFO_HASH_KEY @"hash" #define INFO_TYPE_KEY @"type" #define INFO_PERMISSIONS_KEY @"permissions" #define INFO_SIZE_KEY @"size" static NSDictionary *infoForFile(FTSENT *ent) { NSData *hash = hashOfFileContents(ent); if (!hash) { return nil; } off_t size = (ent->fts_info != FTS_D) ? ent->fts_statp->st_size : 0; assert(ent->fts_statp != NULL); mode_t permissions = ent->fts_statp->st_mode & PERMISSION_FLAGS; return @{INFO_HASH_KEY: hash, INFO_TYPE_KEY: @(ent->fts_info), INFO_PERMISSIONS_KEY : @(permissions), INFO_SIZE_KEY: @(size)}; } static bool aclExists(const FTSENT *ent) { // macOS does not currently support ACLs for symlinks if (ent->fts_info == FTS_SL) { return NO; } acl_t acl = acl_get_link_np(ent->fts_path, ACL_TYPE_EXTENDED); if (acl != NULL) { acl_entry_t entry; int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); assert(acl_free((void *)acl) == 0); return (result == 0); } return false; } static bool codeSignatureExtendedAttributeExists(const FTSENT *ent) { const int options = XATTR_NOFOLLOW; ssize_t listSize = listxattr(ent->fts_path, NULL, 0, options); if (listSize == -1) { return false; } char *buffer = malloc((size_t)listSize); assert(buffer != NULL); ssize_t sizeBack = listxattr(ent->fts_path, buffer, (size_t)listSize, options); assert(sizeBack == listSize); size_t startCharacterIndex = 0; for (size_t characterIndex = 0; characterIndex < (size_t)listSize; characterIndex++) { if (buffer[characterIndex] == '\0') { char *attribute = &buffer[startCharacterIndex]; size_t length = characterIndex - startCharacterIndex; if (strncmp(APPLE_CODE_SIGN_XATTR_CODE_DIRECTORY_KEY, attribute, length) == 0 || strncmp(APPLE_CODE_SIGN_XATTR_CODE_REQUIREMENTS_KEY, attribute, length) == 0 || strncmp(APPLE_CODE_SIGN_XATTR_CODE_SIGNATURE_KEY, attribute, length) == 0) { free(buffer); return true; } startCharacterIndex = characterIndex + 1; } } free(buffer); return false; } static NSString *absolutePath(NSString *path) { NSURL *url = [[NSURL alloc] initFileURLWithPath:path]; return [[url absoluteURL] path]; } static NSString *temporaryPatchFile(NSString *patchFile) { NSString *path = absolutePath(patchFile); NSString *directory = [path stringByDeletingLastPathComponent]; NSString *file = [path lastPathComponent]; return [NSString stringWithFormat:@"%@/.%@.tmp", directory, file]; } #define MIN_FILE_SIZE_FOR_CREATING_DELTA 4096 static BOOL shouldSkipDeltaCompression(NSDictionary* originalInfo, NSDictionary *newInfo) { unsigned long long fileSize = [newInfo[INFO_SIZE_KEY] unsignedLongLongValue]; if (fileSize < MIN_FILE_SIZE_FOR_CREATING_DELTA) { return YES; } if (!originalInfo) { return YES; } if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) { return YES; } if ([originalInfo[INFO_HASH_KEY] isEqual:newInfo[INFO_HASH_KEY]]) { // this is possible if just the permissions have changed return YES; } return NO; } static BOOL shouldDeleteThenExtract(NSDictionary* originalInfo, NSDictionary *newInfo) { if (!originalInfo) { return NO; } if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) { return YES; } return NO; } static BOOL shouldSkipExtracting(NSDictionary *originalInfo, NSDictionary *newInfo) { if (!originalInfo) { return NO; } if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) { return NO; } if (![originalInfo[INFO_HASH_KEY] isEqual:newInfo[INFO_HASH_KEY]]) { return NO; } return YES; } static BOOL shouldChangePermissions(NSDictionary *originalInfo, NSDictionary *newInfo) { if (!originalInfo) { return NO; } if ([originalInfo[INFO_TYPE_KEY] unsignedShortValue] != [newInfo[INFO_TYPE_KEY] unsignedShortValue]) { return NO; } if ([originalInfo[INFO_PERMISSIONS_KEY] unsignedShortValue] == [newInfo[INFO_PERMISSIONS_KEY] unsignedShortValue]) { return NO; } return YES; } BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchFile, SUBinaryDeltaMajorVersion majorVersion, BOOL verbose, NSError * __autoreleasing *error) { assert(source); assert(destination); assert(patchFile); assert(majorVersion >= FIRST_DELTA_DIFF_MAJOR_VERSION && majorVersion <= LATEST_DELTA_DIFF_MAJOR_VERSION); SUBinaryDeltaMinorVersion minorVersion = latestMinorVersionForMajorVersion(majorVersion); NSMutableDictionary *originalTreeState = [NSMutableDictionary dictionary]; char pathBuffer[PATH_MAX] = {0}; if (![source getFileSystemRepresentation:pathBuffer maxLength:sizeof(pathBuffer)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to retrieve file system path representation from source %@", source] }]; } return NO; } char *sourcePaths[] = {pathBuffer, 0}; FTS *fts = fts_open(sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles); if (!fts) { if (error != NULL) { *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"fts_open failed on source: %@", @(strerror(errno))] }]; } return NO; } if (verbose) { fprintf(stderr, "Creating version %u.%u patch...\n", majorVersion, minorVersion); fprintf(stderr, "Processing %s...", [source fileSystemRepresentation]); } FTSENT *ent = 0; while ((ent = fts_read(fts))) { if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D) { continue; } NSString *key = pathRelativeToDirectory(source, stringWithFileSystemRepresentation(ent->fts_path)); if (![key length]) { continue; } NSDictionary *info = infoForFile(ent); if (!info) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to retrieve info for file %@", @(ent->fts_path)] }]; } return NO; } originalTreeState[key] = info; if (aclExists(ent)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Diffing ACLs are not supported. Detected ACL in before-tree on file %@", @(ent->fts_path)] }]; } return NO; } if (codeSignatureExtendedAttributeExists(ent)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Diffing code signed extended attributes are not supported. Detected extended attribute in before-tree on file %@", @(ent->fts_path)] }]; } return NO; } } fts_close(fts); NSString *beforeHash = hashOfTreeWithVersion(source, majorVersion); if (!beforeHash) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to generate hash for tree %@", source] }]; } return NO; } NSMutableDictionary *newTreeState = [NSMutableDictionary dictionary]; for (NSString *key in originalTreeState) { newTreeState[key] = [NSNull null]; } if (verbose) { fprintf(stderr, "\nProcessing %s...", [destination fileSystemRepresentation]); } pathBuffer[0] = 0; if (![destination getFileSystemRepresentation:pathBuffer maxLength:sizeof(pathBuffer)]) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to retrieve file system path representation from destination %@", destination] }]; } return NO; } sourcePaths[0] = pathBuffer; fts = fts_open(sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles); if (!fts) { if (error != NULL) { *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"fts_open failed on destination: %@", @(strerror(errno))] }]; } return NO; } while ((ent = fts_read(fts))) { if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D) { continue; } NSString *key = pathRelativeToDirectory(destination, stringWithFileSystemRepresentation(ent->fts_path)); if (![key length]) { continue; } NSDictionary *info = infoForFile(ent); if (!info) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to retrieve info from file %@", @(ent->fts_path)] }]; } return NO; } // We should validate permissions and ACLs even if we don't store the info in the diff in the case of ACLs, // or in the case of permissions if the patch version is 1 // We should also not allow files with code signed extended attributes since Apple doesn't recommend inserting these // inside an application, and since we don't preserve extended attribitutes anyway mode_t permissions = [info[INFO_PERMISSIONS_KEY] unsignedShortValue]; if (!IS_VALID_PERMISSIONS(permissions)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Invalid file permissions after-tree on file %@ (only permissions with modes 0755 and 0644 are supported)", @(ent->fts_path)] }]; } return NO; } if (aclExists(ent)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Diffing ACLs are not supported. Detected ACL in after-tree on file %@", @(ent->fts_path)] }]; } return NO; } if (codeSignatureExtendedAttributeExists(ent)) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Diffing code signed extended attributes are not supported. Detected extended attribute in after-tree on file %@", @(ent->fts_path)] }]; } return NO; } NSDictionary *oldInfo = originalTreeState[key]; if ([info isEqual:oldInfo]) { [newTreeState removeObjectForKey:key]; } else { newTreeState[key] = info; if (oldInfo && [oldInfo[INFO_TYPE_KEY] unsignedShortValue] == FTS_D && [info[INFO_TYPE_KEY] unsignedShortValue] != FTS_D) { NSArray *parentPathComponents = key.pathComponents; for (NSString *childPath in originalTreeState) { NSArray *childPathComponents = childPath.pathComponents; if (childPathComponents.count > parentPathComponents.count && [parentPathComponents isEqualToArray:[childPathComponents subarrayWithRange:NSMakeRange(0, parentPathComponents.count)]]) { [newTreeState removeObjectForKey:childPath]; } } } } } fts_close(fts); NSString *afterHash = hashOfTreeWithVersion(destination, majorVersion); if (!afterHash) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to generate hash for tree %@", destination] }]; } return NO; } if (verbose) { fprintf(stderr, "\nGenerating delta..."); } NSString *temporaryFile = temporaryPatchFile(patchFile); xar_t x = xar_open([temporaryFile fileSystemRepresentation], WRITE); if (!x) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to write to %@", temporaryFile] }]; } return NO; } xar_opt_set(x, XAR_OPT_COMPRESSION, "bzip2"); xar_subdoc_t attributes = xar_subdoc_new(x, BINARY_DELTA_ATTRIBUTES_KEY); xar_subdoc_prop_set(attributes, MAJOR_DIFF_VERSION_KEY, [[NSString stringWithFormat:@"%u", majorVersion] UTF8String]); xar_subdoc_prop_set(attributes, MINOR_DIFF_VERSION_KEY, [[NSString stringWithFormat:@"%u", minorVersion] UTF8String]); // Version 1 patches don't have a major or minor version field, so we need to differentiate between the hash keys const char *beforeHashKey = MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) ? BEFORE_TREE_SHA1_KEY : BEFORE_TREE_SHA1_OLD_KEY; const char *afterHashKey = MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) ? AFTER_TREE_SHA1_KEY : AFTER_TREE_SHA1_OLD_KEY; xar_subdoc_prop_set(attributes, beforeHashKey, [beforeHash UTF8String]); xar_subdoc_prop_set(attributes, afterHashKey, [afterHash UTF8String]); NSOperationQueue *deltaQueue = [[NSOperationQueue alloc] init]; NSMutableArray *deltaOperations = [NSMutableArray array]; // Sort the keys by preferring the ones from the original tree to appear first // We want to enforce deleting before extracting in the case paths differ only by case NSArray *keys = [[newTreeState allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString *key1, NSString *key2) { NSComparisonResult insensitiveCompareResult = [key1 caseInsensitiveCompare:key2]; if (insensitiveCompareResult != NSOrderedSame) { return insensitiveCompareResult; } return originalTreeState[key1] ? NSOrderedAscending : NSOrderedDescending; }]; for (NSString* key in keys) { id value = [newTreeState valueForKey:key]; if ([value isEqual:[NSNull null]]) { xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], (char *)"", 1); assert(newFile); xar_prop_set(newFile, DELETE_KEY, "true"); if (verbose) { fprintf(stderr, "\n❌ %s %s", VERBOSE_REMOVED, [key fileSystemRepresentation]); } continue; } NSDictionary *originalInfo = originalTreeState[key]; NSDictionary *newInfo = newTreeState[key]; if (shouldSkipDeltaCompression(originalInfo, newInfo)) { if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) && shouldSkipExtracting(originalInfo, newInfo)) { if (shouldChangePermissions(originalInfo, newInfo)) { xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], (char *)"", 1); assert(newFile); xar_prop_set(newFile, MODIFY_PERMISSIONS_KEY, [[NSString stringWithFormat:@"%u", [newInfo[INFO_PERMISSIONS_KEY] unsignedShortValue]] UTF8String]); if (verbose) { fprintf(stderr, "\n👮 %s %s (0%o -> 0%o)", VERBOSE_MODIFIED, [key fileSystemRepresentation], [originalInfo[INFO_PERMISSIONS_KEY] unsignedShortValue], [newInfo[INFO_PERMISSIONS_KEY] unsignedShortValue]); } } } else { NSString *path = [destination stringByAppendingPathComponent:key]; xar_file_t newFile = xar_add_frompath(x, 0, [key fileSystemRepresentation], [path fileSystemRepresentation]); assert(newFile); if (shouldDeleteThenExtract(originalInfo, newInfo)) { if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) { xar_prop_set(newFile, DELETE_KEY, "true"); } else { xar_prop_set(newFile, DELETE_THEN_EXTRACT_OLD_KEY, "true"); } } if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion)) { xar_prop_set(newFile, EXTRACT_KEY, "true"); } if (verbose) { if (originalInfo) { fprintf(stderr, "\n✏️ %s %s", VERBOSE_UPDATED, [key fileSystemRepresentation]); } else { fprintf(stderr, "\n✅ %s %s", VERBOSE_ADDED, [key fileSystemRepresentation]); } } } } else { NSNumber *permissions = (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBeigeMajorVersion) && shouldChangePermissions(originalInfo, newInfo)) ? newInfo[INFO_PERMISSIONS_KEY] : nil; CreateBinaryDeltaOperation *operation = [[CreateBinaryDeltaOperation alloc] initWithRelativePath:key oldTree:source newTree:destination oldPermissions:originalInfo[INFO_PERMISSIONS_KEY] newPermissions:permissions]; [deltaQueue addOperation:operation]; [deltaOperations addObject:operation]; } } [deltaQueue waitUntilAllOperationsAreFinished]; for (CreateBinaryDeltaOperation *operation in deltaOperations) { NSString *resultPath = [operation resultPath]; if (!resultPath) { if (verbose) { fprintf(stderr, "\n"); } if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to create patch from source %@ and destination %@", operation.relativePath, resultPath] }]; } return NO; } if (verbose) { fprintf(stderr, "\n🔨 %s %s", VERBOSE_DIFFED, [[operation relativePath] fileSystemRepresentation]); } xar_file_t newFile = xar_add_frompath(x, 0, [[operation relativePath] fileSystemRepresentation], [resultPath fileSystemRepresentation]); assert(newFile); xar_prop_set(newFile, BINARY_DELTA_KEY, "true"); unlink([resultPath fileSystemRepresentation]); if (operation.permissions) { xar_prop_set(newFile, MODIFY_PERMISSIONS_KEY, [[NSString stringWithFormat:@"%u", [operation.permissions unsignedShortValue]] UTF8String]); if (verbose) { fprintf(stderr, "\n👮 %s %s (0%o -> 0%o)", VERBOSE_MODIFIED, [[operation relativePath] fileSystemRepresentation], operation.oldPermissions.unsignedShortValue, operation.permissions.unsignedShortValue); } } } xar_close(x); unlink([patchFile fileSystemRepresentation]); link([temporaryFile fileSystemRepresentation], [patchFile fileSystemRepresentation]); unlink([temporaryFile fileSystemRepresentation]); if (verbose) { fprintf(stderr, "\nDone!\n"); } return YES; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaTool.m ================================================ // // SUBinaryDeltaTool.m // Sparkle // // Created by Mark Rowe on 2009-06-01. // Copyright 2009 Mark Rowe. All rights reserved. // #include "SUBinaryDeltaApply.h" #include "SUBinaryDeltaCreate.h" #import "SUBinaryDeltaCommon.h" #include #include #define VERBOSE_FLAG @"--verbose" #define VERSION_FLAG @"--version" #define CREATE_COMMAND @"create" #define APPLY_COMMAND @"apply" #define VERSION_COMMAND @"version" #define VERSION_ALTERNATE_COMMAND @"--version" static void printUsage(NSString *programName) { fprintf(stderr, "Usage:\n"); fprintf(stderr, "%s create [--verbose] [--version=] \n", [programName UTF8String]); fprintf(stderr, "%s apply [--verbose] \n", [programName UTF8String]); fprintf(stderr, "%s version []\n", [programName UTF8String]); } static BOOL runCreateCommand(NSString *programName, NSArray *args) { if (args.count < 3 || args.count > 5) { printUsage(programName); return NO; } NSUInteger numberOflagsFound = 0; NSUInteger verboseIndex = [args indexOfObject:VERBOSE_FLAG]; NSUInteger versionIndex = NSNotFound; for (NSUInteger argumentIndex = 0; argumentIndex < args.count; ++argumentIndex) { if ([args[argumentIndex] hasPrefix:VERSION_FLAG]) { versionIndex = argumentIndex; break; } } if (verboseIndex != NSNotFound) { ++numberOflagsFound; } if (versionIndex != NSNotFound) { ++numberOflagsFound; } if (args.count - numberOflagsFound < 3) { printUsage(programName); return NO; } BOOL verbose = (verboseIndex != NSNotFound); NSString *versionField = (versionIndex != NSNotFound) ? args[versionIndex] : nil; NSArray *versionComponents = nil; if (versionField) { versionComponents = [versionField componentsSeparatedByString:@"="]; if (versionComponents.count != 2) { printUsage(programName); return NO; } } SUBinaryDeltaMajorVersion patchVersion = !versionComponents ? LATEST_DELTA_DIFF_MAJOR_VERSION : (SUBinaryDeltaMajorVersion)[[versionComponents[1] componentsSeparatedByString:@"."][0] intValue]; // ignore minor version if provided if (patchVersion < FIRST_DELTA_DIFF_MAJOR_VERSION) { fprintf(stderr, "Version provided (%u) is not valid\n", patchVersion); return NO; } if (patchVersion > LATEST_DELTA_DIFF_MAJOR_VERSION) { fprintf(stderr, "This program is too old to create a version %u patch, or the version number provided is invalid\n", patchVersion); return NO; } NSMutableArray *fileArgs = [NSMutableArray array]; for (NSString *argument in args) { if (![argument hasPrefix:VERSION_FLAG] && ![argument isEqualToString:VERBOSE_FLAG]) { [fileArgs addObject:argument]; } } if (fileArgs.count != 3) { printUsage(programName); return NO; } BOOL isDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[0] isDirectory:&isDirectory] || !isDirectory) { printUsage(programName); fprintf(stderr, "Error: before-tree must be a directory\n"); return NO; } if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[1] isDirectory:&isDirectory] || !isDirectory) { printUsage(programName); fprintf(stderr, "Error: after-tree must be a directory\n"); return NO; } NSError *createDiffError = nil; if (!createBinaryDelta(fileArgs[0], fileArgs[1], fileArgs[2], patchVersion, verbose, &createDiffError)) { fprintf(stderr, "%s\n", [createDiffError.localizedDescription UTF8String]); return NO; } return YES; } static BOOL runApplyCommand(NSString *programName, NSArray *args) { if (args.count < 3 || args.count > 4) { printUsage(programName); return NO; } BOOL verbose = [args containsObject:VERBOSE_FLAG]; if (args.count == 4 && !verbose) { printUsage(programName); return NO; } NSMutableArray *fileArgs = [NSMutableArray array]; for (NSString *argument in args) { if (![argument isEqualToString:VERBOSE_FLAG]) { [fileArgs addObject:argument]; } } if (fileArgs.count != 3) { printUsage(programName); return NO; } BOOL isDirectory; if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[0] isDirectory:&isDirectory] || !isDirectory) { printUsage(programName); fprintf(stderr, "Error: before-tree must be a directory\n"); return NO; } if (![[NSFileManager defaultManager] fileExistsAtPath:fileArgs[2] isDirectory:&isDirectory] || isDirectory) { printUsage(programName); fprintf(stderr, "Error: patch-file must be a file %d\n", isDirectory); return NO; } NSError *applyDiffError = nil; if (!applyBinaryDelta(fileArgs[0], fileArgs[1], fileArgs[2], verbose, &applyDiffError)) { fprintf(stderr, "%s\n", [applyDiffError.localizedDescription UTF8String]); return NO; } return YES; } static BOOL runVersionCommand(NSString *programName, NSArray *args) { if (args.count > 1) { printUsage(programName); return NO; } if (args.count == 0) { fprintf(stdout, "%u.%u\n", LATEST_DELTA_DIFF_MAJOR_VERSION, latestMinorVersionForMajorVersion(LATEST_DELTA_DIFF_MAJOR_VERSION)); } else { NSString *patchFile = args[0]; xar_t x = xar_open([patchFile fileSystemRepresentation], READ); if (!x) { fprintf(stderr, "Unable to open patch %s\n", [patchFile fileSystemRepresentation]); return NO; } SUBinaryDeltaMajorVersion majorDiffVersion = FIRST_DELTA_DIFF_MAJOR_VERSION; SUBinaryDeltaMinorVersion minorDiffVersion = FIRST_DELTA_DIFF_MINOR_VERSION; xar_subdoc_t subdoc; for (subdoc = xar_subdoc_first(x); subdoc; subdoc = xar_subdoc_next(subdoc)) { if (!strcmp(xar_subdoc_name(subdoc), BINARY_DELTA_ATTRIBUTES_KEY)) { const char *value = 0; // available in version 2.0 or later xar_subdoc_prop_get(subdoc, MAJOR_DIFF_VERSION_KEY, &value); if (value) majorDiffVersion = (SUBinaryDeltaMajorVersion)[@(value) intValue]; // available in version 2.0 or later xar_subdoc_prop_get(subdoc, MINOR_DIFF_VERSION_KEY, &value); if (value) minorDiffVersion = (SUBinaryDeltaMinorVersion)[@(value) intValue]; } } fprintf(stdout, "%u.%u\n", majorDiffVersion, minorDiffVersion); } return YES; } int main(int __unused argc, char __unused *argv[]) { @autoreleasepool { NSArray *args = [[NSProcessInfo processInfo] arguments]; NSString *programName = [args[0] lastPathComponent]; if (args.count < 2) { printUsage(programName); return 1; } NSString *command = args[1]; NSArray *commandArguments = [args subarrayWithRange:NSMakeRange(2, args.count - 2)]; BOOL result; if ([command isEqualToString:CREATE_COMMAND]) { result = runCreateCommand(programName, commandArguments); } else if ([command isEqualToString:APPLY_COMMAND]) { result = runApplyCommand(programName, commandArguments); } else if ([command isEqualToString:VERSION_COMMAND] || [command isEqualToString:VERSION_ALTERNATE_COMMAND]) { result = runVersionCommand(programName, commandArguments); } else { result = NO; printUsage(programName); } return result ? 0 : 1; } } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaUnarchiver.h ================================================ // // SUBinaryDeltaUnarchiver.h // Sparkle // // Created by Mark Rowe on 2009-06-03. // Copyright 2009 Mark Rowe. All rights reserved. // #ifndef SUBINARYDELTAUNARCHIVER_H #define SUBINARYDELTAUNARCHIVER_H #import #import "SUUnarchiver.h" @interface SUBinaryDeltaUnarchiver : SUUnarchiver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUBinaryDeltaUnarchiver.m ================================================ // // SUBinaryDeltaUnarchiver.m // Sparkle // // Created by Mark Rowe on 2009-06-03. // Copyright 2009 Mark Rowe. All rights reserved. // #import "SUBinaryDeltaCommon.h" #import "SUBinaryDeltaUnarchiver.h" #import "SUBinaryDeltaApply.h" #import "SUUnarchiver_Private.h" #import "SUHost.h" #import "SULog.h" #import "NTSynchronousTask.h" @implementation SUBinaryDeltaUnarchiver + (BOOL)canUnarchivePath:(NSString *)path { return [[path pathExtension] isEqualToString:@"delta"]; } - (void)applyBinaryDelta { @autoreleasepool { NSString *sourcePath = self.updateHostBundlePath; NSString *targetPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[sourcePath lastPathComponent]]; NSError *applyDiffError = nil; BOOL success = applyBinaryDelta(sourcePath, targetPath, self.archivePath, NO, &applyDiffError); if (success) { dispatch_async(dispatch_get_main_queue(), ^{ [self notifyDelegateOfSuccess]; }); } else { SULog(@"Applying delta patch failed with error: %@", applyDiffError); dispatch_async(dispatch_get_main_queue(), ^{ [self notifyDelegateOfFailure]; }); } } } - (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self applyBinaryDelta]; }); } + (void)load { [self registerImplementation:self]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUCodeSigningVerifier.h ================================================ // // SUCodeSigningVerifier.h // Sparkle // // Created by Andy Matuschak on 7/5/12. // // #ifndef SUCODESIGNINGVERIFIER_H #define SUCODESIGNINGVERIFIER_H #import @interface SUCodeSigningVerifier : NSObject + (BOOL)codeSignatureMatchesHostAndIsValidAtPath:(NSString *)applicationPath error:(NSError **)error; + (BOOL)codeSignatureIsValidAtPath:(NSString *)applicationPath error:(NSError **)error; + (BOOL)hostApplicationIsCodeSigned; + (BOOL)applicationAtPathIsCodeSigned:(NSString *)applicationPath; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUCodeSigningVerifier.m ================================================ // // SUCodeSigningVerifier.m // Sparkle // // Created by Andy Matuschak on 7/5/12. // // #include #include #import "SUCodeSigningVerifier.h" #import "SULog.h" @implementation SUCodeSigningVerifier + (BOOL)codeSignatureMatchesHostAndIsValidAtPath:(NSString *)applicationPath error:(NSError *__autoreleasing *)error { OSStatus result; SecRequirementRef requirement = NULL; SecStaticCodeRef staticCode = NULL; SecCodeRef hostCode = NULL; NSBundle *newBundle; CFErrorRef cfError = NULL; if (error) { *error = nil; } result = SecCodeCopySelf(kSecCSDefaultFlags, &hostCode); if (result != noErr) { SULog(@"Failed to copy host code %d", result); goto finally; } result = SecCodeCopyDesignatedRequirement(hostCode, kSecCSDefaultFlags, &requirement); if (result != noErr) { SULog(@"Failed to copy designated requirement. Code Signing OSStatus code: %d", result); goto finally; } newBundle = [NSBundle bundleWithPath:applicationPath]; if (!newBundle) { SULog(@"Failed to load NSBundle for update"); result = -1; goto finally; } result = SecStaticCodeCreateWithPath((__bridge CFURLRef)[newBundle bundleURL], kSecCSDefaultFlags, &staticCode); if (result != noErr) { SULog(@"Failed to get static code %d", result); goto finally; } // Note that kSecCSCheckNestedCode may not work with pre-Mavericks code signing. // See https://github.com/sparkle-project/Sparkle/issues/376#issuecomment-48824267 and https://developer.apple.com/library/mac/technotes/tn2206 SecCSFlags flags = (SecCSFlags) (kSecCSDefaultFlags | kSecCSCheckAllArchitectures); result = SecStaticCodeCheckValidityWithErrors(staticCode, flags, requirement, &cfError); if (cfError) { NSError *tmpError = CFBridgingRelease(cfError); if (error) *error = tmpError; } if (result != noErr) { if (result == errSecCSUnsigned) { SULog(@"The host app is signed, but the new version of the app is not signed using Apple Code Signing. Please ensure that the new app is signed and that archiving did not corrupt the signature."); } if (result == errSecCSReqFailed) { CFStringRef requirementString = nil; if (SecRequirementCopyString(requirement, kSecCSDefaultFlags, &requirementString) == noErr) { SULog(@"Code signature of the new version doesn't match the old version: %@. Please ensure that old and new app is signed using exactly the same certificate.", requirementString); CFRelease(requirementString); } [self logSigningInfoForCode:hostCode label:@"host info"]; [self logSigningInfoForCode:staticCode label:@"new info"]; } } finally: if (hostCode) CFRelease(hostCode); if (staticCode) CFRelease(staticCode); if (requirement) CFRelease(requirement); return (result == noErr); } + (BOOL)codeSignatureIsValidAtPath:(NSString *)applicationPath error:(NSError *__autoreleasing *)error { OSStatus result; SecStaticCodeRef staticCode = NULL; NSBundle *newBundle; CFErrorRef cfError = NULL; if (error) { *error = nil; } newBundle = [NSBundle bundleWithPath:applicationPath]; if (!newBundle) { SULog(@"Failed to load NSBundle"); result = -1; goto finally; } result = SecStaticCodeCreateWithPath((__bridge CFURLRef)[newBundle bundleURL], kSecCSDefaultFlags, &staticCode); if (result != noErr) { SULog(@"Failed to get static code %d", result); goto finally; } // Note that kSecCSCheckNestedCode may not work with pre-Mavericks code signing. // See https://github.com/sparkle-project/Sparkle/issues/376#issuecomment-48824267 and https://developer.apple.com/library/mac/technotes/tn2206 SecCSFlags flags = (SecCSFlags) (kSecCSDefaultFlags | kSecCSCheckAllArchitectures); result = SecStaticCodeCheckValidityWithErrors(staticCode, flags, NULL, &cfError); if (cfError) { NSError *tmpError = CFBridgingRelease(cfError); if (error) *error = tmpError; } if (result != noErr) { if (result == errSecCSUnsigned) { SULog(@"Error: The app is not signed using Apple Code Signing. %@", applicationPath); } if (result == errSecCSReqFailed) { [self logSigningInfoForCode:staticCode label:@"new info"]; } } finally: if (staticCode) CFRelease(staticCode); return (result == noErr); } static id valueOrNSNull(id value) { return value ? value : [NSNull null]; } + (void)logSigningInfoForCode:(SecStaticCodeRef)code label:(NSString*)label { CFDictionaryRef signingInfo = nil; const SecCSFlags flags = (SecCSFlags) (kSecCSSigningInformation | kSecCSRequirementInformation | kSecCSDynamicInformation | kSecCSContentInformation); if (SecCodeCopySigningInformation(code, flags, &signingInfo) == noErr) { NSDictionary *signingDict = CFBridgingRelease(signingInfo); NSMutableDictionary *relevantInfo = [NSMutableDictionary dictionary]; for (NSString *key in @[@"format", @"identifier", @"requirements", @"teamid", @"signing-time"]) { [relevantInfo setObject:valueOrNSNull([signingDict objectForKey:key]) forKey:key]; } NSDictionary *infoPlist = [signingDict objectForKey:@"info-plist"]; [relevantInfo setObject:valueOrNSNull([infoPlist objectForKey:@"CFBundleShortVersionString"]) forKey:@"version"]; [relevantInfo setObject:valueOrNSNull([infoPlist objectForKey:(__bridge NSString *)kCFBundleVersionKey]) forKey:@"build"]; SULog(@"%@: %@", label, relevantInfo); } } + (BOOL)hostApplicationIsCodeSigned { OSStatus result; SecCodeRef hostCode = NULL; result = SecCodeCopySelf(kSecCSDefaultFlags, &hostCode); if (result != 0) return NO; SecRequirementRef requirement = NULL; result = SecCodeCopyDesignatedRequirement(hostCode, kSecCSDefaultFlags, &requirement); if (hostCode) CFRelease(hostCode); if (requirement) CFRelease(requirement); return (result == 0); } + (BOOL)applicationAtPathIsCodeSigned:(NSString *)applicationPath { OSStatus result; SecStaticCodeRef staticCode = NULL; NSBundle *newBundle; newBundle = [NSBundle bundleWithPath:applicationPath]; if (!newBundle) { SULog(@"Failed to load NSBundle"); return NO; } result = SecStaticCodeCreateWithPath((__bridge CFURLRef)[newBundle bundleURL], kSecCSDefaultFlags, &staticCode); if (result == errSecCSUnsigned) { return NO; } SecRequirementRef requirement = NULL; result = SecCodeCopyDesignatedRequirement(staticCode, kSecCSDefaultFlags, &requirement); if (staticCode) { CFRelease(staticCode); } if (requirement) { CFRelease(requirement); } if (result == errSecCSUnsigned) { return NO; } return (result == 0); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUConstants.h ================================================ // // SUConstants.h // Sparkle // // Created by Andy Matuschak on 3/16/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUCONSTANTS_H #define SUCONSTANTS_H #import // ----------------------------------------------------------------------------- // Misc: // ----------------------------------------------------------------------------- extern const NSTimeInterval SUMinimumUpdateCheckInterval; extern const NSTimeInterval SUDefaultUpdateCheckInterval; extern NSString *const SUBundleIdentifier; // ----------------------------------------------------------------------------- // Notifications: // ----------------------------------------------------------------------------- extern NSString *const SUTechnicalErrorInformationKey; // ----------------------------------------------------------------------------- // PList keys:: // ----------------------------------------------------------------------------- extern NSString *const SUFeedURLKey; extern NSString *const SUHasLaunchedBeforeKey; extern NSString *const SUShowReleaseNotesKey; extern NSString *const SUSkippedVersionKey; extern NSString *const SUScheduledCheckIntervalKey; extern NSString *const SULastCheckTimeKey; extern NSString *const SUExpectsDSASignatureKey; extern NSString *const SUPublicDSAKeyKey; extern NSString *const SUPublicDSAKeyFileKey; extern NSString *const SUAutomaticallyUpdateKey; extern NSString *const SUAllowsAutomaticUpdatesKey; extern NSString *const SUEnableAutomaticChecksKey; extern NSString *const SUEnableSystemProfilingKey; extern NSString *const SUSendProfileInfoKey; extern NSString *const SULastProfileSubmitDateKey; extern NSString *const SUPromptUserOnFirstLaunchKey; extern NSString *const SUKeepDownloadOnFailedInstallKey; extern NSString *const SUDefaultsDomainKey; extern NSString *const SUEnableJavaScriptKey; extern NSString *const SUFixedHTMLDisplaySizeKey __attribute__((deprecated("This key is obsolete and has no effect."))); extern NSString *const SUAppendVersionNumberKey __attribute__((deprecated("This key is obsolete. See SPARKLE_APPEND_VERSION_NUMBER."))); extern NSString *const SUEnableAutomatedDowngradesKey __attribute__((deprecated("This key is obsolete. See SPARKLE_AUTOMATED_DOWNGRADES."))); extern NSString *const SUNormalizeInstalledApplicationNameKey __attribute__((deprecated("This key is obsolete. SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME."))); extern NSString *const SURelaunchToolNameKey __attribute__((deprecated("This key is obsolete. SPARKLE_RELAUNCH_TOOL_NAME."))); // ----------------------------------------------------------------------------- // Appcast keys:: // ----------------------------------------------------------------------------- extern NSString *const SUAppcastAttributeDeltaFrom; extern NSString *const SUAppcastAttributeDSASignature; extern NSString *const SUAppcastAttributeShortVersionString; extern NSString *const SUAppcastAttributeVersion; extern NSString *const SUAppcastElementCriticalUpdate; extern NSString *const SUAppcastElementDeltas; extern NSString *const SUAppcastElementMinimumSystemVersion; extern NSString *const SUAppcastElementMaximumSystemVersion; extern NSString *const SUAppcastElementReleaseNotesLink; extern NSString *const SUAppcastElementTags; extern NSString *const SURSSAttributeURL; extern NSString *const SURSSElementDescription; extern NSString *const SURSSElementEnclosure; extern NSString *const SURSSElementLink; extern NSString *const SURSSElementPubDate; extern NSString *const SURSSElementTitle; #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUConstants.m ================================================ // // SUConstants.m // Sparkle // // Created by Andy Matuschak on 3/16/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUConstants.h" #ifndef DEBUG #define DEBUG 0 #endif // Define some minimum intervals to avoid DoS-like checking attacks const NSTimeInterval SUMinimumUpdateCheckInterval = DEBUG ? 60 : (60 * 60); const NSTimeInterval SUDefaultUpdateCheckInterval = DEBUG ? 60 : (60 * 60 * 24); NSString *const SUBundleIdentifier = @SPARKLE_BUNDLE_IDENTIFIER; NSString *const SUTechnicalErrorInformationKey = @"SUTechnicalErrorInformation"; NSString *const SUHasLaunchedBeforeKey = @"SUHasLaunchedBefore"; NSString *const SUFeedURLKey = @"SUFeedURL"; NSString *const SUShowReleaseNotesKey = @"SUShowReleaseNotes"; NSString *const SUSkippedVersionKey = @"SUSkippedVersion"; NSString *const SUScheduledCheckIntervalKey = @"SUScheduledCheckInterval"; NSString *const SULastCheckTimeKey = @"SULastCheckTime"; NSString *const SUExpectsDSASignatureKey = @"SUExpectsDSASignature"; NSString *const SUPublicDSAKeyKey = @"SUPublicDSAKey"; NSString *const SUPublicDSAKeyFileKey = @"SUPublicDSAKeyFile"; NSString *const SUAutomaticallyUpdateKey = @"SUAutomaticallyUpdate"; NSString *const SUAllowsAutomaticUpdatesKey = @"SUAllowsAutomaticUpdates"; NSString *const SUEnableSystemProfilingKey = @"SUEnableSystemProfiling"; NSString *const SUEnableAutomaticChecksKey = @"SUEnableAutomaticChecks"; NSString *const SUSendProfileInfoKey = @"SUSendProfileInfo"; NSString *const SULastProfileSubmitDateKey = @"SULastProfileSubmissionDate"; NSString *const SUPromptUserOnFirstLaunchKey = @"SUPromptUserOnFirstLaunch"; NSString *const SUEnableJavaScriptKey = @"SUEnableJavaScript"; NSString *const SUFixedHTMLDisplaySizeKey = @"SUFixedHTMLDisplaySize"; NSString *const SUKeepDownloadOnFailedInstallKey = @"SUKeepDownloadOnFailedInstall"; NSString *const SUDefaultsDomainKey = @"SUDefaultsDomain"; NSString *const SUSparkleErrorDomain = @"SUSparkleErrorDomain"; NSString *const SUAppendVersionNumberKey = @"SUAppendVersionNumber"; NSString *const SUEnableAutomatedDowngradesKey = @"SUEnableAutomatedDowngrades"; NSString *const SUNormalizeInstalledApplicationNameKey = @"SUNormalizeInstalledApplicationName"; NSString *const SURelaunchToolNameKey = @"SURelaunchToolName"; NSString *const SUAppcastAttributeDeltaFrom = @"sparkle:deltaFrom"; NSString *const SUAppcastAttributeDSASignature = @"sparkle:dsaSignature"; NSString *const SUAppcastAttributeShortVersionString = @"sparkle:shortVersionString"; NSString *const SUAppcastAttributeVersion = @"sparkle:version"; NSString *const SUAppcastElementCriticalUpdate = @"sparkle:criticalUpdate"; NSString *const SUAppcastElementDeltas = @"sparkle:deltas"; NSString *const SUAppcastElementMinimumSystemVersion = @"sparkle:minimumSystemVersion"; NSString *const SUAppcastElementMaximumSystemVersion = @"sparkle:maximumSystemVersion"; NSString *const SUAppcastElementReleaseNotesLink = @"sparkle:releaseNotesLink"; NSString *const SUAppcastElementTags = @"sparkle:tags"; NSString *const SURSSAttributeURL = @"url"; NSString *const SURSSElementDescription = @"description"; NSString *const SURSSElementEnclosure = @"enclosure"; NSString *const SURSSElementLink = @"link"; NSString *const SURSSElementPubDate = @"pubDate"; NSString *const SURSSElementTitle = @"title"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUDSAVerifier.h ================================================ // // SUDSAVerifier.h // Sparkle // // Created by Andy Matuschak on 3/16/06. // Copyright 2006 Andy Matuschak. All rights reserved. // // Includes code by Zach Waldowski on 10/18/13. // Copyright 2014 Big Nerd Ranch. Licensed under MIT. // // Includes code from Plop by Mark Hamlin. // Copyright 2011 Mark Hamlin. Licensed under BSD. // #ifndef SUDSAVERIFIER_H #define SUDSAVERIFIER_H #import #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1090 @interface NSData (SUDSAVerifier) - (id)initWithBase64Encoding:(NSString *)base64String; @end #endif @interface SUDSAVerifier : NSObject + (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString; - (instancetype)initWithPublicKeyData:(NSData *)data; - (BOOL)verifyFileAtPath:(NSString *)path signature:(NSData *)signature; - (BOOL)verifyStream:(NSInputStream *)stream signature:(NSData *)signature; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUDSAVerifier.m ================================================ // // SUDSAVerifier.m // Sparkle // // Created by Andy Matuschak on 3/16/06. // Copyright 2006 Andy Matuschak. All rights reserved. // // Includes code by Zach Waldowski on 10/18/13. // Copyright 2014 Big Nerd Ranch. Licensed under MIT. // // Includes code from Plop by Mark Hamlin. // Copyright 2011 Mark Hamlin. Licensed under BSD. // #import "SUDSAVerifier.h" #import "SULog.h" #include @implementation SUDSAVerifier { SecKeyRef _secKey; } + (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString { if (!encodedSignature) { SULog(@"There is no DSA signature to check"); return NO; } if (!path) { return NO; } SUDSAVerifier *verifier = [[self alloc] initWithPublicKeyData:[pkeyString dataUsingEncoding:NSUTF8StringEncoding]]; if (!verifier) { return NO; } NSString *strippedSignature = [encodedSignature stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; NSData *signature = [[NSData alloc] initWithBase64Encoding:strippedSignature]; return [verifier verifyFileAtPath:path signature:signature]; } - (instancetype)initWithPublicKeyData:(NSData *)data { self = [super init]; if (!self || !data.length) { SULog(@"Could not read public DSA key"); return nil; } SecExternalFormat format = kSecFormatOpenSSL; SecExternalItemType itemType = kSecItemTypePublicKey; CFArrayRef items = NULL; OSStatus status = SecItemImport((__bridge CFDataRef)data, NULL, &format, &itemType, (SecItemImportExportFlags)0, NULL, NULL, &items); if (status != errSecSuccess || !items) { if (items) { CFRelease(items); } SULog(@"Public DSA key could not be imported: %d", status); return nil; } if (format == kSecFormatOpenSSL && itemType == kSecItemTypePublicKey && CFArrayGetCount(items) == 1) { // Seems silly, but we can't quiet the warning about dropping CFTypeRef's const qualifier through // any manner of casting I've tried, including interim explicit cast to void*. The -Wcast-qual // warning is on by default with -Weverything and apparently became more noisy as of Xcode 7. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-qual" _secKey = (SecKeyRef)CFRetain(CFArrayGetValueAtIndex(items, 0)); #pragma clang diagnostic pop } CFRelease(items); return self; } - (void)dealloc { if (_secKey) { CFRelease(_secKey); } } - (BOOL)verifyFileAtPath:(NSString *)path signature:(NSData *)signature { if (!path.length) { return NO; } NSInputStream *dataInputStream = [NSInputStream inputStreamWithFileAtPath:path]; return [self verifyStream:dataInputStream signature:signature]; } - (BOOL)verifyStream:(NSInputStream *)stream signature:(NSData *)signature { if (!stream || !signature) { return NO; } __block SecGroupTransformRef group = SecTransformCreateGroupTransform(); __block SecTransformRef dataReadTransform = NULL; __block SecTransformRef dataDigestTransform = NULL; __block SecTransformRef dataVerifyTransform = NULL; __block CFErrorRef error = NULL; BOOL (^cleanup)(void) = ^{ if (group) CFRelease(group); if (dataReadTransform) CFRelease(dataReadTransform); if (dataDigestTransform) CFRelease(dataDigestTransform); if (dataVerifyTransform) CFRelease(dataVerifyTransform); if (error) CFRelease(error); return NO; }; dataReadTransform = SecTransformCreateReadTransformWithReadStream((__bridge CFReadStreamRef)stream); if (!dataReadTransform) { SULog(@"File containing update archive could not be read (failed to create SecTransform for input stream)"); return cleanup(); } dataDigestTransform = SecDigestTransformCreate(kSecDigestSHA1, CC_SHA1_DIGEST_LENGTH, NULL); if (!dataDigestTransform) { return cleanup(); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdirect-ivar-access" dataVerifyTransform = SecVerifyTransformCreate(_secKey, (__bridge CFDataRef)signature, &error); #pragma clang diagnostic pop if (!dataVerifyTransform || error) { SULog(@"Could not understand format of the signature: %@; Signature data: %@", error, signature); return cleanup(); } SecTransformConnectTransforms(dataReadTransform, kSecTransformOutputAttributeName, dataDigestTransform, kSecTransformInputAttributeName, group, &error); if (error) { SULog(@"%@", error); return cleanup(); } SecTransformConnectTransforms(dataDigestTransform, kSecTransformOutputAttributeName, dataVerifyTransform, kSecTransformInputAttributeName, group, &error); if (error) { SULog(@"%@", error); return cleanup(); } NSNumber *result = CFBridgingRelease(SecTransformExecute(group, &error)); if (error) { SULog(@"DSA signature verification failed: %@", error); return cleanup(); } if (!result.boolValue) { SULog(@"DSA signature does not match. Data of the update file being checked is different than data that has been signed, or the public key and the private key are not from the same set."); } cleanup(); return result.boolValue; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUDiskImageUnarchiver.h ================================================ // // SUDiskImageUnarchiver.h // Sparkle // // Created by Andy Matuschak on 6/16/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUDISKIMAGEUNARCHIVER_H #define SUDISKIMAGEUNARCHIVER_H #import #import "SUUnarchiver.h" @interface SUDiskImageUnarchiver : SUUnarchiver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUDiskImageUnarchiver.m ================================================ // // SUDiskImageUnarchiver.m // Sparkle // // Created by Andy Matuschak on 6/16/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUDiskImageUnarchiver.h" #import "SUUnarchiver_Private.h" #import "NTSynchronousTask.h" #import "SULog.h" #include @implementation SUDiskImageUnarchiver + (BOOL)canUnarchivePath:(NSString *)path { return [[path pathExtension] isEqualToString:@"dmg"]; } // Called on a non-main thread. - (void)extractDMG { @autoreleasepool { [self extractDMGWithPassword:nil]; } } // Called on a non-main thread. - (void)extractDMGWithPassword:(NSString *)__unused password { @autoreleasepool { BOOL mountedSuccessfully = NO; SULog(@"Extracting %@ as a DMG", self.archivePath); // get a unique mount point path NSString *mountPoint = nil; FSRef tmpRef; NSFileManager *manager; NSError *error; NSArray *contents; // We have to declare these before a goto to prevent an error under ARC. // No, we cannot have them in the dispatch_async calls, as the goto "jump enters // lifetime of block which strongly captures a variable" dispatch_block_t delegateFailure = ^{ [self notifyDelegateOfFailure]; }; dispatch_block_t delegateSuccess = ^{ [self notifyDelegateOfSuccess]; }; do { // Using NSUUID would make creating UUIDs be done in Cocoa, // and thus managed under ARC. Sadly, the class is in 10.8 and later. CFUUIDRef uuid = CFUUIDCreate(NULL); if (uuid) { NSString *uuidString = CFBridgingRelease(CFUUIDCreateString(NULL, uuid)); if (uuidString) { mountPoint = [@"/Volumes" stringByAppendingPathComponent:uuidString]; } CFRelease(uuid); } } while (noErr == FSPathMakeRefWithOptions((const UInt8 *)[mountPoint fileSystemRepresentation], kFSPathMakeRefDoNotFollowLeafSymlink, &tmpRef, NULL)); NSData *promptData = nil; promptData = [NSData dataWithBytes:"yes\n" length:4]; NSMutableArray *arguments = [@[@"attach", self.archivePath, @"-mountpoint", mountPoint, /*@"-noverify",*/ @"-nobrowse", @"-noautoopen"] mutableCopy]; if (self.decryptionPassword) { NSMutableData *passwordData = [[self.decryptionPassword dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; // From the hdiutil docs: // read a null-terminated passphrase from standard input // // Add the null terminator, then the newline [passwordData appendData:[NSData dataWithBytes:"\0" length:1]]; [passwordData appendData:promptData]; promptData = passwordData; [arguments addObject:@"-stdinpass"]; } NSData *output = nil; NSInteger taskResult = -1; @try { NTSynchronousTask *task = [[NTSynchronousTask alloc] init]; [task run:@"/usr/bin/hdiutil" directory:@"/" withArgs:arguments input:promptData]; taskResult = [task result]; output = [[task output] copy]; } @catch (NSException *) { goto reportError; } if (taskResult != 0) { NSString *resultStr = output ? [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding] : nil; SULog(@"hdiutil failed with code: %ld data: <<%@>>", (long)taskResult, resultStr); goto reportError; } mountedSuccessfully = YES; // Now that we've mounted it, we need to copy out its contents. manager = [[NSFileManager alloc] init]; contents = [manager contentsOfDirectoryAtPath:mountPoint error:&error]; if (error) { SULog(@"Couldn't enumerate contents of archive mounted at %@: %@", mountPoint, error); goto reportError; } for (NSString *item in contents) { NSString *fromPath = [mountPoint stringByAppendingPathComponent:item]; NSString *toPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:item]; // We skip any files in the DMG which are not readable. if (![manager isReadableFileAtPath:fromPath]) { continue; } SULog(@"copyItemAtPath:%@ toPath:%@", fromPath, toPath); if (![manager copyItemAtPath:fromPath toPath:toPath error:&error]) { SULog(@"Couldn't copy item: %@ : %@", error, error.userInfo ? error.userInfo : @""); goto reportError; } } dispatch_async(dispatch_get_main_queue(), delegateSuccess); goto finally; reportError: dispatch_async(dispatch_get_main_queue(), delegateFailure); finally: if (mountedSuccessfully) [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:@[@"detach", mountPoint, @"-force"]]; else SULog(@"Can't mount DMG %@", self.archivePath); } } - (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self extractDMG]; }); } + (void)load { [self registerImplementation:self]; } - (BOOL)isEncrypted:(NSData *)resultData { BOOL result = NO; if(resultData) { NSString *data = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]; if ((data != nil) && !NSEqualRanges([data rangeOfString:@"passphrase-count"], NSMakeRange(NSNotFound, 0))) { result = YES; } } return result; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUErrors.h ================================================ // // SUErrors.h // Sparkle // // Created by C.W. Betts on 10/13/14. // Copyright (c) 2014 Sparkle Project. All rights reserved. // #ifndef SUERRORS_H #define SUERRORS_H #if __has_feature(modules) @import Foundation; #else #import #endif #import "SUExport.h" /** * Error domain used by Sparkle */ SU_EXPORT extern NSString *const SUSparkleErrorDomain; typedef NS_ENUM(OSStatus, SUError) { // Appcast phase errors. SUAppcastParseError = 1000, SUNoUpdateError = 1001, SUAppcastError = 1002, SURunningFromDiskImageError = 1003, // Download phase errors. SUTemporaryDirectoryError = 2000, SUDownloadError = 2001, // Extraction phase errors. SUUnarchivingError = 3000, SUSignatureError = 3001, // Installation phase errors. SUFileCopyFailure = 4000, SUAuthenticationFailure = 4001, SUMissingUpdateError = 4002, SUMissingInstallerToolError = 4003, SURelaunchError = 4004, SUInstallationError = 4005, SUDowngradeError = 4006, SUInstallationCancelledError = 4007, // System phase errors SUSystemPowerOffError = 5000 }; #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUExport.h ================================================ // // SUExport.h // Sparkle // // Created by Jake Petroules on 2014-08-23. // Copyright (c) 2014 Sparkle Project. All rights reserved. // #ifndef SUEXPORT_H #define SUEXPORT_H #ifdef BUILDING_SPARKLE #define SU_EXPORT __attribute__((visibility("default"))) #else #define SU_EXPORT #endif #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUFileManager.h ================================================ // // SUFileManager.h // Sparkle // // Created by Mayur Pawashe on 7/18/15. // Copyright (c) 2015 zgcoder. All rights reserved. // #import /** * A class used for performing file operations that may also perform authorization if allowed and if permission is denied when trying to * perform them normally as the running user. All operations on this class may be used on thread other than the main thread. * This class provides basic file operations and stays away from including much application-level logic. */ @interface SUFileManager : NSObject /** * Creates a file manager that will not authorize for file operations * @return A new file manager instance */ + (instancetype)defaultManager; /** * Creates a file manager that allows authorizing for file operations * @param authorizationToolPath Specifies the path to the tool that can perform file operations and be run as root. * This tool will be invoked when read or write access is denied when attempting ordinary file operations. See the `fileop` tool included in Sparkle. * @return A new file manager instance * * This method just creates the file manager. It doesn't acquire authorization immediately or if it doesn't need to. */ + (instancetype)fileManagerWithAuthorizationToolPath:(NSString *)authorizationToolPath; /** * Returns a file manager that allows or disallows authorizing for file operations based on the current file manager * @return A file manager instance that can perform authorized operations if the current file manager has already performed them. * If the current file manager instance hasn't yet performed authorized operations, then neither can the instance returned by this method * * This may return a newly created file manager or re-use the existing file manager depending on the current authorization rights. */ - (instancetype)fileManagerByPreservingAuthorizationRights; /** * Creates a temporary directory on the same volume as a provided URL * @param preferredName A name that may be used when creating the temporary directory. Note that in the uncommon case this name is used, the temporary directory will be created inside the directory pointed by appropriateURL * @param appropriateURL A URL to a directory that resides on the volume that the temporary directory will be created on. In the uncommon case, the temporary directory may be created inside this directory. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return A URL pointing to the newly created temporary directory, or nil with a populated error object if an error occurs. * * When moving an item from a source to a destination, it is desirable to create a temporary intermediate destination on the same volume as the destination to ensure * that the item will be moved, and not copied, from the intermediate point to the final destination. This ensures file atomicity. */ - (NSURL *)makeTemporaryDirectoryWithPreferredName:(NSString *)preferredName appropriateForDirectoryURL:(NSURL *)appropriateURL error:(NSError **)error; /** * Creates a directory at the target URL * @param targetURL A URL pointing to the directory to create. The item at this URL must not exist, and the parent directory of this URL must already exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the item was created successfully, otherwise NO along with a populated error object * * This is an atomic operation. */ - (BOOL)makeDirectoryAtURL:(NSURL *)targetURL error:(NSError **)error; /** * Moves an item from a source to a destination * @param sourceURL A URL pointing to the item to move. The item at this URL must exist. * @param destinationURL A URL pointing to the destination the item will be moved at. An item must not already exist at this URL. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the item was moved successfully, otherwise NO along with a populated error object * * If sourceURL and destinationURL reside on the same volume, this operation will be an atomic move operation. * Otherwise this will be equivalent to a copy & remove which will be a nonatomic operation. */ - (BOOL)moveItemAtURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL error:(NSError **)error; /** * Copies an item from a source to a destination * @param sourceURL A URL pointing to the item to move. The item at this URL must exist. * @param destinationURL A URL pointing to the destination the item will be moved at. An item must not already exist at this URL. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the item was copied successfully, otherwise NO along with a populated error object * * This is not an atomic operation. */ - (BOOL)copyItemAtURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL error:(NSError **)error; /** * Moves an item at a specified URL to the running user's trash directory * @param url A URL pointing to the item to move to the trash. The item at this URL must exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the item was moved to the trash successfully, otherwise NO along with a populated error object * * * This method has to locate the trash directory and uses an intermediate temporary directory before trashing the item. * A copy may have to be done if the url is not on the same volume as the running user's trash directory. * If a failure occurs in the middle of this operation, the item to remove may be lost forever or stuck in a temporary location. * * This is not an atomic operation, nor intended to be a recoverable operation if the worst comes to worst. */ - (BOOL)moveItemAtURLToTrash:(NSURL *)url error:(NSError **)error; /** * Removes an item at a URL * @param url A URL pointing to the item to remove. The item at this URL must exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the item was removed successfully, otherwise NO along with a populated error object * * This is not an atomic operation. */ - (BOOL)removeItemAtURL:(NSURL *)url error:(NSError **)error; /** * Changes the owner and group IDs of an item at a specified target URL to match another URL * @param targetURL A URL pointing to the target item whose owner and group IDs to alter. This will be applied recursively if the item is a directory. The item at this URL must exist. * @param matchURL A URL pointing to the item whose owner and group IDs will be used for changing on the targetURL. The item at this URL must exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the target item's owner and group IDs have changed to match the origin's ones, otherwise NO along with a populated error object * * If the owner and group IDs match on the root items of targetURL and matchURL, this method stops and assumes that nothing needs to be done. * Otherwise this method recursively changes the IDs if the target is a directory. If an item in the directory is encountered that is unable to be changed, * then this method stops and returns NO. * * This is not an atomic operation. */ - (BOOL)changeOwnerAndGroupOfItemAtRootURL:(NSURL *)targetURL toMatchURL:(NSURL *)matchURL error:(NSError **)error; /** * Updates the modification and access time of an item at a specified target URL to the current time * @param targetURL A URL pointing to the target item whose modification and access time to update. The item at this URL must exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the target item's modification and access times have been updated, otherwise NO along with a populated error object * * This method updates the modification and access time of an item to the current time, ideal for letting the system know we installed a new file or * application. * * This is not an atomic operation. */ - (BOOL)updateModificationAndAccessTimeOfItemAtURL:(NSURL *)targetURL error:(NSError **)error; /** * Releases Apple's quarantine extended attribute from the item at the specified root URL * @param rootURL A URL pointing to the item to release from Apple's quarantine. This will be applied recursively if the item is a directory. The item at this URL must exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if all the items at the target could be released from quarantine, otherwise NO if any items couldn't along with a populated error object * * This method removes quarantine attributes from an item, ideally an application, so that when the user launches a new application themselves, they * don't have to witness the system dialog alerting them that they downloaded an application from the internet and asking if they want to continue. * Note that this may not exactly mimic the system behavior when a user opens an application for the first time (i.e, the xattr isn't deleted), * but this should be sufficient enough for our purposes. * * This method may return NO even if some items do get released from quarantine if the target URL is pointing to a directory. * Thus if an item cannot be released from quarantine, this method still continues on to the next enumerated item. * * This is not an atomic operation. */ - (BOOL)releaseItemFromQuarantineAtRootURL:(NSURL *)rootURL error:(NSError **)error; /** * Runs an installer package (pkg) in a headless mode using /usr/sbin/installer * @param packageURL A URL pointing to the package to execute. The item at this URL must exist. * @param error If an error occurs, upon returns contains an NSError object that describes the problem. If you are not interested in possible errors, you may pass in NULL. * @return YES if the installer ran the package successfully, otherwise NO with a populated error object * * This method uses the system wide installer tool to run the provided package. This process does not show any UI, except for * an initial authorization prompt if the calling process does not have root privileges. In other words, root privileges are required to use this method, and the file manager instance must have been created by allowing authorization. * An error can occur if the package is unable to be ran by the installer, or if the installer reports a non-zero exit status code. */ - (BOOL)executePackageAtURL:(NSURL *)packageURL error:(NSError **)error; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUFileManager.m ================================================ // // SUFileManager.m // Sparkle // // Created by Mayur Pawashe on 7/18/15. // Copyright (c) 2015 zgcoder. All rights reserved. // #import "SUFileManager.h" #import "SUOperatingSystem.h" #import "SUFileOperationConstants.h" #include #include #include static char SUAppleQuarantineIdentifier[] = "com.apple.quarantine"; static BOOL SUMakeRefFromURL(NSURL *url, FSRef *ref, NSError **error) { char path[PATH_MAX] = {0}; if (![url.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"URL of the file (%@) cannot be represented as a file path", url.lastPathComponent] }]; } return NO; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" OSStatus makeResult = FSPathMakeRefWithOptions((const UInt8 *)path, kFSPathMakeRefDoNotFollowLeafSymlink, ref, NULL); #pragma clang diagnostic pop if (makeResult != noErr) { if (error != NULL) { *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:makeResult userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to create file system reference for %@", url.lastPathComponent] }]; } return NO; } return YES; } // Used to indicate if the type of NSError requires us to attempt to peform the same operation again except with authentication // To be safe, both read and write permission denied's are included because Cocoa's error methods are not very well documented // and at least one case is caused from lack of read permissions (-[NSURL setResourceValue:forKey:error:]) #define NS_HAS_PERMISSION_ERROR(error) (error.code == NSFileReadNoPermissionError || error.code == NSFileWriteNoPermissionError) #pragma clang diagnostic push // Use direct access because it's easier, clearer, and faster #pragma clang diagnostic ignored "-Wdirect-ivar-access" @implementation SUFileManager { AuthorizationRef _auth; NSFileManager *_fileManager; NSString *_authorizationToolPath; } - (instancetype)initWithAuthorizationToolPath:(NSString *)authorizationToolPath { self = [super init]; if (self != nil) { _fileManager = [[NSFileManager alloc] init]; _authorizationToolPath = [authorizationToolPath copy]; } return self; } + (instancetype)defaultManager { return [[self alloc] initWithAuthorizationToolPath:nil]; } + (instancetype)fileManagerWithAuthorizationToolPath:(NSString *)authorizationToolPath { return [[self alloc] initWithAuthorizationToolPath:authorizationToolPath]; } - (instancetype)fileManagerByPreservingAuthorizationRights { // Check if we don't allow authorization, or that we haven't needed to authorize yet, to create or re-use a // file manager instance with these restrictions return (_authorizationToolPath != nil && _auth != NULL) ? self : [SUFileManager defaultManager]; } // Acquires an authorization reference with root privileges which is intended to be used for authorized file operations - (BOOL)_acquireAuthorizationWithError:(NSError *__autoreleasing *)error { // No need to continue if we already acquired an authorization reference if (_auth != NULL) { return YES; } if (_authorizationToolPath == nil) { if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: @"Unable to grant authorization to perform action because it is explicitly turned off" }]; } return NO; } OSStatus createStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &_auth); if (createStatus != errAuthorizationSuccess) { if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed creating authorization reference with status code %d.", createStatus] }]; } _auth = NULL; return NO; } AuthorizationItem rightItems[] = { // The right that allows us to run tools as root user {.name = kAuthorizationRightExecute, .valueLength = 0, .value = NULL, .flags = 0} }; AuthorizationRights rights = { .count = sizeof(rightItems) / sizeof(*rightItems), .items = rightItems }; AuthorizationFlags flags = (AuthorizationFlags)(kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize); // This will test if we can gain authorization for running utlities as root OSStatus copyStatus = AuthorizationCopyRights(_auth, &rights, kAuthorizationEmptyEnvironment, flags, NULL); if (copyStatus != errAuthorizationSuccess) { if (error != NULL) { if (copyStatus == errAuthorizationCanceled) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationCancelledError userInfo:@{ NSLocalizedDescriptionKey: @"Authorization access was cancelled by the user." }]; } else { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed granting authorization rights with status code %d.", copyStatus] }]; } } AuthorizationFree(_auth, kAuthorizationFlagDefaults); _auth = NULL; return NO; } return YES; } - (BOOL)_authorizeAndExecuteCommand:(char *)command sourcePath:(char *)sourcePath destinationPath:(char *)destinationPath error:(NSError * __autoreleasing *)error { NSError *acquireError = nil; if (![self _acquireAuthorizationWithError:&acquireError]) { if (error != NULL) { *error = acquireError; } return NO; } char *arguments[] = { command, sourcePath, destinationPath, NULL }; char toolPath[PATH_MAX] = {0}; if (![_authorizationToolPath getFileSystemRepresentation:toolPath maxLength:sizeof(toolPath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Authorization tool (%@) cannot be represented as a valid file name.", _authorizationToolPath.lastPathComponent] }]; } return NO; } FILE *pipe = NULL; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (AuthorizationExecuteWithPrivileges(_auth, toolPath, kAuthorizationFlagDefaults, arguments, &pipe) != errAuthorizationSuccess) { #pragma clang diagnostic pop if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey:@"Failed to run authorization tool." }]; } return NO; } uint32_t pidData = 0; if (fread(&pidData, sizeof(pidData), 1, pipe) < 1) { if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey:@"Failed to retrieve authorized executable process identifier." }]; } fclose(pipe); return NO; } pid_t childPid = (int32_t)CFSwapInt32LittleToHost(pidData); int status = 0; pid_t waitResult; do { waitResult = waitpid(childPid, &status, 0); } while (waitResult == -1 && errno == EINTR); fclose(pipe); if (waitResult == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Failed to execute authorized executable with result %d and status (%d, %d, %d).", waitResult, status, WIFEXITED(status), WEXITSTATUS(status)] }]; } return NO; } return YES; } - (void)dealloc { if (_auth != NULL) { AuthorizationFree(_auth, kAuthorizationFlagDefaults); } } // -[NSFileManager attributesOfItemAtPath:error:] won't follow symbolic links - (BOOL)_itemExistsAtURL:(NSURL *)fileURL { NSString *path = fileURL.path; if (path == nil) { return NO; } return [_fileManager attributesOfItemAtPath:path error:NULL] != nil; } - (BOOL)_itemExistsAtURL:(NSURL *)fileURL isDirectory:(BOOL *)isDirectory { NSString *path = fileURL.path; if (path == nil) { return NO; } NSDictionary *attributes = [_fileManager attributesOfItemAtPath:path error:NULL]; if (attributes == nil) { return NO; } if (isDirectory != NULL) { *isDirectory = [[attributes objectForKey:NSFileType] isEqualToString:NSFileTypeDirectory]; } return YES; } // Wrapper around getxattr() - (ssize_t)_getXAttr:(const char *)name fromFile:(NSString *)file options:(int)options { char path[PATH_MAX] = {0}; if (![file getFileSystemRepresentation:path maxLength:sizeof(path)]) { errno = 0; return -1; } return getxattr(path, name, NULL, 0, 0, options); } // Wrapper around removexattr() - (int)_removeXAttr:(const char *)attr fromFile:(NSString *)file options:(int)options { char path[PATH_MAX] = {0}; if (![file getFileSystemRepresentation:path maxLength:sizeof(path)]) { errno = 0; return -1; } return removexattr(path, attr, options); } #define XATTR_UTILITY_PATH "/usr/bin/xattr" // Recursively remove an xattr at a specified root URL with authentication - (BOOL)_removeQuarantineWithAuthenticationAtRootURL:(NSURL *)rootURL error:(NSError *__autoreleasing *)error { // Because this is a system utility, it's fine to follow the symbolic link if one exists if (![_fileManager fileExistsAtPath:@(XATTR_UTILITY_PATH)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: @"xattr utility does not exist on this system." }]; } return NO; } char path[PATH_MAX] = {0}; if (![rootURL.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File to remove (%@) cannot be represented as a valid file name.", rootURL.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; BOOL success = [self _authorizeAndExecuteCommand:SUFileOpRemoveQuarantineCommand sourcePath:path destinationPath:NULL error:&executeError]; if (!success && error != NULL) { *error = executeError; } return success; } - (BOOL)_releaseItemFromQuarantineAtRootURL:(NSURL *)rootURL withQuarantineRetrieval:(BOOL (^)(NSURL *))quarantineRetrieval quarantineRemoval:(BOOL (^)(NSURL *, NSError * __autoreleasing *))quarantineRemoval isAccessError:(BOOL (^)(NSError *))isAccessError error:(NSError * __autoreleasing *)error { if (![self _itemExistsAtURL:rootURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to remove quarantine because %@ does not exist.", rootURL.path.lastPathComponent] }]; } return NO; } BOOL (^releasingQuarantineRequiredAuthentication)(NSURL *, BOOL *, BOOL *) = ^(NSURL *fileURL, BOOL *didReleaseQuarantine, BOOL *success) { BOOL removedQuarantine = NO; BOOL attemptedAuthentication = NO; if (quarantineRetrieval(fileURL)) { NSError *removalError = nil; if (quarantineRemoval(fileURL, &removalError)) { removedQuarantine = YES; } else { if (isAccessError(removalError)) { removedQuarantine = [self _removeQuarantineWithAuthenticationAtRootURL:rootURL error:error]; attemptedAuthentication = YES; } else { if (success != NULL) { // Make sure we haven't already run into an error if (*success && error != NULL) { *error = removalError; } // Fail, but still try to release other items from quarantine *success = NO; } } } } if (didReleaseQuarantine != NULL) { *didReleaseQuarantine = removedQuarantine; } return attemptedAuthentication; }; BOOL success = YES; BOOL releasedRootQuarantine = NO; if (releasingQuarantineRequiredAuthentication(rootURL, &releasedRootQuarantine, &success)) { return releasedRootQuarantine; } // Only recurse if it's actually a directory. Don't recurse into a // root-level symbolic link. NSString *rootURLPath = rootURL.path; NSDictionary *rootAttributes = [_fileManager attributesOfItemAtPath:rootURLPath error:nil]; NSString *rootType = [rootAttributes objectForKey:NSFileType]; // 10.7 can't subscript this if ([rootType isEqualToString:NSFileTypeDirectory]) { // The NSDirectoryEnumerator will avoid recursing into any contained // symbolic links, so no further type checks are needed. NSDirectoryEnumerator *directoryEnumerator = [_fileManager enumeratorAtURL:rootURL includingPropertiesForKeys:nil options:(NSDirectoryEnumerationOptions)0 errorHandler:nil]; for (NSURL *file in directoryEnumerator) { BOOL releasedQuarantine = NO; if (releasingQuarantineRequiredAuthentication(file, &releasedQuarantine, &success)) { return releasedQuarantine; } } } return success; } // Removes the directory tree rooted at |root| from the file quarantine. // The quarantine was introduced on macOS 10.5 and is described at: // // http://developer.apple.com/releasenotes/Carbon/RN-LaunchServices/index.html#apple_ref/doc/uid/TP40001369-DontLinkElementID_2 // // If |root| is not a directory, then it alone is removed from the quarantine. // Symbolic links, including |root| if it is a symbolic link, will not be // traversed. // Ordinarily, the quarantine is managed by calling LSSetItemAttribute // to set the kLSItemQuarantineProperties attribute to a dictionary specifying // the quarantine properties to be applied. However, it does not appear to be // possible to remove an item from the quarantine directly through any public // Launch Services calls. Instead, this method takes advantage of the fact // that the quarantine is implemented in part by setting an extended attribute, // "com.apple.quarantine", on affected files. Removing this attribute is // sufficient to remove files from the quarantine. // This works by removing the quarantine extended attribute for every file we come across. // We used to have code similar to the method below that used -[NSURL getResourceValue:forKey:error:] and -[NSURL setResourceValue:forKey:error:] // However, those methods *really suck* - you can't rely on the return value from getting the resource value and if you set the resource value // when the key isn't present, errors are spewed out to the console - (BOOL)releaseItemFromQuarantineAtRootURL:(NSURL *)rootURL error:(NSError *__autoreleasing *)error { static const int removeXAttrOptions = XATTR_NOFOLLOW; return [self _releaseItemFromQuarantineAtRootURL:rootURL withQuarantineRetrieval:^BOOL(NSURL *fileURL) { return ([self _getXAttr:SUAppleQuarantineIdentifier fromFile:fileURL.path options:removeXAttrOptions] >= 0); } quarantineRemoval:^BOOL(NSURL *fileURL, NSError * __autoreleasing *removalError) { BOOL removedQuarantine = ([self _removeXAttr:SUAppleQuarantineIdentifier fromFile:fileURL.path options:removeXAttrOptions] == 0); if (!removedQuarantine && removalError != NULL) { *removalError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to remove file quarantine on %@.", fileURL.lastPathComponent] }]; } return removedQuarantine; } isAccessError:^BOOL(NSError *removalError) { return (removalError.code == EACCES); } error:error]; } /* * Copies an item from one location to another * This intentionally does *not* use copyfile() or any API that uses it such as NSFileManager's copy item method * This is because copyfile() can fail to copy symbolic links from one network mount to another, which will affect copying apps * This failure occurs because the system may think symbolic links on a SMB mount are zero bytes in size * For more info, see bug reports at http://openradar.appspot.com/radar?id=4925873463492608 * and http://openradar.appspot.com/radar?id=5024037222744064 */ - (BOOL)copyItemAtURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL error:(NSError * __autoreleasing *)error { if (![self _itemExistsAtURL:sourceURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Source file to copy (%@) does not exist.", sourceURL.lastPathComponent] }]; } return NO; } if (![self _itemExistsAtURL:destinationURL.URLByDeletingLastPathComponent]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination parent directory to copy into (%@) does not exist.", destinationURL.URLByDeletingLastPathComponent.lastPathComponent] }]; } return NO; } if ([self _itemExistsAtURL:destinationURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteFileExistsError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination file to copy to (%@) already exists.", destinationURL.lastPathComponent] }]; } return NO; } FSRef sourceRef; if (!SUMakeRefFromURL(sourceURL, &sourceRef, error)) { return NO; } FSRef destinationParentRef; if (!SUMakeRefFromURL(destinationURL.URLByDeletingLastPathComponent, &destinationParentRef, error)) { return NO; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" OSStatus copyResult = FSCopyObjectSync(&sourceRef, &destinationParentRef, (__bridge CFStringRef)(destinationURL.lastPathComponent), NULL, kFSFileOperationDefaultOptions); #pragma clang diagnostic pop if (copyResult == noErr) { return YES; } // Note: I have received afpAccessDenied error in testing even when not copying from/to an AFP mount, // when the error should have been a normal permission denied one if (copyResult != permErr && copyResult != afpAccessDenied) { if (error != NULL) { *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:copyResult userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to copy file (%@)", sourceURL.lastPathComponent] }]; } return NO; } char sourcePath[PATH_MAX] = {0}; if (![sourceURL.path getFileSystemRepresentation:sourcePath maxLength:sizeof(sourcePath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination to copy file to (%@) cannot be represented as a valid file name.", sourceURL.lastPathComponent] }]; } return NO; } char destinationPath[PATH_MAX] = {0}; if (![destinationURL.path getFileSystemRepresentation:destinationPath maxLength:sizeof(destinationPath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination to copy file to (%@) cannot be represented as a valid file name.", destinationURL.lastPathComponent] }]; } return NO; } NSError *executeError = nil; if (![self _authorizeAndExecuteCommand:SUFileOpCopyCommand sourcePath:sourcePath destinationPath:destinationPath error:&executeError]) { if (error != NULL) { *error = executeError; } return NO; } return YES; } /* * Retrieves the volume ID that a particular url resides on * The url must point to a file that exists * There is no cocoa equivalent for obtaining the volume ID * Although NSURLVolumeURLForRemountingKey exists as a resource key for NSURL, * that will not return a URL if the mount is not re-mountable and I otherwise don't trust the API */ - (BOOL)_getVolumeID:(FSVolumeRefNum *)volumeID ofItemAtURL:(NSURL *)url { FSRef pathRef; if (!SUMakeRefFromURL(url, &pathRef, NULL)) { return NO; } FSCatalogInfo catalogInfo; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" OSErr catalogError = FSGetCatalogInfo(&pathRef, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); #pragma clang diagnostic pop if (catalogError != noErr) { return NO; } if (volumeID != NULL) { *volumeID = catalogInfo.volume; } return YES; } - (BOOL)moveItemAtURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL error:(NSError *__autoreleasing *)error { if (![self _itemExistsAtURL:sourceURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Source file to move (%@) does not exist.", sourceURL.lastPathComponent] }]; } return NO; } NSURL *destinationURLParent = destinationURL.URLByDeletingLastPathComponent; if (![self _itemExistsAtURL:destinationURLParent]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination parent directory to move into (%@) does not exist.", destinationURLParent.lastPathComponent] }]; } return NO; } if ([self _itemExistsAtURL:destinationURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteFileExistsError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination file to move (%@) already exists.", destinationURL.lastPathComponent] }]; } return NO; } // If the source and destination are on different volumes, we should not do a move; // from my experience a move may fail when moving particular files from // one network mount to another one. This is possibly related to the fact that // moving a file will try to preserve ownership but copying won't FSVolumeRefNum sourceVolume = 0; BOOL foundSourceVolume = [self _getVolumeID:&sourceVolume ofItemAtURL:sourceURL]; FSVolumeRefNum destinationVolume = 0; BOOL foundDestinationVolume = [self _getVolumeID:&destinationVolume ofItemAtURL:destinationURLParent]; if (foundSourceVolume && foundDestinationVolume && sourceVolume != destinationVolume) { return ([self copyItemAtURL:sourceURL toURL:destinationURL error:error] && [self removeItemAtURL:sourceURL error:error]); } NSError *moveError = nil; if ([_fileManager moveItemAtURL:sourceURL toURL:destinationURL error:&moveError]) { return YES; } if (!NS_HAS_PERMISSION_ERROR(moveError)) { if (error != NULL) { *error = moveError; } return NO; } char sourcePath[PATH_MAX] = {0}; if (![sourceURL.path getFileSystemRepresentation:sourcePath maxLength:sizeof(sourcePath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File to move (%@) cannot be represented as a valid file name.", sourceURL.path.lastPathComponent] }]; } return NO; } char destinationPath[PATH_MAX] = {0}; if (![destinationURL.path getFileSystemRepresentation:destinationPath maxLength:sizeof(destinationPath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination (%@) cannot be represented as a valid file name.", destinationURL.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; if (![self _authorizeAndExecuteCommand:SUFileOpMoveCommand sourcePath:sourcePath destinationPath:destinationPath error:&executeError]) { if (error != NULL) { NSString *errorMessage = [NSString stringWithFormat:@"Failed to perform authorized file move for %@.", sourceURL.lastPathComponent]; *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey:errorMessage, NSUnderlyingErrorKey: executeError }]; } return NO; } return YES; } - (BOOL)_changeOwnerAndGroupOfItemAtURL:(NSURL *)targetURL ownerID:(NSNumber *)ownerID groupID:(NSNumber *)groupID needsAuth:(BOOL *)needsAuth error:(NSError * __autoreleasing *)error { char path[PATH_MAX] = {0}; if (![targetURL.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File to change owner & group (%@) cannot be represented as a valid file name.", targetURL.path.lastPathComponent] }]; } return NO; } int fileDescriptor = open(path, O_RDONLY | O_SYMLINK); if (fileDescriptor == -1) { if (error != NULL) { *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to open file descriptor to %@", targetURL.path.lastPathComponent] }]; } return NO; } // We use fchown instead of chown because the latter can follow symbolic links BOOL success = fchown(fileDescriptor, ownerID.unsignedIntValue, groupID.unsignedIntValue) == 0; close(fileDescriptor); if (!success) { if (errno == EPERM) { if (needsAuth != NULL) { *needsAuth = YES; } } else { if (error != NULL) { *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to change owner & group for %@ with owner ID %u and group ID %u.", targetURL.path.lastPathComponent, ownerID.unsignedIntValue, groupID.unsignedIntValue] }]; } return NO; } } return YES; } - (BOOL)changeOwnerAndGroupOfItemAtRootURL:(NSURL *)targetURL toMatchURL:(NSURL *)matchURL error:(NSError * __autoreleasing *)error { BOOL isTargetADirectory = NO; if (![self _itemExistsAtURL:targetURL isDirectory:&isTargetADirectory]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to change owner & group IDs because %@ does not exist.", targetURL.path.lastPathComponent] }]; } return NO; } if (![self _itemExistsAtURL:matchURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to match owner & group IDs because %@ does not exist.", matchURL.path.lastPathComponent] }]; } return NO; } NSError *matchFileAttributesError = nil; NSString *matchURLPath = matchURL.path; NSDictionary *matchFileAttributes = [_fileManager attributesOfItemAtPath:matchURLPath error:&matchFileAttributesError]; if (matchFileAttributes == nil) { if (error != NULL) { *error = matchFileAttributesError; } return NO; } NSError *targetFileAttributesError = nil; NSString *targetURLPath = targetURL.path; NSDictionary *targetFileAttributes = [_fileManager attributesOfItemAtPath:targetURLPath error:&targetFileAttributesError]; if (targetFileAttributes == nil) { if (error != NULL) { *error = targetFileAttributesError; } return NO; } NSNumber *ownerID = [matchFileAttributes objectForKey:NSFileOwnerAccountID]; if (ownerID == nil) { // shouldn't be possible to error here, but just in case if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadNoPermissionError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Owner ID could not be read from %@.", matchURL.path.lastPathComponent] }]; } return NO; } NSNumber *groupID = [matchFileAttributes objectForKey:NSFileGroupOwnerAccountID]; if (groupID == nil) { // shouldn't be possible to error here, but just in case if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadNoPermissionError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Group ID could not be read from %@.", matchURL.path.lastPathComponent] }]; } return NO; } NSNumber *targetOwnerID = [targetFileAttributes objectForKey:NSFileOwnerAccountID]; NSNumber *targetGroupID = [targetFileAttributes objectForKey:NSFileGroupOwnerAccountID]; if ((targetOwnerID != nil && [ownerID isEqualToNumber:targetOwnerID]) && (targetGroupID != nil && [groupID isEqualToNumber:targetGroupID])) { // Assume they're the same even if we don't check every file recursively // Speeds up the common case return YES; } BOOL needsAuth = NO; if (![self _changeOwnerAndGroupOfItemAtURL:targetURL ownerID:ownerID groupID:groupID needsAuth:&needsAuth error:error]) { return NO; } if (isTargetADirectory) { NSDirectoryEnumerator *directoryEnumerator = [_fileManager enumeratorAtURL:targetURL includingPropertiesForKeys:nil options:(NSDirectoryEnumerationOptions)0 errorHandler:nil]; for (NSURL *url in directoryEnumerator) { if (![self _changeOwnerAndGroupOfItemAtURL:url ownerID:ownerID groupID:groupID needsAuth:&needsAuth error:error]) { return NO; } if (needsAuth) { break; } } } if (!needsAuth) { return YES; } char targetPath[PATH_MAX] = {0}; if (![targetURL.path getFileSystemRepresentation:targetPath maxLength:sizeof(targetPath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Target file (%@) cannot be represented as a valid file name.", targetURL.path.lastPathComponent] }]; } return NO; } char matchPath[PATH_MAX] = {0}; if (![matchURLPath getFileSystemRepresentation:matchPath maxLength:sizeof(matchPath)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Match file (%@) cannot be represented as a valid file name.", matchURL.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; BOOL success = [self _authorizeAndExecuteCommand:SUFileOpChangeOwnerAndGroupCommand sourcePath:targetPath destinationPath:matchPath error:&executeError]; if (!success && error != NULL) { NSString *errorMessage = [NSString stringWithFormat:@"Failed to change owner & group on %@ to match %@ with authorization.", targetURL.path.lastPathComponent, matchURLPath.lastPathComponent]; *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage, NSUnderlyingErrorKey: executeError }]; } return success; } // /usr/bin/touch can be used to update an application, as described in: // https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCConcepts/LSCConcepts.html // The document says LSRegisterURL() can be used as well but this hasn't worked out well for me in practice // Anyway, updating the modification time of the application is important because the system will be aware a new version of your app is available, // Finder will report the correct file size and other metadata for it, URL schemes your app may register will be updated, etc. // Behind the scenes, touch calls to utimes() which is what we use here - unless we need to authenticate - (BOOL)updateModificationAndAccessTimeOfItemAtURL:(NSURL *)targetURL error:(NSError * __autoreleasing *)error { if (![self _itemExistsAtURL:targetURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to update modification & access time because %@ does not exist.", targetURL.path.lastPathComponent] }]; } return NO; } char path[PATH_MAX] = {0}; if (![targetURL.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File to update modification & access time (%@) cannot be represented as a valid file name.", targetURL.path.lastPathComponent] }]; } return NO; } int fileDescriptor = open(path, O_RDONLY | O_SYMLINK); if (fileDescriptor == -1) { if (error != NULL) { *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to open file descriptor to %@", targetURL.path.lastPathComponent] }]; } return NO; } // Using futimes() because utimes() follows symbolic links BOOL updatedTime = (futimes(fileDescriptor, NULL) == 0); close(fileDescriptor); if (updatedTime) { return YES; } if (errno != EACCES) { if (error != NULL) { *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to update modification & access time for %@", targetURL.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; BOOL success = [self _authorizeAndExecuteCommand:SUFileOpUpdateModificationAndAccessTimeCommand sourcePath:path destinationPath:NULL error:&executeError]; if (!success && error != NULL) { NSString *errorMessage = [NSString stringWithFormat:@"Failed to update modification & access time on %@ with authorization.", targetURL.path.lastPathComponent]; *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage, NSUnderlyingErrorKey: executeError }]; } return success; } // Creates a directory at the item pointed by url // An item cannot already exist at the url, but the parent must be a directory that exists - (BOOL)makeDirectoryAtURL:(NSURL *)url error:(NSError * __autoreleasing *)error { if ([self _itemExistsAtURL:url]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteFileExistsError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to create directory because file %@ already exists.", url.path.lastPathComponent] }]; } return NO; } NSURL *parentURL = [url URLByDeletingLastPathComponent]; BOOL isParentADirectory = NO; if (![self _itemExistsAtURL:parentURL isDirectory:&isParentADirectory] || !isParentADirectory) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to create directory because parent directory %@ does not exist.", parentURL.path.lastPathComponent] }]; } return NO; } NSError *createDirectoryError = nil; if ([_fileManager createDirectoryAtURL:url withIntermediateDirectories:NO attributes:nil error:&createDirectoryError]) { return YES; } if (!NS_HAS_PERMISSION_ERROR(createDirectoryError)) { if (error != NULL) { *error = createDirectoryError; } return NO; } char path[PATH_MAX] = {0}; if (![url.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Directory to create (%@) cannot be represented as a valid file name.", url.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; BOOL success = [self _authorizeAndExecuteCommand:SUFileOpMakeDirectoryCommand sourcePath:path destinationPath:NULL error:&executeError]; if (!success) { if (error != NULL) { NSString *errorMessage = [NSString stringWithFormat:@"Failed to make directory %@ with authorization.", url.path.lastPathComponent]; *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: errorMessage, NSUnderlyingErrorKey: executeError }]; } } return success; } - (NSURL *)makeTemporaryDirectoryWithPreferredName:(NSString *)preferredName appropriateForDirectoryURL:(NSURL *)directoryURL error:(NSError * __autoreleasing *)error { NSError *tempError = nil; NSURL *tempURL = [_fileManager URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:directoryURL create:YES error:&tempError]; if (tempURL != nil) { return tempURL; } // It is pretty unlikely in my testing we will get here, but just in case we do, we should create a directory inside // the directory pointed by directoryURL, using the preferredName NSURL *desiredURL = [directoryURL URLByAppendingPathComponent:preferredName]; NSUInteger tagIndex = 1; while ([self _itemExistsAtURL:desiredURL] && tagIndex <= 9999) { desiredURL = [directoryURL URLByAppendingPathComponent:[preferredName stringByAppendingFormat:@" (%lu)", (unsigned long)++tagIndex]]; } return [self makeDirectoryAtURL:desiredURL error:error] ? desiredURL : nil; } - (BOOL)removeItemAtURL:(NSURL *)url error:(NSError * __autoreleasing *)error { if (![self _itemExistsAtURL:url]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to remove file %@ because it does not exist.", url.path.lastPathComponent] }]; } return NO; } NSError *removeError = nil; if ([_fileManager removeItemAtURL:url error:&removeError]) { return YES; } if (!NS_HAS_PERMISSION_ERROR(removeError)) { if (error != NULL) { *error = removeError; } return NO; } char path[PATH_MAX] = {0}; if (![url.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File to remove (%@) cannot be represented as a valid file name.", url.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; BOOL success = [self _authorizeAndExecuteCommand:SUFileOpRemoveCommand sourcePath:path destinationPath:NULL error:&executeError]; if (!success && error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAuthenticationFailure userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to remove %@ with authentication.", url.path.lastPathComponent], NSUnderlyingErrorKey: executeError }]; } return success; } - (BOOL)moveItemAtURLToTrash:(NSURL *)url error:(NSError *__autoreleasing *)error { if (![self _itemExistsAtURL:url]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to move %@ to the trash because the file does not exist.", url.path.lastPathComponent] }]; } return NO; } NSURL *trashURL = nil; BOOL canUseNewTrashAPI = YES; #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1080 canUseNewTrashAPI = [SUOperatingSystem isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 8, 0}]; if (!canUseNewTrashAPI) { FSRef trashRef; if (FSFindFolder(kUserDomain, kTrashFolderType, kDontCreateFolder, &trashRef) == noErr) { trashURL = CFBridgingRelease(CFURLCreateFromFSRef(kCFAllocatorDefault, &trashRef)); } } #endif if (canUseNewTrashAPI) { trashURL = [_fileManager URLForDirectory:NSTrashDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; } if (trashURL == nil) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: @"Failed to locate the user's trash folder." }]; } return NO; } // In the rare worst case scenario, our temporary directory will be labeled incomplete and be in the user's trash directory, // indicating that whatever inside of there is not yet completely moved. // Regardless, we want the item to be in our Volume before we try moving it to the trash NSString *preferredName = [url.lastPathComponent.stringByDeletingPathExtension stringByAppendingString:@" (Incomplete Files)"]; NSURL *tempDirectory = [self makeTemporaryDirectoryWithPreferredName:preferredName appropriateForDirectoryURL:trashURL error:error]; if (tempDirectory == nil) { return NO; } NSString *urlLastPathComponent = url.lastPathComponent; NSURL *tempItemURL = [tempDirectory URLByAppendingPathComponent:urlLastPathComponent]; if (![self moveItemAtURL:url toURL:tempItemURL error:error]) { // If we can't move the item at url, just remove it completely; chances are it's not going to be missed [self removeItemAtURL:url error:NULL]; [self removeItemAtURL:tempDirectory error:NULL]; return NO; } if (![self changeOwnerAndGroupOfItemAtRootURL:tempItemURL toMatchURL:trashURL error:error]) { // Removing the item inside of the temp directory is better than trying to move the item to the trash with incorrect ownership [self removeItemAtURL:tempDirectory error:NULL]; return NO; } // If we get here, we should be able to trash the item normally without authentication BOOL success = NO; #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1080 if (!canUseNewTrashAPI) { NSString *tempParentPath = tempItemURL.URLByDeletingLastPathComponent.path; success = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:tempParentPath destination:@"" files:@[tempItemURL.lastPathComponent] tag:NULL]; if (!success && error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to move file %@ into the trash.", tempItemURL.lastPathComponent] }]; } } #endif if (canUseNewTrashAPI) { NSError *trashError = nil; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" success = [_fileManager trashItemAtURL:tempItemURL resultingItemURL:NULL error:&trashError]; #pragma clang diagnostic pop if (!success && error != NULL) { *error = trashError; } } [self removeItemAtURL:tempDirectory error:NULL]; return success; } // Unlike other methods, authorization is required to execute this method successfully - (BOOL)executePackageAtURL:(NSURL *)packageURL error:(NSError * __autoreleasing *)error { if (![self _itemExistsAtURL:packageURL]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to execute package %@ because the file does not exist.", packageURL.path.lastPathComponent] }]; } return NO; } char path[PATH_MAX] = {0}; if (![packageURL.path getFileSystemRepresentation:path maxLength:sizeof(path)]) { if (error != NULL) { *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadInvalidFileNameError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Package to execute (%@) cannot be represented as a valid file name.", packageURL.path.lastPathComponent] }]; } return NO; } NSError *executeError = nil; BOOL success = [self _authorizeAndExecuteCommand:SUFileOpInstallCommand sourcePath:path destinationPath:NULL error:&executeError]; if (!success && error != NULL) { NSString* errorMessage = @"Failed to execute package installer."; *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:@{NSLocalizedDescriptionKey: errorMessage, NSUnderlyingErrorKey: executeError}]; } return success; } @end #pragma clang diagnostic pop ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUGuidedPackageInstaller.h ================================================ // // SUGuidedPackageInstaller.h // Sparkle // // Created by Graham Miln on 14/05/2010. // Copyright 2010 Dragon Systems Software Limited. All rights reserved. // /*! # Sparkle Guided Installations A guided installation allows Sparkle to download and install a package (pkg) or multi-package (mpkg) without user interaction. The installer package is installed using macOS's built-in command line installer, `/usr/sbin/installer`. No installation interface is shown to the user. A guided installation can be started by applications other than the application being replaced. This is particularly useful where helper applications or agents are used. ## To Do - Replace the use of `AuthorizationExecuteWithPrivilegesAndWait`. This method remains because it is well supported and tested. Ideally a helper tool or XPC would be used. */ #ifndef SUGUIDEDPACKAGEINSTALLER_H #define SUGUIDEDPACKAGEINSTALLER_H #import #import "Sparkle.h" #import "SUInstaller.h" @interface SUGuidedPackageInstaller : SUInstaller /*! Perform the guided installation */ + (void)performInstallationToPath:(NSString *)path fromPath:(NSString *)installerGuide host:(SUHost *)host fileOperationToolPath:(NSString *)fileOperationToolPath versionComparator:(id)comparator completionHandler:(void (^)(NSError *))completionHandler; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUGuidedPackageInstaller.m ================================================ // // SUGuidedPackageInstaller.m // Sparkle // // Created by Graham Miln on 14/05/2010. // Copyright 2010 Dragon Systems Software Limited. All rights reserved. // #import "SUGuidedPackageInstaller.h" #import "SUFileManager.h" @implementation SUGuidedPackageInstaller + (void)performInstallationToPath:(NSString *)destinationPath fromPath:(NSString *)packagePath host:(SUHost *)__unused host fileOperationToolPath:(NSString *)fileOperationToolPath versionComparator:(id)__unused comparator completionHandler:(void (^)(NSError *))completionHandler { SUParameterAssert(packagePath); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ SUFileManager *fileManager = [SUFileManager fileManagerWithAuthorizationToolPath:fileOperationToolPath]; NSError *error = nil; BOOL validInstallation = [fileManager executePackageAtURL:[NSURL fileURLWithPath:packagePath] error:&error]; dispatch_async(dispatch_get_main_queue(), ^{ [self finishInstallationToPath:destinationPath withResult:validInstallation error:error completionHandler:completionHandler]; }); }); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUHost.h ================================================ // // SUHost.h // Sparkle // // Copyright 2008 Andy Matuschak. All rights reserved. // #import #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #if __MAC_OS_X_VERSION_MAX_ALLOWED < 101000 typedef struct { NSInteger majorVersion; NSInteger minorVersion; NSInteger patchVersion; } NSOperatingSystemVersion; #endif @interface SUHost : NSObject @property (strong, readonly) NSBundle *bundle; - (instancetype)initWithBundle:(NSBundle *)aBundle; @property (readonly, copy) NSString *bundlePath; @property (readonly) BOOL allowsAutomaticUpdates; @property (readonly, copy) NSString *installationPath; @property (readonly, copy) NSString *name; @property (readonly, copy) NSString *version; @property (readonly, copy) NSString *displayVersion; @property (readonly, copy) NSImage *icon; @property (getter=isRunningOnReadOnlyVolume, readonly) BOOL runningOnReadOnlyVolume; @property (getter=isBackgroundApplication, readonly) BOOL backgroundApplication; @property (readonly, copy) NSString *publicDSAKey; @property (readonly, copy) NSArray *systemProfile; - (id)objectForInfoDictionaryKey:(NSString *)key; - (BOOL)boolForInfoDictionaryKey:(NSString *)key; - (id)objectForUserDefaultsKey:(NSString *)defaultName; - (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName; - (BOOL)boolForUserDefaultsKey:(NSString *)defaultName; - (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName; - (id)objectForKey:(NSString *)key; - (BOOL)boolForKey:(NSString *)key; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUHost.m ================================================ // // SUHost.m // Sparkle // // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUHost.h" #import "SUConstants.h" #import "SUSystemProfiler.h" #include // For statfs for isRunningOnReadOnlyVolume #import "SULog.h" #if __MAC_OS_X_VERSION_MAX_ALLOWED < 101000 @interface NSProcessInfo () - (NSOperatingSystemVersion)operatingSystemVersion; @end #endif @interface SUHost () @property (strong, readwrite) NSBundle *bundle; @property (copy) NSString *defaultsDomain; @property (assign) BOOL usesStandardUserDefaults; @end @implementation SUHost @synthesize bundle; @synthesize defaultsDomain; @synthesize usesStandardUserDefaults; - (instancetype)initWithBundle:(NSBundle *)aBundle { if ((self = [super init])) { SUParameterAssert(aBundle); self.bundle = aBundle; if (![self.bundle bundleIdentifier]) { SULog(@"Error: the bundle being updated at %@ has no %@! This will cause preference read/write to not work properly.", self.bundle, kCFBundleIdentifierKey); } self.defaultsDomain = [self.bundle objectForInfoDictionaryKey:SUDefaultsDomainKey]; if (!self.defaultsDomain) { self.defaultsDomain = [self.bundle bundleIdentifier]; } // If we're using the main bundle's defaults we'll use the standard user defaults mechanism, otherwise we have to get CF-y. NSString *mainBundleIdentifier = NSBundle.mainBundle.bundleIdentifier; usesStandardUserDefaults = !self.defaultsDomain || [self.defaultsDomain isEqualToString:mainBundleIdentifier]; } return self; } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self bundlePath], [self installationPath]]; } - (NSString *)bundlePath { return [self.bundle bundlePath]; } - (BOOL)allowsAutomaticUpdates { // Does the developer want us to disable automatic updates? NSNumber *developerAllowsAutomaticUpdates = [self objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey]; if (developerAllowsAutomaticUpdates != nil && !developerAllowsAutomaticUpdates.boolValue) { return NO; } // Can we automatically update in the background without bugging the user (e.g, with a administrator password prompt)? // Note it's very well possible to have the bundle be writable but not be able to write into the parent directory // And if the bundle isn't writable, but we can write into the parent directory, we will still need to authorize to replace it NSString *bundlePath = [self bundlePath]; return [[NSFileManager defaultManager] isWritableFileAtPath:bundlePath.stringByDeletingLastPathComponent] && [[NSFileManager defaultManager] isWritableFileAtPath:bundlePath]; } - (NSString *)installationPath { if (SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME) { // We'll install to "#{CFBundleName}.app", but only if that path doesn't already exist. If we're "Foo 4.2.app," and there's a "Foo.app" in this directory, we don't want to overwrite it! But if there's no "Foo.app," we'll take that name. NSString *normalizedAppPath = [[[self.bundle bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self.bundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey], [[self.bundle bundlePath] pathExtension]]]; if (![[NSFileManager defaultManager] fileExistsAtPath:normalizedAppPath]) { return normalizedAppPath; } } return [self.bundle bundlePath]; } - (NSString *__nonnull)name { NSString *name; // Allow host bundle to provide a custom name name = [self objectForInfoDictionaryKey:@"SUBundleName"]; if (name && name.length > 0) return name; name = [self.bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]; if (name && name.length > 0) return name; name = [self objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey]; if (name && name.length > 0) return name; return [[[NSFileManager defaultManager] displayNameAtPath:[self.bundle bundlePath]] stringByDeletingPathExtension]; } - (NSString *__nonnull)version { NSString *version = [self.bundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]; if (!version || [version isEqualToString:@""]) [NSException raise:@"SUNoVersionException" format:@"This host (%@) has no %@! This attribute is required.", [self bundlePath], (__bridge NSString *)kCFBundleVersionKey]; return version; } - (NSString *__nonnull)displayVersion { NSString *shortVersionString = [self.bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; if (shortVersionString) return shortVersionString; else return [self version]; // Fall back on the normal version string. } - (NSImage *__nonnull)icon { // Cache the application icon. NSString *iconPath = [self.bundle pathForResource:[self.bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType:@"icns"]; // According to the macOS docs, "CFBundleIconFile - This key identifies the file containing // the icon for the bundle. The filename you specify does not need to include the .icns // extension, although it may." // // However, if it *does* include the '.icns' the above method fails (tested on macOS 10.3.9) so we'll also try: if (!iconPath) { iconPath = [self.bundle pathForResource:[self.bundle objectForInfoDictionaryKey:@"CFBundleIconFile"] ofType:nil]; } NSImage *icon = [[NSImage alloc] initWithContentsOfFile:iconPath]; // Use a default icon if none is defined. if (!icon) { BOOL isMainBundle = (self.bundle == [NSBundle mainBundle]); NSString *fileType = isMainBundle ? (__bridge NSString *)kUTTypeApplication : (__bridge NSString *)kUTTypeBundle; icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileType]; } return icon; } - (BOOL)isRunningOnReadOnlyVolume { struct statfs statfs_info; statfs([[self.bundle bundlePath] fileSystemRepresentation], &statfs_info); return (statfs_info.f_flags & MNT_RDONLY) != 0; } - (BOOL)isBackgroundApplication { return ([[NSApplication sharedApplication] activationPolicy] == NSApplicationActivationPolicyAccessory); } - (NSString *__nullable)publicDSAKey { // Maybe the key is just a string in the Info.plist. NSString *key = [self.bundle objectForInfoDictionaryKey:SUPublicDSAKeyKey]; if (key) { return key; } // More likely, we've got a reference to a Resources file by filename: NSString *keyFilename = [self objectForInfoDictionaryKey:SUPublicDSAKeyFileKey]; if (!keyFilename) { return nil; } NSString *keyPath = [self.bundle pathForResource:keyFilename ofType:nil]; if (!keyPath) { return nil; } return [NSString stringWithContentsOfFile:keyPath encoding:NSASCIIStringEncoding error:nil]; } - (NSArray *)systemProfile { return [[SUSystemProfiler sharedSystemProfiler] systemProfileArrayForHost:self]; } - (id)objectForInfoDictionaryKey:(NSString *)key { return [self.bundle objectForInfoDictionaryKey:key]; } - (BOOL)boolForInfoDictionaryKey:(NSString *)key { return [[self objectForInfoDictionaryKey:key] boolValue]; } - (id)objectForUserDefaultsKey:(NSString *)defaultName { if (!defaultName || !self.defaultsDomain) { return nil; } // Under Tiger, CFPreferencesCopyAppValue doesn't get values from NSRegistrationDomain, so anything // passed into -[NSUserDefaults registerDefaults:] is ignored. The following line falls // back to using NSUserDefaults, but only if the host bundle is the main bundle. if (self.usesStandardUserDefaults) { return [[NSUserDefaults standardUserDefaults] objectForKey:defaultName]; } CFPropertyListRef obj = CFPreferencesCopyAppValue((__bridge CFStringRef)defaultName, (__bridge CFStringRef)self.defaultsDomain); return CFBridgingRelease(obj); } - (void)setObject:(id)value forUserDefaultsKey:(NSString *)defaultName { if (self.usesStandardUserDefaults) { [[NSUserDefaults standardUserDefaults] setObject:value forKey:defaultName]; } else { CFPreferencesSetValue((__bridge CFStringRef)defaultName, (__bridge CFPropertyListRef)(value), (__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); CFPreferencesSynchronize((__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); } } - (BOOL)boolForUserDefaultsKey:(NSString *)defaultName { if (self.usesStandardUserDefaults) { return [[NSUserDefaults standardUserDefaults] boolForKey:defaultName]; } BOOL value; CFPropertyListRef plr = CFPreferencesCopyAppValue((__bridge CFStringRef)defaultName, (__bridge CFStringRef)self.defaultsDomain); if (plr == NULL) { value = NO; } else { value = (BOOL)CFBooleanGetValue((CFBooleanRef)plr); CFRelease(plr); } return value; } - (void)setBool:(BOOL)value forUserDefaultsKey:(NSString *)defaultName { if (self.usesStandardUserDefaults) { [[NSUserDefaults standardUserDefaults] setBool:value forKey:defaultName]; } else { CFPreferencesSetValue((__bridge CFStringRef)defaultName, (__bridge CFBooleanRef) @(value), (__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); CFPreferencesSynchronize((__bridge CFStringRef)self.defaultsDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); } } - (id)objectForKey:(NSString *)key { return [self objectForUserDefaultsKey:key] ? [self objectForUserDefaultsKey:key] : [self objectForInfoDictionaryKey:key]; } - (BOOL)boolForKey:(NSString *)key { return [self objectForUserDefaultsKey:key] ? [self boolForUserDefaultsKey:key] : [self boolForInfoDictionaryKey:key]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUInstaller.h ================================================ // // SUInstaller.h // Sparkle // // Created by Andy Matuschak on 4/10/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUINSTALLER_H #define SUINSTALLER_H #import #import "SUVersionComparisonProtocol.h" @class SUHost; @interface SUInstaller : NSObject + (NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolder forHost:(SUHost *)host isPackage:(BOOL *)isPackagePtr isGuided:(BOOL *)isGuidedPtr; + (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)host installationPath:(NSString *)installationPath fileOperationToolPath:(NSString *)fileOperationToolPath versionComparator:(id)comparator completionHandler:(void (^)(NSError *))completionHandler; + (void)finishInstallationToPath:(NSString *)installationPath withResult:(BOOL)result error:(NSError *)error completionHandler:(void (^)(NSError *))completionHandler; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUInstaller.m ================================================ // // SUInstaller.m // Sparkle // // Created by Andy Matuschak on 4/10/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUInstaller.h" #import "SUPlainInstaller.h" #import "SUPackageInstaller.h" #import "SUGuidedPackageInstaller.h" #import "SUHost.h" #import "SUConstants.h" #import "SULog.h" @implementation SUInstaller + (BOOL)isAliasFolderAtPath:(NSString *)path { NSNumber *aliasFlag = nil; [[NSURL fileURLWithPath:path] getResourceValue:&aliasFlag forKey:NSURLIsAliasFileKey error:nil]; NSNumber *directoryFlag = nil; [[NSURL fileURLWithPath:path] getResourceValue:&directoryFlag forKey:NSURLIsDirectoryKey error:nil]; return aliasFlag.boolValue && directoryFlag.boolValue; } + (NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolder forHost:(SUHost *)host isPackage:(BOOL *)isPackagePtr isGuided:(BOOL *)isGuidedPtr { SUParameterAssert(inUpdateFolder); SUParameterAssert(host); // Search subdirectories for the application NSString *currentFile, *newAppDownloadPath = nil, *bundleFileName = [[host bundlePath] lastPathComponent], *alternateBundleFileName = [[host name] stringByAppendingPathExtension:[[host bundlePath] pathExtension]]; BOOL isPackage = NO; BOOL isGuided = NO; NSString *fallbackPackagePath = nil; NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:inUpdateFolder]; NSString *bundleFileNameNoExtension = [bundleFileName stringByDeletingPathExtension]; while ((currentFile = [dirEnum nextObject])) { NSString *currentPath = [inUpdateFolder stringByAppendingPathComponent:currentFile]; NSString *currentFilename = [currentFile lastPathComponent]; NSString *currentExtension = [currentFile pathExtension]; NSString *currentFilenameNoExtension = [currentFilename stringByDeletingPathExtension]; if ([currentFilename isEqualToString:bundleFileName] || [currentFilename isEqualToString:alternateBundleFileName]) // We found one! { isPackage = NO; newAppDownloadPath = currentPath; break; } else if ([currentExtension isEqualToString:@"pkg"] || [currentExtension isEqualToString:@"mpkg"]) { if ([currentFilenameNoExtension isEqualToString:bundleFileNameNoExtension]) { isPackage = YES; newAppDownloadPath = currentPath; break; } else { // Remember any other non-matching packages we have seen should we need to use one of them as a fallback. fallbackPackagePath = currentPath; } } else { // Try matching on bundle identifiers in case the user has changed the name of the host app NSBundle *incomingBundle = [NSBundle bundleWithPath:currentPath]; NSString *hostBundleIdentifier = host.bundle.bundleIdentifier; if (incomingBundle && [incomingBundle.bundleIdentifier isEqualToString:hostBundleIdentifier]) { isPackage = NO; newAppDownloadPath = currentPath; break; } } // Some DMGs have symlinks into /Applications! That's no good! if ([self isAliasFolderAtPath:currentPath]) [dirEnum skipDescendents]; } // We don't have a valid path. Try to use the fallback package. if (newAppDownloadPath == nil && fallbackPackagePath != nil) { isPackage = YES; newAppDownloadPath = fallbackPackagePath; } if (isPackage) { // foo.app -> foo.sparkle_guided.pkg or foo.sparkle_guided.mpkg if ([[[newAppDownloadPath stringByDeletingPathExtension] pathExtension] isEqualToString:@"sparkle_guided"]) { isGuided = YES; } } if (isPackagePtr) *isPackagePtr = isPackage; if (isGuidedPtr) *isGuidedPtr = isGuided; if (!newAppDownloadPath) { SULog(@"Searched %@ for %@.(app|pkg)", inUpdateFolder, bundleFileNameNoExtension); } return newAppDownloadPath; } + (void)installFromUpdateFolder:(NSString *)inUpdateFolder overHost:(SUHost *)host installationPath:(NSString *)installationPath fileOperationToolPath:(NSString *)fileOperationToolPath versionComparator:(id)comparator completionHandler:(void (^)(NSError *))completionHandler { BOOL isPackage = NO; BOOL isGuided = NO; NSString *newAppDownloadPath = [self installSourcePathInUpdateFolder:inUpdateFolder forHost:host isPackage:&isPackage isGuided:&isGuided]; if (newAppDownloadPath == nil) { [self finishInstallationToPath:installationPath withResult:NO error:[NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingUpdateError userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't find an appropriate update in the downloaded package." }] completionHandler:completionHandler]; } else { if (isPackage && isGuided) { [SUGuidedPackageInstaller performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host fileOperationToolPath:fileOperationToolPath versionComparator:comparator completionHandler:completionHandler]; } else if (isPackage) { [SUPackageInstaller performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host fileOperationToolPath:fileOperationToolPath versionComparator:comparator completionHandler:completionHandler]; } else { [SUPlainInstaller performInstallationToPath:installationPath fromPath:newAppDownloadPath host:host fileOperationToolPath:fileOperationToolPath versionComparator:comparator completionHandler:completionHandler]; } } } + (void)mdimportInstallationPath:(NSString *)installationPath { // *** GETS CALLED ON NON-MAIN THREAD! SULog(@"mdimporting"); NSTask *mdimport = [[NSTask alloc] init]; [mdimport setLaunchPath:@"/usr/bin/mdimport"]; [mdimport setArguments:@[installationPath]]; @try { [mdimport launch]; [mdimport waitUntilExit]; } @catch (NSException *launchException) { // No big deal. SULog(@"Error: %@", [launchException description]); } } + (void)finishInstallationToPath:(NSString *)installationPath withResult:(BOOL)result error:(NSError *)error completionHandler:(void (^)(NSError *))completionHandler { if (result) { [self mdimportInstallationPath:installationPath]; dispatch_async(dispatch_get_main_queue(), ^{ completionHandler(nil); }); } else { if (!error) { error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:nil]; } dispatch_async(dispatch_get_main_queue(), ^{ completionHandler(error); }); } } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SULog.h ================================================ /* * SULog.h * EyeTV * * Created by Uli Kusterer on 12/03/2009. * Copyright 2008 Elgato Systems GmbH. All rights reserved. * */ /* Log output for troubleshooting Sparkle failures on end-user machines. Your tech support will hug you if you tell them about this. */ #ifndef SULOG_H #define SULOG_H // ----------------------------------------------------------------------------- // Headers: // ----------------------------------------------------------------------------- #include // ----------------------------------------------------------------------------- // Prototypes: // ----------------------------------------------------------------------------- void SUClearLog(void); void SULog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SULog.m ================================================ /* * SULog.m * EyeTV * * Created by Uli Kusterer on 12/03/2009. * Copyright 2009 Elgato Systems GmbH. All rights reserved. * */ // ----------------------------------------------------------------------------- // Headers: // ----------------------------------------------------------------------------- #include "SULog.h" // ----------------------------------------------------------------------------- // Constants: // ----------------------------------------------------------------------------- static NSString *const SULogFilePath = @"~/Library/Logs/SparkleUpdateLog.log"; // ----------------------------------------------------------------------------- // SUClearLog: // Erase the log at the start of an update. We don't want to litter the // user's hard disk with logging data that's mostly unused, so each app // should clear the log before it starts updating, so only the most recent // update is kept around. // // TAKES: // sender - Object that sent this message, typically of type X. // ----------------------------------------------------------------------------- void SUClearLog(void) { FILE *logfile = fopen([[SULogFilePath stringByExpandingTildeInPath] fileSystemRepresentation], "w"); if (logfile) { fclose(logfile); SULog(@"===== %@ =====", [[NSFileManager defaultManager] displayNameAtPath:[[NSBundle mainBundle] bundlePath]]); } } // ----------------------------------------------------------------------------- // SULog: // Like NSLog, but logs to one specific log file. Each line is prefixed // with the current date and time, to help in regressing issues. // // TAKES: // format - NSLog/printf-style format string. // ... - More parameters depending on format string's contents. // ----------------------------------------------------------------------------- void SULog(NSString *format, ...) { static BOOL loggedYet = NO; if (!loggedYet) { loggedYet = YES; SUClearLog(); } va_list ap; va_start(ap, format); NSString *theStr = [[NSString alloc] initWithFormat:format arguments:ap]; NSLog(@"Sparkle: %@", theStr); FILE *logfile = fopen([[SULogFilePath stringByExpandingTildeInPath] fileSystemRepresentation], "a"); if (logfile) { theStr = [NSString stringWithFormat:@"%@: %@\n", [NSDate date], theStr]; NSData *theData = [theStr dataUsingEncoding:NSUTF8StringEncoding]; fwrite([theData bytes], 1, [theData length], logfile); fclose(logfile); } va_end(ap); } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUOperatingSystem.h ================================================ // // SUOperatingSystem.h // Sparkle // // Copyright © 2015 Sparkle Project. All rights reserved. // #import @interface SUOperatingSystem : NSObject + (NSOperatingSystemVersion)operatingSystemVersion; + (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version; + (NSString *)systemVersionString; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUOperatingSystem.m ================================================ // // SUOperatingSystem.m // Sparkle // // Copyright © 2015 Sparkle Project. All rights reserved. // #import "SUOperatingSystem.h" @implementation SUOperatingSystem + (NSOperatingSystemVersion)operatingSystemVersion { #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wselector" // Xcode 5.1.1: operatingSystemVersion is clearly declared, must warn due to a compiler bug? if (![NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) #pragma clang diagnostic pop { NSOperatingSystemVersion version = { 0, 0, 0 }; NSURL *coreServices = [[NSFileManager defaultManager] URLForDirectory:NSCoreServiceDirectory inDomain:NSSystemDomainMask appropriateForURL:nil create:NO error:nil]; NSURL *url = [coreServices URLByAppendingPathComponent:@"SystemVersion.plist"]; assert(url != nil); NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:url]; NSArray *components = [ [dictionary objectForKey: @"ProductVersion"] componentsSeparatedByString:@"."]; version.majorVersion = components.count > 0 ? [ [components objectAtIndex:0] integerValue] : 0; version.minorVersion = components.count > 1 ? [ [components objectAtIndex:1] integerValue] : 0; version.patchVersion = components.count > 2 ? [ [components objectAtIndex:2] integerValue] : 0; return version; } #endif #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" return [[NSProcessInfo processInfo] operatingSystemVersion]; #pragma clang diagnostic pop } + (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version { const NSOperatingSystemVersion systemVersion = self.operatingSystemVersion; if (systemVersion.majorVersion == version.majorVersion) { if (systemVersion.minorVersion == version.minorVersion) { return systemVersion.patchVersion >= version.patchVersion; } return systemVersion.minorVersion >= version.minorVersion; } return systemVersion.majorVersion >= version.majorVersion; } + (NSString *)systemVersionString { NSOperatingSystemVersion version = self.operatingSystemVersion; return [NSString stringWithFormat:@"%ld.%ld.%ld", (long)version.majorVersion, (long)version.minorVersion, (long)version.patchVersion]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUPackageInstaller.h ================================================ // // SUPackageInstaller.h // Sparkle // // Created by Andy Matuschak on 4/10/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUPACKAGEINSTALLER_H #define SUPACKAGEINSTALLER_H #import #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUPlainInstaller.h" @interface SUPackageInstaller : SUPlainInstaller @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUPackageInstaller.m ================================================ // // SUPackageInstaller.m // Sparkle // // Created by Andy Matuschak on 4/10/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUPackageInstaller.h" #import #import "SUConstants.h" @implementation SUPackageInstaller + (void)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)__unused host fileOperationToolPath:(NSString *)__unused fileOperationToolPath versionComparator:(id)__unused comparator completionHandler:(void (^)(NSError *))completionHandler { // Run installer using the "open" command to ensure it is launched in front of current application. // -W = wait until the app has quit. // -n = Open another instance if already open. // -b = app bundle identifier NSString *command = @"/usr/bin/open"; NSArray *args = @[@"-W", @"-n", @"-b", @"com.apple.installer", path]; if (![[NSFileManager defaultManager] fileExistsAtPath:command]) { NSError *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUMissingInstallerToolError userInfo:@{ NSLocalizedDescriptionKey: @"Couldn't find Apple's installer tool!" }]; [self finishInstallationToPath:installationPath withResult:NO error:error completionHandler:completionHandler]; return; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSTask *installer = [NSTask launchedTaskWithLaunchPath:command arguments:args]; [installer waitUntilExit]; // Known bug: if the installation fails or is canceled, Sparkle goes ahead and restarts, thinking everything is fine. dispatch_async(dispatch_get_main_queue(), ^{ [self finishInstallationToPath:installationPath withResult:YES error:nil completionHandler:completionHandler]; }); }); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUPipedUnarchiver.h ================================================ // // SUPipedUnarchiver.h // Sparkle // // Created by Andy Matuschak on 6/16/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUPIPEDUNARCHIVER_H #define SUPIPEDUNARCHIVER_H #import #import "SUUnarchiver.h" @interface SUPipedUnarchiver : SUUnarchiver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUPipedUnarchiver.m ================================================ // // SUPipedUnarchiver.m // Sparkle // // Created by Andy Matuschak on 6/16/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUPipedUnarchiver.h" #import "SUUnarchiver_Private.h" #import "SULog.h" @implementation SUPipedUnarchiver + (SEL)selectorConformingToTypeOfPath:(NSString *)path { static NSDictionary *typeSelectorDictionary; if (!typeSelectorDictionary) typeSelectorDictionary = @{ @".zip": @"extractZIP", @".tar": @"extractTAR", @".tar.gz": @"extractTGZ", @".tgz": @"extractTGZ", @".tar.bz2": @"extractTBZ", @".tbz": @"extractTBZ", @".tar.xz": @"extractTXZ", @".txz": @"extractTXZ", @".tar.lzma": @"extractTXZ"}; NSString *lastPathComponent = [path lastPathComponent]; for (NSString *currentType in typeSelectorDictionary) { NSString *value = [typeSelectorDictionary objectForKey:currentType]; assert(value); if ([currentType length] > [lastPathComponent length]) continue; if ([[lastPathComponent substringFromIndex:[lastPathComponent length] - [currentType length]] isEqualToString:currentType]) { return NSSelectorFromString(value); } } return NULL; } - (void)start { [NSThread detachNewThreadSelector:[[self class] selectorConformingToTypeOfPath:self.archivePath] toTarget:self withObject:nil]; } + (BOOL)canUnarchivePath:(NSString *)path { return ([self selectorConformingToTypeOfPath:path] != nil); } // This method abstracts the types that use a command line tool piping data from stdin. - (void)extractArchivePipingDataToCommand:(NSString *)command args:(NSArray*)args { // *** GETS CALLED ON NON-MAIN THREAD!!! @autoreleasepool { NSString *destination = [self.archivePath stringByDeletingLastPathComponent]; SULog(@"Extracting using '%@' '%@' < '%@' '%@'", command, [args componentsJoinedByString:@"' '"], self.archivePath, destination); // Get the file size. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.archivePath error:nil]; NSUInteger expectedLength = [[attributes objectForKey:NSFileSize] unsignedIntegerValue]; if (expectedLength > 0) { NSFileHandle *archiveInput = [NSFileHandle fileHandleForReadingAtPath:self.archivePath]; NSPipe *pipe = [NSPipe pipe]; NSFileHandle *archiveOutput = [pipe fileHandleForWriting]; NSTask *task = [[NSTask alloc] init]; [task setStandardInput:[pipe fileHandleForReading]]; [task setStandardError:[NSFileHandle fileHandleWithStandardError]]; [task setStandardOutput:[NSFileHandle fileHandleWithStandardOutput]]; [task setLaunchPath:command]; [task setArguments:[args arrayByAddingObject:destination]]; [task launch]; NSUInteger bytesRead = 0; do { NSData *data = [archiveInput readDataOfLength:256*1024]; NSUInteger len = [data length]; if (!len) { break; } bytesRead += len; [archiveOutput writeData:data]; dispatch_async(dispatch_get_main_queue(), ^{ [self notifyDelegateOfProgress:(double)bytesRead / (double)expectedLength]; }); } while(bytesRead < expectedLength); [archiveOutput closeFile]; [task waitUntilExit]; if ([task terminationStatus] == 0) { if (bytesRead == expectedLength) { dispatch_async(dispatch_get_main_queue(), ^{ [self notifyDelegateOfSuccess]; }); return; } else { SULog(@"Extraction failed, command '%@' got only %ld of %ld bytes", command, (long)bytesRead, (long)expectedLength); } } else { SULog(@"Extraction failed, command '%@' returned %d", command, [task terminationStatus]); } } else { SULog(@"Extraction failed, archive '%@' is empty", self.archivePath); } dispatch_async(dispatch_get_main_queue(), ^{ [self notifyDelegateOfFailure]; }); } } - (void)extractTAR { // *** GETS CALLED ON NON-MAIN THREAD!!! [self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-xC"]]; } - (void)extractTGZ { // *** GETS CALLED ON NON-MAIN THREAD!!! [self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-zxC"]]; } - (void)extractTBZ { // *** GETS CALLED ON NON-MAIN THREAD!!! [self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-jxC"]]; } - (void)extractZIP { // *** GETS CALLED ON NON-MAIN THREAD!!! [self extractArchivePipingDataToCommand:@"/usr/bin/ditto" args:@[@"-x",@"-k",@"-"]]; } - (void)extractTXZ { // *** GETS CALLED ON NON-MAIN THREAD!!! [self extractArchivePipingDataToCommand:@"/usr/bin/tar" args:@[@"-zxC"]]; } + (void)load { [self registerImplementation:self]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUPlainInstaller.h ================================================ // // SUPlainInstaller.h // Sparkle // // Created by Andy Matuschak on 4/10/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUPLAININSTALLER_H #define SUPLAININSTALLER_H #import #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUInstaller.h" #import "SUVersionComparisonProtocol.h" @class SUHost; @interface SUPlainInstaller : SUInstaller + (void)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)host fileOperationToolPath:(NSString *)fileOperationToolPath versionComparator:(id)comparator completionHandler:(void (^)(NSError *))completionHandler; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUPlainInstaller.m ================================================ // // SUPlainInstaller.m // Sparkle // // Created by Andy Matuschak on 4/10/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUPlainInstaller.h" #import "SUFileManager.h" #import "SUCodeSigningVerifier.h" #import "SUConstants.h" #import "SUHost.h" #import "SULog.h" @implementation SUPlainInstaller // Returns the bundle version from the specified host that is appropriate to use as a filename, or nil if we're unable to retrieve one + (NSString *)bundleVersionAppropriateForFilenameFromHost:(SUHost *)host { NSString *bundleVersion = [host objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]; NSString *trimmedVersion = @""; if (bundleVersion != nil) { NSMutableCharacterSet *validCharacters = [NSMutableCharacterSet alphanumericCharacterSet]; [validCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@".-()"]]; trimmedVersion = [bundleVersion stringByTrimmingCharactersInSet:[validCharacters invertedSet]]; } return trimmedVersion.length > 0 ? trimmedVersion : nil; } + (BOOL)performInstallationToURL:(NSURL *)installationURL fromUpdateAtURL:(NSURL *)newURL withHost:(SUHost *)host fileOperationToolPath:(NSString *)fileOperationToolPath error:(NSError * __autoreleasing *)error { if (installationURL == nil || newURL == nil) { // this really shouldn't happen but just in case SULog(@"Failed to perform installation because either installation URL (%@) or new URL (%@) is nil", installationURL, newURL); if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:@{ NSLocalizedDescriptionKey: @"Failed to perform installation because the paths to install at and from are not valid" }]; } return NO; } SUFileManager *fileManager = [SUFileManager fileManagerWithAuthorizationToolPath:fileOperationToolPath]; // Create a temporary directory for our new app that resides on our destination's volume NSURL *tempNewDirectoryURL = [fileManager makeTemporaryDirectoryWithPreferredName:[installationURL.lastPathComponent.stringByDeletingPathExtension stringByAppendingString:@" (Incomplete Update)"] appropriateForDirectoryURL:installationURL.URLByDeletingLastPathComponent error:error]; if (tempNewDirectoryURL == nil) { SULog(@"Failed to make new temp directory"); return NO; } // Move the new app to our temporary directory NSString *newURLLastPathComponent = newURL.lastPathComponent; NSURL *newTempURL = [tempNewDirectoryURL URLByAppendingPathComponent:newURLLastPathComponent]; if (![fileManager moveItemAtURL:newURL toURL:newTempURL error:error]) { SULog(@"Failed to move the new app from %@ to its temp directory at %@", newURL.path, newTempURL.path); [fileManager removeItemAtURL:tempNewDirectoryURL error:NULL]; return NO; } // Release our new app from quarantine, fix its owner and group IDs, and update its modification time while it's at our temporary destination // We must leave moving the app to its destination as the final step in installing it, so that // it's not possible our new app can be left in an incomplete state at the final destination NSError *quarantineError = nil; if (![fileManager releaseItemFromQuarantineAtRootURL:newTempURL error:&quarantineError]) { // Not big enough of a deal to fail the entire installation SULog(@"Failed to release quarantine at %@ with error %@", newTempURL.path, quarantineError); } NSURL *oldURL = [NSURL fileURLWithPath:host.bundlePath]; if (oldURL == nil) { // this really shouldn't happen but just in case SULog(@"Failed to construct URL from bundle path: %@", host.bundlePath); if (error != NULL) { *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUInstallationError userInfo:@{ NSLocalizedDescriptionKey: @"Failed to perform installation because a path could not be constructed for the old installation" }]; } return NO; } if (![fileManager changeOwnerAndGroupOfItemAtRootURL:newTempURL toMatchURL:oldURL error:error]) { // But this is big enough of a deal to fail SULog(@"Failed to change owner and group of new app at %@ to match old app at %@", newTempURL.path, oldURL.path); [fileManager removeItemAtURL:tempNewDirectoryURL error:NULL]; return NO; } if (![fileManager updateModificationAndAccessTimeOfItemAtURL:newTempURL error:error]) { // Not a fatal error, but a pretty unfortunate one SULog(@"Failed to update modification and access time of new app at %@", newTempURL.path); } // Decide on a destination name we should use for the older app when we move it around the file system NSString *oldDestinationName = nil; if (SPARKLE_APPEND_VERSION_NUMBER) { NSString *oldBundleVersion = [self bundleVersionAppropriateForFilenameFromHost:host]; oldDestinationName = [oldURL.lastPathComponent.stringByDeletingPathExtension stringByAppendingFormat:@" (%@)", oldBundleVersion != nil ? oldBundleVersion : @"old"]; } else { oldDestinationName = oldURL.lastPathComponent.stringByDeletingPathExtension; } NSString *oldURLExtension = oldURL.pathExtension; NSString *oldDestinationNameWithPathExtension = [oldDestinationName stringByAppendingPathExtension:oldURLExtension]; // Create a temporary directory for our old app that resides on its volume NSURL *tempOldDirectoryURL = [fileManager makeTemporaryDirectoryWithPreferredName:oldDestinationName appropriateForDirectoryURL:oldURL.URLByDeletingLastPathComponent error:error]; if (tempOldDirectoryURL == nil) { SULog(@"Failed to create temporary directory for old app at %@", oldURL.path); [fileManager removeItemAtURL:tempNewDirectoryURL error:NULL]; return NO; } // Move the old app to the temporary directory NSURL *oldTempURL = [tempOldDirectoryURL URLByAppendingPathComponent:oldDestinationNameWithPathExtension]; if (![fileManager moveItemAtURL:oldURL toURL:oldTempURL error:error]) { SULog(@"Failed to move the old app at %@ to a temporary location at %@", oldURL.path, oldTempURL.path); // Just forget about our updated app on failure [fileManager removeItemAtURL:tempNewDirectoryURL error:NULL]; [fileManager removeItemAtURL:tempOldDirectoryURL error:NULL]; return NO; } // Move the new app to its final destination if (![fileManager moveItemAtURL:newTempURL toURL:installationURL error:error]) { SULog(@"Failed to move new app at %@ to final destination %@", newTempURL.path, installationURL.path); // Forget about our updated app on failure [fileManager removeItemAtURL:tempNewDirectoryURL error:NULL]; // Attempt to restore our old app back the way it was on failure [fileManager moveItemAtURL:oldTempURL toURL:oldURL error:NULL]; [fileManager removeItemAtURL:tempOldDirectoryURL error:NULL]; return NO; } // From here on out, we don't really need to bring up authorization if we haven't done so prior SUFileManager *constrainedFileManager = [fileManager fileManagerByPreservingAuthorizationRights]; // Cleanup: move the old app to the trash NSError *trashError = nil; if (![constrainedFileManager moveItemAtURLToTrash:oldTempURL error:&trashError]) { SULog(@"Failed to move %@ to trash with error %@", oldTempURL, trashError); } [constrainedFileManager removeItemAtURL:tempOldDirectoryURL error:NULL]; [constrainedFileManager removeItemAtURL:tempNewDirectoryURL error:NULL]; return YES; } + (void)performInstallationToPath:(NSString *)installationPath fromPath:(NSString *)path host:(SUHost *)host fileOperationToolPath:(NSString *)fileOperationToolPath versionComparator:(id)comparator completionHandler:(void (^)(NSError *))completionHandler { SUParameterAssert(host); BOOL allowDowngrades = SPARKLE_AUTOMATED_DOWNGRADES; // Prevent malicious downgrades if (!allowDowngrades) { if ([comparator compareVersion:[host version] toVersion:[[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]] == NSOrderedDescending) { NSString *errorMessage = [NSString stringWithFormat:@"Sparkle Updater: Possible attack in progress! Attempting to \"upgrade\" from %@ to %@. Aborting update.", [host version], [[NSBundle bundleWithPath:path] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]]; NSError *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUDowngradeError userInfo:@{ NSLocalizedDescriptionKey: errorMessage }]; [self finishInstallationToPath:installationPath withResult:NO error:error completionHandler:completionHandler]; return; } } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error = nil; BOOL result = [self performInstallationToURL:[NSURL fileURLWithPath:installationPath] fromUpdateAtURL:[NSURL fileURLWithPath:path] withHost:host fileOperationToolPath:fileOperationToolPath error:&error]; dispatch_async(dispatch_get_main_queue(), ^{ [self finishInstallationToPath:installationPath withResult:result error:error completionHandler:completionHandler]; }); }); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUProbingUpdateDriver.h ================================================ // // SUProbingUpdateDriver.h // Sparkle // // Created by Andy Matuschak on 5/7/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUPROBINGUPDATEDRIVER_H #define SUPROBINGUPDATEDRIVER_H #import #import "SUBasicUpdateDriver.h" // This replaces the old SUStatusChecker. @interface SUProbingUpdateDriver : SUBasicUpdateDriver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUProbingUpdateDriver.m ================================================ // // SUProbingUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 5/7/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUProbingUpdateDriver.h" #import "SUUpdater.h" @implementation SUProbingUpdateDriver // Stop as soon as we have an answer! Since the superclass implementations are not called, we are responsible for notifying the delegate. - (void)didFindValidUpdate { id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updater:didFindValidUpdate:)]) [updaterDelegate updater:self.updater didFindValidUpdate:self.updateItem]; NSDictionary *userInfo = (self.updateItem != nil) ? @{ SUUpdaterAppcastItemNotificationKey: self.updateItem } : nil; [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidFindValidUpdateNotification object:self.updater userInfo:userInfo]; [self abortUpdate]; } - (void)didNotFindUpdate { id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updaterDidNotFindUpdate:)]) { [updaterDelegate updaterDidNotFindUpdate:self.updater]; } [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater]; [self abortUpdate]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUScheduledUpdateDriver.h ================================================ // // SUScheduledUpdateDriver.h // Sparkle // // Created by Andy Matuschak on 5/6/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUSCHEDULEDUPDATEDRIVER_H #define SUSCHEDULEDUPDATEDRIVER_H #import #import "SUUIBasedUpdateDriver.h" @interface SUScheduledUpdateDriver : SUUIBasedUpdateDriver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUScheduledUpdateDriver.m ================================================ // // SUScheduledUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 5/6/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUScheduledUpdateDriver.h" #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" @interface SUScheduledUpdateDriver () @property (assign) BOOL showErrors; @end @implementation SUScheduledUpdateDriver @synthesize showErrors; - (void)didFindValidUpdate { self.showErrors = YES; // We only start showing errors after we present the UI for the first time. [super didFindValidUpdate]; } - (void)didNotFindUpdate { id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updaterDidNotFindUpdate:)]) { [updaterDelegate updaterDidNotFindUpdate:self.updater]; } [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater]; [self abortUpdate]; // Don't tell the user that no update was found; this was a scheduled update. } - (void)abortUpdateWithError:(NSError *)error { if (self.showErrors) { [super abortUpdateWithError:error]; } else { // Call delegate separately here because otherwise it won't know we stopped. // Normally this gets called by the superclass id updaterDelegate = [self.updater delegate]; if ([updaterDelegate respondsToSelector:@selector(updater:didAbortWithError:)]) { [updaterDelegate updater:self.updater didAbortWithError:error]; } [self abortUpdate]; } } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUStandardVersionComparator.h ================================================ // // SUStandardVersionComparator.h // Sparkle // // Created by Andy Matuschak on 12/21/07. // Copyright 2007 Andy Matuschak. All rights reserved. // #ifndef SUSTANDARDVERSIONCOMPARATOR_H #define SUSTANDARDVERSIONCOMPARATOR_H #if __has_feature(modules) @import Foundation; #else #import #endif #import "SUExport.h" #import "SUVersionComparisonProtocol.h" /*! Sparkle's default version comparator. This comparator is adapted from MacPAD, by Kevin Ballard. It's "dumb" in that it does essentially string comparison, in components split by character type. */ SU_EXPORT @interface SUStandardVersionComparator : NSObject /*! Returns a singleton instance of the comparator. */ + (SUStandardVersionComparator *)defaultComparator; /*! Compares version strings through textual analysis. See the implementation for more details. */ - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUStandardVersionComparator.m ================================================ // // SUStandardVersionComparator.m // Sparkle // // Created by Andy Matuschak on 12/21/07. // Copyright 2007 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUStandardVersionComparator.h" @implementation SUStandardVersionComparator + (SUStandardVersionComparator *)defaultComparator { static SUStandardVersionComparator *defaultComparator = nil; if (defaultComparator == nil) { defaultComparator = [[SUStandardVersionComparator alloc] init]; } return defaultComparator; } typedef NS_ENUM(NSInteger, SUCharacterType) { kNumberType, kStringType, kSeparatorType, }; - (SUCharacterType)typeOfCharacter:(NSString *)character { if ([character isEqualToString:@"."]) { return kSeparatorType; } else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[character characterAtIndex:0]]) { return kNumberType; } else if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[character characterAtIndex:0]]) { return kSeparatorType; } else if ([[NSCharacterSet punctuationCharacterSet] characterIsMember:[character characterAtIndex:0]]) { return kSeparatorType; } else { return kStringType; } } - (NSArray *)splitVersionString:(NSString *)version { NSString *character; NSMutableString *s; NSUInteger i, n; SUCharacterType oldType, newType; NSMutableArray *parts = [NSMutableArray array]; if ([version length] == 0) { // Nothing to do here return parts; } s = [[version substringToIndex:1] mutableCopy]; oldType = [self typeOfCharacter:s]; n = [version length] - 1; for (i = 1; i <= n; ++i) { character = [version substringWithRange:NSMakeRange(i, 1)]; newType = [self typeOfCharacter:character]; if (oldType != newType || oldType == kSeparatorType) { // We've reached a new segment NSString *aPart = [[NSString alloc] initWithString:s]; [parts addObject:aPart]; [s setString:character]; } else { // Add character to string and continue [s appendString:character]; } oldType = newType; } // Add the last part onto the array [parts addObject:[NSString stringWithString:s]]; return parts; } - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB { NSArray *partsA = [self splitVersionString:versionA]; NSArray *partsB = [self splitVersionString:versionB]; NSString *partA, *partB; NSUInteger i, n; long long valueA, valueB; SUCharacterType typeA, typeB; n = MIN([partsA count], [partsB count]); for (i = 0; i < n; ++i) { partA = [partsA objectAtIndex:i]; partB = [partsB objectAtIndex:i]; typeA = [self typeOfCharacter:partA]; typeB = [self typeOfCharacter:partB]; // Compare types if (typeA == typeB) { // Same type; we can compare if (typeA == kNumberType) { valueA = [partA longLongValue]; valueB = [partB longLongValue]; if (valueA > valueB) { return NSOrderedDescending; } else if (valueA < valueB) { return NSOrderedAscending; } } else if (typeA == kStringType) { NSComparisonResult result = [partA compare:partB]; if (result != NSOrderedSame) { return result; } } } else { // Not the same type? Now we have to do some validity checking if (typeA != kStringType && typeB == kStringType) { // typeA wins return NSOrderedDescending; } else if (typeA == kStringType && typeB != kStringType) { // typeB wins return NSOrderedAscending; } else { // One is a number and the other is a period. The period is invalid if (typeA == kNumberType) { return NSOrderedDescending; } else { return NSOrderedAscending; } } } } // The versions are equal up to the point where they both still have parts // Lets check to see if one is larger than the other if ([partsA count] != [partsB count]) { // Yep. Lets get the next part of the larger // n holds the index of the part we want. NSString *missingPart; SUCharacterType missingType; NSComparisonResult shorterResult, largerResult; if ([partsA count] > [partsB count]) { missingPart = [partsA objectAtIndex:n]; shorterResult = NSOrderedAscending; largerResult = NSOrderedDescending; } else { missingPart = [partsB objectAtIndex:n]; shorterResult = NSOrderedDescending; largerResult = NSOrderedAscending; } missingType = [self typeOfCharacter:missingPart]; // Check the type if (missingType == kStringType) { // It's a string. Shorter version wins return shorterResult; } else { // It's a number/period. Larger version wins return largerResult; } } // The 2 strings are identical return NSOrderedSame; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUStatus.xib ================================================ NSIsNil ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUStatusController.h ================================================ // // SUStatusController.h // Sparkle // // Created by Andy Matuschak on 3/14/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUSTATUSCONTROLLER_H #define SUSTATUSCONTROLLER_H #import "SUWindowController.h" @class SUHost; @interface SUStatusController : SUWindowController @property (weak) IBOutlet NSButton *actionButton; @property (weak) IBOutlet NSProgressIndicator *progressBar; @property (copy) NSString *statusText; @property double progressValue; @property (nonatomic) double maxProgressValue; @property (getter=isButtonEnabled) BOOL buttonEnabled; - (instancetype)initWithHost:(SUHost *)host; // Pass 0 for the max progress value to get an indeterminate progress bar. // Pass nil for the status text to not show it. - (void)beginActionWithTitle:(NSString *)title maxProgressValue:(double)maxProgressValue statusText:(NSString *)statusText; // If isDefault is YES, the button's key equivalent will be \r. - (void)setButtonTitle:(NSString *)buttonTitle target:(id)target action:(SEL)action isDefault:(BOOL)isDefault; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUStatusController.m ================================================ // // SUStatusController.m // Sparkle // // Created by Andy Matuschak on 3/14/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUStatusController.h" #import "SUHost.h" @interface SUStatusController () @property (copy) NSString *title, *buttonTitle; @property (strong) SUHost *host; @end @implementation SUStatusController @synthesize progressValue; @synthesize maxProgressValue; @synthesize statusText; @synthesize title; @synthesize buttonTitle; @synthesize host; @synthesize actionButton; @synthesize progressBar; - (instancetype)initWithHost:(SUHost *)aHost { self = [super initWithWindowNibName:@"SUStatus"]; if (self) { self.host = aHost; [self setShouldCascadeWindows:NO]; } return self; } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self.host bundlePath], [self.host installationPath]]; } - (void)windowDidLoad { if ([self.host isBackgroundApplication]) { [[self window] setLevel:NSFloatingWindowLevel]; } [[self window] center]; [[self window] setFrameAutosaveName:@"SUStatusFrame"]; [self.progressBar setUsesThreadedAnimation:YES]; } - (NSString *)windowTitle { return [NSString stringWithFormat:SULocalizedString(@"Updating %@", nil), [self.host name]]; } - (NSImage *)applicationIcon { return [self.host icon]; } - (void)beginActionWithTitle:(NSString *)aTitle maxProgressValue:(double)aMaxProgressValue statusText:(NSString *)aStatusText { self.title = aTitle; self.maxProgressValue = aMaxProgressValue; self.statusText = aStatusText; } - (void)setButtonTitle:(NSString *)aButtonTitle target:(id)target action:(SEL)action isDefault:(BOOL)isDefault { self.buttonTitle = aButtonTitle; [self window]; [self.actionButton sizeToFit]; // Except we're going to add 15 px for padding. [self.actionButton setFrameSize:NSMakeSize([self.actionButton frame].size.width + 15, [self.actionButton frame].size.height)]; // Now we have to move it over so that it's always 15px from the side of the window. [self.actionButton setFrameOrigin:NSMakePoint([[self window] frame].size.width - 15 - [self.actionButton frame].size.width, [self.actionButton frame].origin.y)]; // Redisplay superview to clean up artifacts [[self.actionButton superview] display]; [self.actionButton setTarget:target]; [self.actionButton setAction:action]; [self.actionButton setKeyEquivalent:isDefault ? @"\r" : @""]; // 06/05/2008 Alex: Avoid a crash when cancelling during the extraction [self setButtonEnabled:(target != nil)]; } - (BOOL)progressBarShouldAnimate { return YES; } - (void)setButtonEnabled:(BOOL)enabled { [self.actionButton setEnabled:enabled]; } - (BOOL)isButtonEnabled { return [self.actionButton isEnabled]; } - (void)setMaxProgressValue:(double)value { if (value < 0.0) value = 0.0; maxProgressValue = value; [self setProgressValue:0.0]; [self.progressBar setIndeterminate:(value == 0.0)]; [self.progressBar startAnimation:self]; [self.progressBar setUsesThreadedAnimation:YES]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUSystemProfiler.h ================================================ // // SUSystemProfiler.h // Sparkle // // Created by Andy Matuschak on 12/22/07. // Copyright 2007 Andy Matuschak. All rights reserved. // #ifndef SUSYSTEMPROFILER_H #define SUSYSTEMPROFILER_H #import @class SUHost; @interface SUSystemProfiler : NSObject + (SUSystemProfiler *)sharedSystemProfiler; - (NSMutableArray *)systemProfileArrayForHost:(SUHost *)host; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUSystemProfiler.m ================================================ // // SUSystemProfiler.m // Sparkle // // Created by Andy Matuschak on 12/22/07. // Copyright 2007 Andy Matuschak. All rights reserved. // Adapted from Sparkle+, by Tom Harrington. // #import #import "SUSystemProfiler.h" #import "SUHost.h" #import "SUOperatingSystem.h" #include static NSString *const SUSystemProfilerApplicationNameKey = @"appName"; static NSString *const SUSystemProfilerApplicationVersionKey = @"appVersion"; static NSString *const SUSystemProfilerCPU64bitKey = @"cpu64bit"; static NSString *const SUSystemProfilerCPUCountKey = @"ncpu"; static NSString *const SUSystemProfilerCPUFrequencyKey = @"cpuFreqMHz"; static NSString *const SUSystemProfilerCPUTypeKey = @"cputype"; static NSString *const SUSystemProfilerCPUSubtypeKey = @"cpusubtype"; static NSString *const SUSystemProfilerHardwareModelKey = @"model"; static NSString *const SUSystemProfilerMemoryKey = @"ramMB"; static NSString *const SUSystemProfilerOperatingSystemVersionKey = @"osVersion"; static NSString *const SUSystemProfilerPreferredLanguageKey = @"lang"; @implementation SUSystemProfiler + (SUSystemProfiler *)sharedSystemProfiler { static SUSystemProfiler *sharedSystemProfiler = nil; if (!sharedSystemProfiler) { sharedSystemProfiler = [[self alloc] init]; } return sharedSystemProfiler; } - (NSDictionary *)modelTranslationTable { // Use explicit class to use the correct bundle even when subclassed NSString *path = [[NSBundle bundleForClass:[SUSystemProfiler class]] pathForResource:@"SUModelTranslation" ofType:@"plist"]; return [[NSDictionary alloc] initWithContentsOfFile:path]; } - (NSMutableArray *)systemProfileArrayForHost:(SUHost *)host { NSDictionary *modelTranslation = [self modelTranslationTable]; // Gather profile information and append it to the URL. NSMutableArray *profileArray = [NSMutableArray array]; NSArray *profileDictKeys = @[@"key", @"displayKey", @"value", @"displayValue"]; int error = 0; int value = 0; size_t length = sizeof(value); // OS version NSString *currentSystemVersion = [SUOperatingSystem systemVersionString]; if (currentSystemVersion != nil) { [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerOperatingSystemVersionKey, @"OS Version", currentSystemVersion, currentSystemVersion] forKeys:profileDictKeys]]; } // CPU type (decoder info for values found here is in mach/machine.h) error = sysctlbyname("hw.cputype", &value, &length, NULL, 0); int cpuType = -1; if (error == 0) { cpuType = value; NSString *visibleCPUType; switch (value) { case CPU_TYPE_ARM: visibleCPUType = @"ARM"; break; case CPU_TYPE_X86: visibleCPUType = @"Intel"; break; case CPU_TYPE_POWERPC: visibleCPUType = @"PowerPC"; break; default: visibleCPUType = @"Unknown"; break; } [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerCPUTypeKey, @"CPU Type", @(value), visibleCPUType] forKeys:profileDictKeys]]; } error = sysctlbyname("hw.cpu64bit_capable", &value, &length, NULL, 0); if (error != 0) { error = sysctlbyname("hw.optional.x86_64", &value, &length, NULL, 0); //x86 specific } if (error != 0) { error = sysctlbyname("hw.optional.64bitops", &value, &length, NULL, 0); //PPC specific } BOOL is64bit = NO; if (error == 0) { is64bit = value == 1; [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerCPU64bitKey, @"CPU is 64-Bit?", @(is64bit), is64bit ? @"Yes" : @"No"] forKeys:profileDictKeys]]; } error = sysctlbyname("hw.cpusubtype", &value, &length, NULL, 0); if (error == 0) { NSString *visibleCPUSubType; if (cpuType == 7) { // Intel // TODO: other Intel processors, like Core i7, i5, i3, Xeon? visibleCPUSubType = is64bit ? @"Intel Core 2" : @"Intel Core"; // If anyone knows how to tell a Core Duo from a Core Solo, please email tph@atomicbird.com } else if (cpuType == 18) { // PowerPC switch (value) { case 9: visibleCPUSubType=@"G3"; break; case 10: case 11: visibleCPUSubType=@"G4"; break; case 100: visibleCPUSubType=@"G5"; break; default: visibleCPUSubType=@"Other"; break; } } else { visibleCPUSubType = @"Other"; } [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerCPUSubtypeKey, @"CPU Subtype", @(value), visibleCPUSubType] forKeys:profileDictKeys]]; } error = sysctlbyname("hw.model", NULL, &length, NULL, 0); if (error == 0) { char *cpuModel = (char *)malloc(sizeof(char) * length); if (cpuModel != NULL) { error = sysctlbyname("hw.model", cpuModel, &length, NULL, 0); if (error == 0) { NSString *rawModelName = @(cpuModel); NSString *visibleModelName = [modelTranslation objectForKey:rawModelName]; if (visibleModelName == nil) { visibleModelName = rawModelName; } [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerHardwareModelKey, @"Mac Model", rawModelName, visibleModelName] forKeys:profileDictKeys]]; } free(cpuModel); } } // Number of CPUs error = sysctlbyname("hw.ncpu", &value, &length, NULL, 0); if (error == 0) { [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerCPUCountKey, @"Number of CPUs", @(value), @(value)] forKeys:profileDictKeys]]; } // User preferred language NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; NSArray *languages = [defs objectForKey:@"AppleLanguages"]; if ([languages count] > 0) { [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerPreferredLanguageKey, @"Preferred Language", [languages objectAtIndex:0], [languages objectAtIndex:0]] forKeys:profileDictKeys]]; } // Application sending the request NSString *appName = [host name]; if (appName) { [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerApplicationNameKey, @"Application Name", appName, appName] forKeys:profileDictKeys]]; } NSString *appVersion = [host version]; if (appVersion) { [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerApplicationVersionKey, @"Application Version", appVersion, appVersion] forKeys:profileDictKeys]]; } // Number of displays? // CPU speed unsigned long hz; size_t hz_size = sizeof(unsigned long); if (sysctlbyname("hw.cpufrequency", &hz, &hz_size, NULL, 0) == 0) { unsigned long mhz = hz / 1000000; [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerCPUFrequencyKey, @"CPU Speed (MHz)", @(mhz), @(mhz / 1000.)] forKeys:profileDictKeys]]; } // amount of RAM unsigned long bytes; size_t bytes_size = sizeof(unsigned long); if (sysctlbyname("hw.memsize", &bytes, &bytes_size, NULL, 0) == 0) { double megabytes = bytes / (1024. * 1024.); [profileArray addObject:[NSDictionary dictionaryWithObjects:@[SUSystemProfilerMemoryKey, @"Memory (MB)", @(megabytes), @(megabytes)] forKeys:profileDictKeys]]; } return profileArray; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUIBasedUpdateDriver.h ================================================ // // SUUIBasedUpdateDriver.h // Sparkle // // Created by Andy Matuschak on 5/5/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUUIBASEDUPDATEDRIVER_H #define SUUIBASEDUPDATEDRIVER_H #import #import "SUBasicUpdateDriver.h" #import "SUUpdateAlert.h" @class SUStatusController; @interface SUUIBasedUpdateDriver : SUBasicUpdateDriver - (void)showAlert:(NSAlert *)alert; - (IBAction)cancelDownload:(id)sender; - (void)installAndRestart:(id)sender; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUIBasedUpdateDriver.m ================================================ // // SUUIBasedUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 5/5/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUUIBasedUpdateDriver.h" #import "SUUpdateAlert.h" #import "SUUpdater_Private.h" #import "SUHost.h" #import "SUOperatingSystem.h" #import "SUStatusController.h" #import "SUConstants.h" #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1080 @interface NSByteCountFormatter : NSFormatter { @private unsigned int _allowedUnits; char _countStyle; BOOL _allowsNonnumericFormatting, _includesUnit, _includesCount, _includesActualByteCount, _adaptive, _zeroPadsFractionDigits; int _formattingContext; int _reserved[5]; } + (NSString *)stringFromByteCount:(long long)byteCount countStyle:(NSByteCountFormatterCountStyle)countStyle; @end #endif @interface SUUIBasedUpdateDriver () @property (strong) SUStatusController *statusController; @property (strong) SUUpdateAlert *updateAlert; @end @implementation SUUIBasedUpdateDriver @synthesize statusController; @synthesize updateAlert; - (instancetype)initWithUpdater:(SUUpdater *)anUpdater { if ((self = [super initWithUpdater:anUpdater])) { self.automaticallyInstallUpdates = NO; } return self; } - (void)didFindValidUpdate { if ([[self.updater delegate] respondsToSelector:@selector(updater:didFindValidUpdate:)]) { [[self.updater delegate] updater:self.updater didFindValidUpdate:self.updateItem]; } if (self.automaticallyInstallUpdates) { [self updateAlertFinishedWithChoice:SUInstallUpdateChoice]; return; } self.updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUUpdateAlertChoice choice) { [self updateAlertFinishedWithChoice:choice]; }]; id versDisp = nil; if ([[self.updater delegate] respondsToSelector:@selector(versionDisplayerForUpdater:)]) { versDisp = [[self.updater delegate] versionDisplayerForUpdater:self.updater]; } [self.updateAlert setVersionDisplayer:versDisp]; // If the app is a menubar app or the like, we need to focus it first and alter the // update prompt to behave like a normal window. Otherwise if the window were hidden // there may be no way for the application to be activated to make it visible again. if ([self.host isBackgroundApplication]) { [[self.updateAlert window] setHidesOnDeactivate:NO]; [NSApp activateIgnoringOtherApps:YES]; } // Only show the update alert if the app is active; otherwise, we'll wait until it is. if ([NSApp isActive]) [[self.updateAlert window] makeKeyAndOrderFront:self]; else [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp]; } - (void)didNotFindUpdate { if ([[self.updater delegate] respondsToSelector:@selector(updaterDidNotFindUpdate:)]) [[self.updater delegate] updaterDidNotFindUpdate:self.updater]; [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdaterDidNotFindUpdateNotification object:self.updater]; if (!self.automaticallyInstallUpdates) { NSAlert *alert = [[NSAlert alloc] init]; alert.messageText = SULocalizedString(@"You're up-to-date!", "Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates."); alert.informativeText = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [self.host name], [self.host displayVersion]]; [alert addButtonWithTitle:SULocalizedString(@"OK", nil)]; [self showAlert:alert]; [self abortUpdate]; } } - (void)applicationDidBecomeActive:(NSNotification *)__unused aNotification { [[self.updateAlert window] makeKeyAndOrderFront:self]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:NSApp]; } - (void)updateAlertFinishedWithChoice:(SUUpdateAlertChoice)choice { self.updateAlert = nil; [self.host setObject:nil forUserDefaultsKey:SUSkippedVersionKey]; switch (choice) { case SUInstallUpdateChoice: self.statusController = [[SUStatusController alloc] initWithHost:self.host]; [self.statusController beginActionWithTitle:SULocalizedString(@"Downloading update...", @"Take care not to overflow the status window.") maxProgressValue:0.0 statusText:nil]; [self.statusController setButtonTitle:SULocalizedString(@"Cancel", nil) target:self action:@selector(cancelDownload:) isDefault:NO]; [self.statusController showWindow:self]; [self downloadUpdate]; break; case SUOpenInfoURLChoice: [[NSWorkspace sharedWorkspace] openURL:[self.updateItem infoURL]]; [self abortUpdate]; break; case SUSkipThisVersionChoice: [self.host setObject:[self.updateItem versionString] forUserDefaultsKey:SUSkippedVersionKey]; [self abortUpdate]; break; case SURemindMeLaterChoice: [self abortUpdate]; break; } } - (void)download:(NSURLDownload *)__unused download didReceiveResponse:(NSURLResponse *)response { [self.statusController setMaxProgressValue:[response expectedContentLength]]; } - (NSString *)localizedStringFromByteCount:(long long)value { if (![SUOperatingSystem isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 8, 0}]) { if (value < 1000) { return [NSString stringWithFormat:@"%.0lf %@", value / 1.0, SULocalizedString(@"B", @"the unit for bytes")]; } if (value < 1000 * 1000) { return [NSString stringWithFormat:@"%.0lf %@", value / 1000.0, SULocalizedString(@"KB", @"the unit for kilobytes")]; } if (value < 1000 * 1000 * 1000) { return [NSString stringWithFormat:@"%.1lf %@", value / 1000.0 / 1000.0, SULocalizedString(@"MB", @"the unit for megabytes")]; } return [NSString stringWithFormat:@"%.2lf %@", value / 1000.0 / 1000.0 / 1000.0, SULocalizedString(@"GB", @"the unit for gigabytes")]; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" return [NSByteCountFormatter stringFromByteCount:value countStyle:NSByteCountFormatterCountStyleFile]; #pragma clang diagnostic pop } - (void)download:(NSURLDownload *)__unused download didReceiveDataOfLength:(NSUInteger)length { [self.statusController setProgressValue:[self.statusController progressValue] + (double)length]; if ([self.statusController maxProgressValue] > 0.0) [self.statusController setStatusText:[NSString stringWithFormat:SULocalizedString(@"%@ of %@", nil), [self localizedStringFromByteCount:(long long)self.statusController.progressValue], [self localizedStringFromByteCount:(long long)self.statusController.maxProgressValue]]]; else [self.statusController setStatusText:[NSString stringWithFormat:SULocalizedString(@"%@ downloaded", nil), [self localizedStringFromByteCount:(long long)self.statusController.progressValue]]]; } - (IBAction)cancelDownload:(id)__unused sender { if (self.download) { [self.download cancel]; if ([[self.updater delegate] respondsToSelector:@selector(userDidCancelDownload:)]) { [[self.updater delegate] userDidCancelDownload:self.updater]; } } [self abortUpdate]; } - (void)extractUpdate { // Now we have to extract the downloaded archive. [self.statusController beginActionWithTitle:SULocalizedString(@"Extracting update...", @"Take care not to overflow the status window.") maxProgressValue:0.0 statusText:nil]; [self.statusController setButtonEnabled:NO]; [super extractUpdate]; } - (void)unarchiver:(SUUnarchiver *)__unused ua extractedProgress:(double)progress { // We do this here instead of in extractUpdate so that we only have a determinate progress bar for archives with progress. if ([self.statusController maxProgressValue] == 0.0) { [self.statusController setMaxProgressValue:1]; } [self.statusController setProgressValue:progress]; } - (void)unarchiverDidFinish:(SUUnarchiver *)__unused ua { if (self.automaticallyInstallUpdates) { [self installWithToolAndRelaunch:YES]; return; } [self.statusController beginActionWithTitle:SULocalizedString(@"Ready to Install", nil) maxProgressValue:1.0 statusText:nil]; [self.statusController setProgressValue:1.0]; // Fill the bar. [self.statusController setButtonEnabled:YES]; [self.statusController setButtonTitle:SULocalizedString(@"Install and Relaunch", nil) target:self action:@selector(installAndRestart:) isDefault:YES]; [[self.statusController window] makeKeyAndOrderFront:self]; [NSApp requestUserAttention:NSInformationalRequest]; } - (void)installAndRestart:(id)__unused sender { [self installWithToolAndRelaunch:YES]; } - (void)installWithToolAndRelaunch:(BOOL)relaunch { [self.statusController beginActionWithTitle:SULocalizedString(@"Installing update...", @"Take care not to overflow the status window.") maxProgressValue:0.0 statusText:nil]; [self.statusController setButtonEnabled:NO]; [super installWithToolAndRelaunch:relaunch]; } - (void)terminateApp { // if a user chooses to NOT relaunch the app (as is the case with WebKit // when it asks you if you are sure you want to close the app with multiple // tabs open), the status window still stays on the screen and obscures // other windows; with this fix, it doesn't if (self.statusController) { [self.statusController close]; self.statusController = nil; } [super terminateApp]; } - (void)abortUpdateWithError:(NSError *)error { NSAlert *alert = [[NSAlert alloc] init]; alert.messageText = SULocalizedString(@"Update Error!", nil); alert.informativeText = [NSString stringWithFormat:@"%@", [error localizedDescription]]; [alert addButtonWithTitle:SULocalizedString(@"Cancel Update", nil)]; [self showAlert:alert]; [super abortUpdateWithError:error]; } - (void)abortUpdate { if (self.statusController) { [self.statusController close]; self.statusController = nil; } [super abortUpdate]; } - (void)showAlert:(NSAlert *)alert { if ([[self.updater delegate] respondsToSelector:@selector(updaterWillShowModalAlert:)]) { [[self.updater delegate] updaterWillShowModalAlert:self.updater]; } // When showing a modal alert we need to ensure that background applications // are focused to inform the user since there is no dock icon to notify them. if ([self.host isBackgroundApplication]) { [NSApp activateIgnoringOtherApps:YES]; } [alert setIcon:[self.host icon]]; [alert runModal]; if ([[self.updater delegate] respondsToSelector:@selector(updaterDidShowModalAlert:)]) [[self.updater delegate] updaterDidShowModalAlert:self.updater]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUnarchiver.h ================================================ // // SUUnarchiver.h // Sparkle // // Created by Andy Matuschak on 3/16/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUUNARCHIVER_H #define SUUNARCHIVER_H #import @class SUHost; @protocol SUUnarchiverDelegate; @interface SUUnarchiver : NSObject @property (copy, readonly) NSString *archivePath; @property (copy, readonly) NSString *updateHostBundlePath; @property (copy, readonly) NSString *decryptionPassword; @property (weak) id delegate; + (SUUnarchiver *)unarchiverForPath:(NSString *)path updatingHostBundlePath:(NSString *)host withPassword:(NSString *)decryptionPassword; - (void)start; @end @protocol SUUnarchiverDelegate - (void)unarchiverDidFinish:(SUUnarchiver *)unarchiver; - (void)unarchiverDidFail:(SUUnarchiver *)unarchiver; @optional - (void)unarchiver:(SUUnarchiver *)unarchiver extractedProgress:(double)progress; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUnarchiver.m ================================================ // // SUUnarchiver.m // Sparkle // // Created by Andy Matuschak on 3/16/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUVersionComparisonProtocol.h" #import "SUUnarchiver.h" #import "SUUnarchiver_Private.h" @implementation SUUnarchiver @synthesize archivePath; @synthesize updateHostBundlePath; @synthesize delegate; @synthesize decryptionPassword; + (SUUnarchiver *)unarchiverForPath:(NSString *)path updatingHostBundlePath:(NSString *)hostPath withPassword:(NSString *)decryptionPassword { for (id current in [self unarchiverImplementations]) { if ([current canUnarchivePath:path]) { return [[current alloc] initWithPath:path hostBundlePath:hostPath password:decryptionPassword]; } } return nil; } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], self.archivePath]; } - (void)start { // No-op } - (instancetype)initWithPath:(NSString *)path hostBundlePath:(NSString *)hostPath password:(NSString *)password { if ((self = [super init])) { archivePath = [path copy]; updateHostBundlePath = hostPath; decryptionPassword = password; } return self; } + (BOOL)canUnarchivePath:(NSString *)__unused path { return NO; } - (void)notifyDelegateOfProgress:(double)progress { if ([self.delegate respondsToSelector:@selector(unarchiver:extractedProgress:)]) { [self.delegate unarchiver:self extractedProgress:progress]; } } - (void)notifyDelegateOfSuccess { if ([self.delegate respondsToSelector:@selector(unarchiverDidFinish:)]) { [self.delegate unarchiverDidFinish:self]; } } - (void)notifyDelegateOfFailure { if ([self.delegate respondsToSelector:@selector(unarchiverDidFail:)]) { [self.delegate unarchiverDidFail:self]; } } static NSMutableArray *gUnarchiverImplementations; + (void)registerImplementation:(Class)implementation { if (!gUnarchiverImplementations) { gUnarchiverImplementations = [[NSMutableArray alloc] init]; } [gUnarchiverImplementations addObject:implementation]; } + (NSArray *)unarchiverImplementations { return [NSArray arrayWithArray:gUnarchiverImplementations]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUnarchiver_Private.h ================================================ // // SUUnarchiver_Private.h // Sparkle // // Created by Andy Matuschak on 6/17/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUUNARCHIVER_PRIVATE_H #define SUUNARCHIVER_PRIVATE_H #import #import "SUUnarchiver.h" @interface SUUnarchiver (Private) + (void)registerImplementation:(Class)implementation; + (NSArray *)unarchiverImplementations; + (BOOL)canUnarchivePath:(NSString *)path; - (instancetype)initWithPath:(NSString *)archive hostBundlePath:(NSString *)host; - (void)notifyDelegateOfProgress:(double)progress; - (void)notifyDelegateOfSuccess; - (void)notifyDelegateOfFailure; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdateAlert.h ================================================ // // SUUpdateAlert.h // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUUPDATEALERT_H #define SUUPDATEALERT_H #import "SUWindowController.h" #import "SUVersionDisplayProtocol.h" @protocol SUUpdateAlertDelegate; typedef NS_ENUM(NSInteger, SUUpdateAlertChoice) { SUInstallUpdateChoice, SURemindMeLaterChoice, SUSkipThisVersionChoice, SUOpenInfoURLChoice }; @class SUAppcastItem, SUHost; @interface SUUpdateAlert : SUWindowController @property (weak) id versionDisplayer; - (instancetype)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)host completionBlock:(void(^)(SUUpdateAlertChoice))c; - (IBAction)installUpdate:sender; - (IBAction)skipThisVersion:sender; - (IBAction)remindMeLater:sender; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdateAlert.m ================================================ // // SUUpdateAlert.m // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright 2006 Andy Matuschak. All rights reserved. // // ----------------------------------------------------------------------------- // Headers: // ----------------------------------------------------------------------------- #import "SUUpdateAlert.h" #import "SUHost.h" #import #import "SUConstants.h" #import "SULog.h" // WebKit protocols are not explicitly declared until 10.11 SDK, so // declare dummy protocols to keep the build working on earlier SDKs. #if __MAC_OS_X_VERSION_MAX_ALLOWED < 101100 @protocol WebFrameLoadDelegate @end @protocol WebPolicyDelegate @end #endif @interface SUUpdateAlert () @property (strong) SUAppcastItem *updateItem; @property (strong) SUHost *host; @property (strong) void(^completionBlock)(SUUpdateAlertChoice); @property (strong) NSProgressIndicator *releaseNotesSpinner; @property (assign) BOOL webViewFinishedLoading; @property (weak) IBOutlet WebView *releaseNotesView; @property (weak) IBOutlet NSView *releaseNotesContainerView; @property (weak) IBOutlet NSTextField *descriptionField; @property (weak) IBOutlet NSButton *automaticallyInstallUpdatesButton; @property (weak) IBOutlet NSButton *installButton; @property (weak) IBOutlet NSButton *skipButton; @property (weak) IBOutlet NSButton *laterButton; @end @implementation SUUpdateAlert @synthesize completionBlock; @synthesize versionDisplayer; @synthesize updateItem; @synthesize host; @synthesize releaseNotesSpinner; @synthesize webViewFinishedLoading; @synthesize releaseNotesView; @synthesize releaseNotesContainerView; @synthesize descriptionField; @synthesize automaticallyInstallUpdatesButton; @synthesize installButton; @synthesize skipButton; @synthesize laterButton; - (instancetype)initWithAppcastItem:(SUAppcastItem *)item host:(SUHost *)aHost completionBlock:(void (^)(SUUpdateAlertChoice))block { self = [super initWithWindowNibName:@"SUUpdateAlert"]; if (self) { self.completionBlock = block; host = aHost; updateItem = item; [self setShouldCascadeWindows:NO]; // Alex: This dummy line makes sure that the binary is linked against WebKit. // The SUUpdateAlert.xib file contains a WebView and if we don't link against WebKit, // we will get a runtime crash when decoding the NIB. It is better to get a link error. [WebView MIMETypesShownAsHTML]; } return self; } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [self.host bundlePath]]; } - (void)endWithSelection:(SUUpdateAlertChoice)choice { [self.releaseNotesView stopLoading:self]; [self.releaseNotesView setFrameLoadDelegate:nil]; [self.releaseNotesView setPolicyDelegate:nil]; [self.releaseNotesView removeFromSuperview]; // Otherwise it gets sent Esc presses (why?!) and gets very confused. [self close]; self.completionBlock(choice); self.completionBlock = nil; } - (IBAction)installUpdate:(id)__unused sender { [self endWithSelection:SUInstallUpdateChoice]; } - (IBAction)openInfoURL:(id)__unused sender { [self endWithSelection:SUOpenInfoURLChoice]; } - (IBAction)skipThisVersion:(id)__unused sender { [self endWithSelection:SUSkipThisVersionChoice]; } - (IBAction)remindMeLater:(id)__unused sender { [self endWithSelection:SURemindMeLaterChoice]; } - (void)displayReleaseNotes { self.releaseNotesView.preferencesIdentifier = SUBundleIdentifier; WebPreferences *prefs = [self.releaseNotesView preferences]; prefs.plugInsEnabled = NO; prefs.javaEnabled = NO; prefs.javaScriptEnabled = [self.host boolForInfoDictionaryKey:SUEnableJavaScriptKey]; self.releaseNotesView.frameLoadDelegate = self; self.releaseNotesView.policyDelegate = self; // Set the default font // "-apple-system-font" is a reference to the system UI font. "-apple-system" is the new recommended token, but for backward compatibility we can't use it. prefs.standardFontFamily = @"-apple-system-font"; prefs.defaultFontSize = (int)[NSFont systemFontSize]; // Stick a nice big spinner in the middle of the web view until the page is loaded. NSRect frame = [[self.releaseNotesView superview] frame]; self.releaseNotesSpinner = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(NSMidX(frame) - 16, NSMidY(frame) - 16, 32, 32)]; [self.releaseNotesSpinner setStyle:NSProgressIndicatorSpinningStyle]; [self.releaseNotesSpinner startAnimation:self]; self.webViewFinishedLoading = NO; [[self.releaseNotesView superview] addSubview:self.releaseNotesSpinner]; // If there's a release notes URL, load it; otherwise, just stick the contents of the description into the web view. if ([self.updateItem releaseNotesURL]) { [[self.releaseNotesView mainFrame] loadRequest:[NSURLRequest requestWithURL:[self.updateItem releaseNotesURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30]]; } else { [[self.releaseNotesView mainFrame] loadHTMLString:[self.updateItem itemDescription] baseURL:nil]; } } - (BOOL)showsReleaseNotes { NSNumber *shouldShowReleaseNotes = [self.host objectForInfoDictionaryKey:SUShowReleaseNotesKey]; if (shouldShowReleaseNotes == nil) { // Don't show release notes if RSS item contains no description and no release notes URL: return (([self.updateItem itemDescription] != nil && [[[self.updateItem itemDescription] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] > 0) || [self.updateItem releaseNotesURL] != nil); } else return [shouldShowReleaseNotes boolValue]; } - (BOOL)allowsAutomaticUpdates { return self.host.allowsAutomaticUpdates; } - (void)windowDidLoad { BOOL showReleaseNotes = [self showsReleaseNotes]; [self.window setFrameAutosaveName: showReleaseNotes ? @"SUUpdateAlert" : @"SUUpdateAlertSmall" ]; if ([self.host isBackgroundApplication]) { [self.window setLevel:NSFloatingWindowLevel]; // This means the window will float over all other apps, if our app is switched out ?! } if (self.updateItem.isInformationOnlyUpdate) { [self.installButton setTitle:SULocalizedString(@"Learn More...", @"Alternate title for 'Install Update' button when there's no download in RSS feed.")]; [self.installButton setAction:@selector(openInfoURL:)]; } if (showReleaseNotes) { [self displayReleaseNotes]; } else { NSLayoutConstraint *automaticallyInstallUpdatesButtonToDescriptionFieldConstraint = [NSLayoutConstraint constraintWithItem:self.automaticallyInstallUpdatesButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.descriptionField attribute:NSLayoutAttributeBottom multiplier:1.0 constant:8.0]; [self.window.contentView addConstraint:automaticallyInstallUpdatesButtonToDescriptionFieldConstraint]; [self.releaseNotesContainerView removeFromSuperview]; } // When we show release notes, it looks ugly if the install buttons are not closer to the release notes view // However when we don't show release notes, it looks ugly if the install buttons are too close to the description field. Shrugs. if (showReleaseNotes && ![self allowsAutomaticUpdates]) { NSLayoutConstraint *skipButtonToReleaseNotesContainerConstraint = [NSLayoutConstraint constraintWithItem:self.skipButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.releaseNotesContainerView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:12.0]; [self.window.contentView addConstraint:skipButtonToReleaseNotesContainerConstraint]; [self.automaticallyInstallUpdatesButton removeFromSuperview]; } [self.window center]; } - (BOOL)windowShouldClose:(NSNotification *) __unused note { [self endWithSelection:SURemindMeLaterChoice]; return YES; } - (NSImage *)applicationIcon { return [self.host icon]; } - (NSString *)titleText { return [NSString stringWithFormat:SULocalizedString(@"A new version of %@ is available!", nil), [self.host name]]; } - (NSString *)descriptionText { NSString *updateItemVersion = [self.updateItem displayVersionString]; NSString *hostVersion = [self.host displayVersion]; // Display more info if the version strings are the same; useful for betas. if (!self.versionDisplayer && [updateItemVersion isEqualToString:hostVersion] ) { updateItemVersion = [updateItemVersion stringByAppendingFormat:@" (%@)", [self.updateItem versionString]]; hostVersion = [hostVersion stringByAppendingFormat:@" (%@)", self.host.version]; } else { [self.versionDisplayer formatVersion:&updateItemVersion andVersion:&hostVersion]; } // We display a slightly different summary depending on if it's an "info-only" item or not NSString *finalString = nil; if (self.updateItem.isInformationOnlyUpdate) { finalString = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?", @"Description text for SUUpdateAlert when the update informational with no download."), self.host.name, updateItemVersion, hostVersion]; } else { finalString = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is now available--you have %@. Would you like to download it now?", @"Description text for SUUpdateAlert when the update is downloadable."), self.host.name, updateItemVersion, hostVersion]; } return finalString; } - (void)webView:(WebView *)sender didFinishLoadForFrame:frame { if ([frame parentFrame] == nil) { self.webViewFinishedLoading = YES; [self.releaseNotesSpinner setHidden:YES]; [sender display]; // necessary to prevent weird scroll bar artifacting } } - (void)webView:(WebView *)__unused sender decidePolicyForNavigationAction:(NSDictionary *)__unused actionInformation request:(NSURLRequest *)request frame:(WebFrame *)__unused frame decisionListener:(id)listener { NSURL *requestURL = request.URL; NSString *scheme = requestURL.scheme; BOOL whitelistedSafe = [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [requestURL.absoluteString isEqualToString:@"about:blank"]; // Do not allow redirects to dangerous protocols such as file:// if (!whitelistedSafe) { SULog(@"Blocked display of %@ URL which may be dangerous", scheme); [listener ignore]; return; } if (self.webViewFinishedLoading) { if (requestURL) { [[NSWorkspace sharedWorkspace] openURL:requestURL]; } [listener ignore]; } else { [listener use]; } } // Clean up the contextual menu. - (NSArray *)webView:(WebView *)__unused sender contextMenuItemsForElement:(NSDictionary *)__unused element defaultMenuItems:(NSArray *)defaultMenuItems { NSMutableArray *webViewMenuItems = [defaultMenuItems mutableCopy]; if (webViewMenuItems) { for (NSMenuItem *menuItem in defaultMenuItems) { NSInteger tag = [menuItem tag]; switch (tag) { case WebMenuItemTagOpenLinkInNewWindow: case WebMenuItemTagDownloadLinkToDisk: case WebMenuItemTagOpenImageInNewWindow: case WebMenuItemTagDownloadImageToDisk: case WebMenuItemTagOpenFrameInNewWindow: case WebMenuItemTagGoBack: case WebMenuItemTagGoForward: case WebMenuItemTagStop: case WebMenuItemTagReload: [webViewMenuItems removeObjectIdenticalTo:menuItem]; } } } return webViewMenuItems; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdateDriver.h ================================================ // // SUUpdateDriver.h // Sparkle // // Created by Andy Matuschak on 5/7/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUUPDATEDRIVER_H #define SUUPDATEDRIVER_H #import extern NSString *const SUUpdateDriverFinishedNotification; @class SUHost, SUUpdater; @interface SUUpdateDriver : NSObject @property (readonly, weak) SUUpdater *updater; @property (strong) SUHost *host; - (instancetype)initWithUpdater:(SUUpdater *)updater; - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)host; - (void)abortUpdate; - (void)showAlert:(NSAlert *)alert; @property (getter=isInterruptible, readonly) BOOL interruptible; @property (readonly) BOOL finished; @property BOOL automaticallyInstallUpdates; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdateDriver.m ================================================ // // SUUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 5/7/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUUpdateDriver.h" #import "SUHost.h" #import "SULog.h" NSString *const SUUpdateDriverFinishedNotification = @"SUUpdateDriverFinished"; @interface SUUpdateDriver () @property (weak) SUUpdater *updater; @property (copy) NSURL *appcastURL; @property (getter=isInterruptible) BOOL interruptible; @end @implementation SUUpdateDriver @synthesize updater; @synthesize host; @synthesize interruptible; @synthesize finished; @synthesize appcastURL; @synthesize automaticallyInstallUpdates; - (instancetype)initWithUpdater:(SUUpdater *)anUpdater { if ((self = [super init])) { self.updater = anUpdater; } return self; } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self.host bundlePath], [self.host installationPath]]; } - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)h { self.appcastURL = URL; self.host = h; } - (void)abortUpdate { [self setValue:@YES forKey:@"finished"]; [[NSNotificationCenter defaultCenter] postNotificationName:SUUpdateDriverFinishedNotification object:self]; } - (void)showAlert:(NSAlert *)alert { // Only UI-based subclass shows the actual alert SULog(@"ALERT: %@\n%@", alert.messageText, alert.informativeText); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdatePermissionPrompt.h ================================================ // // SUUpdatePermissionPrompt.h // Sparkle // // Created by Andy Matuschak on 1/24/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUUPDATEPERMISSIONPROMPT_H #define SUUPDATEPERMISSIONPROMPT_H #import "SUWindowController.h" typedef NS_ENUM(NSInteger, SUPermissionPromptResult) { SUAutomaticallyCheck, SUDoNotAutomaticallyCheck }; @protocol SUUpdatePermissionPromptDelegate; @class SUHost; @interface SUUpdatePermissionPrompt : SUWindowController + (void)promptWithHost:(SUHost *)aHost systemProfile:(NSArray *)profile delegate:(id)d; - (IBAction)toggleMoreInfo:(id)sender; - (IBAction)finishPrompt:(id)sender; @end @protocol SUUpdatePermissionPromptDelegate - (void)updatePermissionPromptFinishedWithResult:(SUPermissionPromptResult)result; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdatePermissionPrompt.m ================================================ // // SUUpdatePermissionPrompt.m // Sparkle // // Created by Andy Matuschak on 1/24/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUUpdatePermissionPrompt.h" #import "SUHost.h" #import "SUConstants.h" @interface SUUpdatePermissionPrompt () @property (assign) BOOL isShowingMoreInfo; @property (assign) BOOL shouldSendProfile; @property (strong) SUHost *host; @property (strong) NSArray *systemProfileInformationArray; @property (weak) id delegate; @property (weak) IBOutlet NSTextField *descriptionTextField; @property (weak) IBOutlet NSView *moreInfoView; @property (weak) IBOutlet NSButton *moreInfoButton; @property (weak) IBOutlet NSTableView *profileTableView; @end @implementation SUUpdatePermissionPrompt @synthesize isShowingMoreInfo = _isShowingMoreInfo; @synthesize shouldSendProfile = _shouldSendProfile; @synthesize host; @synthesize systemProfileInformationArray; @synthesize delegate; @synthesize descriptionTextField; @synthesize moreInfoView; @synthesize moreInfoButton; @synthesize profileTableView; - (BOOL)shouldAskAboutProfile { return [[self.host objectForInfoDictionaryKey:SUEnableSystemProfilingKey] boolValue]; } - (instancetype)initWithHost:(SUHost *)aHost systemProfile:(NSArray *)profile delegate:(id)d { self = [super initWithWindowNibName:@"SUUpdatePermissionPrompt"]; if (self) { host = aHost; delegate = d; self.isShowingMoreInfo = NO; self.shouldSendProfile = [self shouldAskAboutProfile]; systemProfileInformationArray = profile; [self setShouldCascadeWindows:NO]; } return self; } + (void)promptWithHost:(SUHost *)aHost systemProfile:(NSArray *)profile delegate:(id)d { // If this is a background application we need to focus it in order to bring the prompt // to the user's attention. Otherwise the prompt would be hidden behind other applications and // the user would not know why the application was paused. if ([aHost isBackgroundApplication]) { [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; } if (![NSApp modalWindow]) { // do not prompt if there is is another modal window on screen SUUpdatePermissionPrompt *prompt = [[[self class] alloc] initWithHost:aHost systemProfile:profile delegate:d]; NSWindow *window = [prompt window]; if (window) { [NSApp runModalForWindow:window]; } } } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [self.host bundlePath]]; } - (void)windowDidLoad { if (![self shouldAskAboutProfile]) { NSRect frame = [[self window] frame]; frame.size.height -= [self.moreInfoButton frame].size.height; [[self window] setFrame:frame display:YES]; } else { // Set the table view's delegate so we can disable row selection. [self.profileTableView setDelegate:(id)self]; } } - (BOOL)tableView:(NSTableView *) __unused tableView shouldSelectRow:(NSInteger) __unused row { return NO; } - (NSImage *)icon { return [self.host icon]; } - (NSString *)promptDescription { return [NSString stringWithFormat:SULocalizedString(@"Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu.", nil), [self.host name]]; } - (IBAction)toggleMoreInfo:(id)__unused sender { self.isShowingMoreInfo = !self.isShowingMoreInfo; NSView *contentView = [[self window] contentView]; NSRect contentViewFrame = [contentView frame]; NSRect windowFrame = [[self window] frame]; NSRect profileMoreInfoViewFrame = [self.moreInfoView frame]; NSRect profileMoreInfoButtonFrame = [self.moreInfoButton frame]; NSRect descriptionFrame = [self.descriptionTextField frame]; if (self.isShowingMoreInfo) { // Add the subview contentViewFrame.size.height += profileMoreInfoViewFrame.size.height; profileMoreInfoViewFrame.origin.y = profileMoreInfoButtonFrame.origin.y - profileMoreInfoViewFrame.size.height; profileMoreInfoViewFrame.origin.x = descriptionFrame.origin.x; profileMoreInfoViewFrame.size.width = descriptionFrame.size.width; windowFrame.size.height += profileMoreInfoViewFrame.size.height; windowFrame.origin.y -= profileMoreInfoViewFrame.size.height; [self.moreInfoView setFrame:profileMoreInfoViewFrame]; [self.moreInfoView setHidden:YES]; [contentView addSubview:self.moreInfoView positioned:NSWindowBelow relativeTo:self.moreInfoButton]; } else { // Remove the subview [self.moreInfoView setHidden:NO]; [self.moreInfoView removeFromSuperview]; contentViewFrame.size.height -= profileMoreInfoViewFrame.size.height; windowFrame.size.height -= profileMoreInfoViewFrame.size.height; windowFrame.origin.y += profileMoreInfoViewFrame.size.height; } [[self window] setFrame:windowFrame display:YES animate:YES]; [contentView setFrame:contentViewFrame]; [contentView setNeedsDisplay:YES]; [self.moreInfoView setHidden:!self.isShowingMoreInfo]; } - (IBAction)finishPrompt:(id)sender { if (![self.delegate respondsToSelector:@selector(updatePermissionPromptFinishedWithResult:)]) { [NSException raise:@"SUInvalidDelegate" format:@"SUUpdatePermissionPrompt's delegate (%@) doesn't respond to updatePermissionPromptFinishedWithResult:!", self.delegate]; } [self.host setBool:self.shouldSendProfile forUserDefaultsKey:SUSendProfileInfoKey]; [self.delegate updatePermissionPromptFinishedWithResult:([sender tag] == 1 ? SUAutomaticallyCheck : SUDoNotAutomaticallyCheck)]; [[self window] close]; [NSApp stopModal]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdater.h ================================================ // // SUUpdater.h // Sparkle // // Created by Andy Matuschak on 1/4/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SUUPDATER_H #define SUUPDATER_H #if __has_feature(modules) @import Foundation; #else #import #endif #import "SUExport.h" #import "SUVersionComparisonProtocol.h" #import "SUVersionDisplayProtocol.h" @class SUAppcastItem, SUAppcast; @protocol SUUpdaterDelegate; /*! The main API in Sparkle for controlling the update mechanism. This class is used to configure the update paramters as well as manually and automatically schedule and control checks for updates. */ SU_EXPORT @interface SUUpdater : NSObject @property (unsafe_unretained) IBOutlet id delegate; + (SUUpdater *)sharedUpdater; + (SUUpdater *)updaterForBundle:(NSBundle *)bundle; - (instancetype)initForBundle:(NSBundle *)bundle; @property (readonly, strong) NSBundle *hostBundle; @property (strong, readonly) NSBundle *sparkleBundle; @property BOOL automaticallyChecksForUpdates; @property NSTimeInterval updateCheckInterval; /*! * The URL of the appcast used to download update information. * * This property must be called on the main thread. */ @property (copy) NSURL *feedURL; @property (nonatomic, copy) NSString *userAgentString; @property (copy) NSDictionary *httpHeaders; @property BOOL sendsSystemProfile; @property BOOL automaticallyDownloadsUpdates; @property (nonatomic, copy) NSString *decryptionPassword; /*! Explicitly checks for updates and displays a progress dialog while doing so. This method is meant for a main menu item. Connect any menu item to this action in Interface Builder, and Sparkle will check for updates and report back its findings verbosely when it is invoked. */ - (IBAction)checkForUpdates:(id)sender; /*! Checks for updates, but does not display any UI unless an update is found. This is meant for programmatically initating a check for updates. That is, it will display no UI unless it actually finds an update, in which case it proceeds as usual. If the fully automated updating is turned on, however, this will invoke that behavior, and if an update is found, it will be downloaded and prepped for installation. */ - (void)checkForUpdatesInBackground; /*! Checks for updates and, if available, immediately downloads and installs them. A progress dialog is shown but the user will never be prompted to read the release notes. You may want to respond to the userDidCancelDownload delegate method in case the user clicks the "Cancel" button while the update is downloading. */ - (void)installUpdatesIfAvailable; /*! Returns the date of last update check. \returns \c nil if no check has been performed. */ @property (readonly, copy) NSDate *lastUpdateCheckDate; /*! Begins a "probing" check for updates which will not actually offer to update to that version. However, the delegate methods SUUpdaterDelegate::updater:didFindValidUpdate: and SUUpdaterDelegate::updaterDidNotFindUpdate: will be called, so you can use that information in your UI. */ - (void)checkForUpdateInformation; /*! Appropriately schedules or cancels the update checking timer according to the preferences for time interval and automatic checks. This call does not change the date of the next check, but only the internal NSTimer. */ - (void)resetUpdateCycle; @property (readonly) BOOL updateInProgress; @end // ----------------------------------------------------------------------------- // SUUpdater Notifications for events that might be interesting to more than just the delegate // The updater will be the notification object // ----------------------------------------------------------------------------- SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; #define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; #define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; // Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; // Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; // ----------------------------------------------------------------------------- // SUUpdater Delegate: // ----------------------------------------------------------------------------- /*! Provides methods to control the behavior of an SUUpdater object. */ @protocol SUUpdaterDelegate @optional /*! Returns whether to allow Sparkle to pop up. For example, this may be used to prevent Sparkle from interrupting a setup assistant. \param updater The SUUpdater instance. */ - (BOOL)updaterMayCheckForUpdates:(SUUpdater *)updater; /*! Returns additional parameters to append to the appcast URL's query string. This is potentially based on whether or not Sparkle will also be sending along the system profile. \param updater The SUUpdater instance. \param sendingProfile Whether the system profile will also be sent. \return An array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. */ - (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; /*! Returns a custom appcast URL. Override this to dynamically specify the entire URL. \param updater The SUUpdater instance. */ - (NSString *)feedURLStringForUpdater:(SUUpdater *)updater; /*! Returns whether Sparkle should prompt the user about automatic update checks. Use this to override the default behavior. \param updater The SUUpdater instance. */ - (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)updater; /*! Called after Sparkle has downloaded the appcast from the remote server. Implement this if you want to do some special handling with the appcast once it finishes loading. \param updater The SUUpdater instance. \param appcast The appcast that was downloaded from the remote server. */ - (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; /*! Returns the item in the appcast corresponding to the update that should be installed. If you're using special logic or extensions in your appcast, implement this to use your own logic for finding a valid update, if any, in the given appcast. \param appcast The appcast that was downloaded from the remote server. \param updater The SUUpdater instance. */ - (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater; /*! Called when a valid update is found by the update driver. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that is proposed to be installed. */ - (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; /*! Called when a valid update is not found. \param updater The SUUpdater instance. */ - (void)updaterDidNotFindUpdate:(SUUpdater *)updater; /*! Called immediately before downloading the specified update. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that is proposed to be downloaded. \param request The mutable URL request that will be used to download the update. */ - (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; /*! Called after the specified update failed to download. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that failed to download. \param error The error generated by the failed download. */ - (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; /*! Called when the user clicks the cancel button while and update is being downloaded. \param updater The SUUpdater instance. */ - (void)userDidCancelDownload:(SUUpdater *)updater; /*! Called immediately before installing the specified update. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that is proposed to be installed. */ - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; /*! Returns whether the relaunch should be delayed in order to perform other tasks. This is not called if the user didn't relaunch on the previous update, in that case it will immediately restart. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that is proposed to be installed. \param invocation The invocation that must be completed before continuing with the relaunch. \return \c YES to delay the relaunch until \p invocation is invoked. */ - (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation; /*! Returns whether the application should be relaunched at all. Some apps \b cannot be relaunched under certain circumstances. This method can be used to explicitly prevent a relaunch. \param updater The SUUpdater instance. */ - (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; /*! Called immediately before relaunching. \param updater The SUUpdater instance. */ - (void)updaterWillRelaunchApplication:(SUUpdater *)updater; /*! Returns an object that compares version numbers to determine their arithmetic relation to each other. This method allows you to provide a custom version comparator. If you don't implement this method or return \c nil, the standard version comparator will be used. \sa SUStandardVersionComparator \param updater The SUUpdater instance. */ - (id)versionComparatorForUpdater:(SUUpdater *)updater; /*! Returns an object that formats version numbers for display to the user. If you don't implement this method or return \c nil, the standard version formatter will be used. \sa SUUpdateAlert \param updater The SUUpdater instance. */ - (id)versionDisplayerForUpdater:(SUUpdater *)updater; /*! Returns the path which is used to relaunch the client after the update is installed. The default is the path of the host bundle. \param updater The SUUpdater instance. */ - (NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; /*! Called before an updater shows a modal alert window, to give the host the opportunity to hide attached windows that may get in the way. \param updater The SUUpdater instance. */ - (void)updaterWillShowModalAlert:(SUUpdater *)updater; /*! Called after an updater shows a modal alert window, to give the host the opportunity to hide attached windows that may get in the way. \param updater The SUUpdater instance. */ - (void)updaterDidShowModalAlert:(SUUpdater *)updater; /*! Called when an update is scheduled to be silently installed on quit. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that is proposed to be installed. \param invocation Can be used to trigger an immediate silent install and relaunch. */ - (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; /*! Calls after an update that was scheduled to be silently installed on quit has been canceled. \param updater The SUUpdater instance. \param item The appcast item corresponding to the update that was proposed to be installed. */ - (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item; /*! Called after an update is aborted due to an error. \param updater The SUUpdater instance. \param error The error that caused the abort */ - (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdater.m ================================================ // // SUUpdater.m // Sparkle // // Created by Andy Matuschak on 1/4/06. // Copyright 2006 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" #import "SUHost.h" #import "SUUpdatePermissionPrompt.h" #import "SUAutomaticUpdateDriver.h" #import "SUProbingUpdateDriver.h" #import "SUUserInitiatedUpdateDriver.h" #import "SUScheduledUpdateDriver.h" #import "SUConstants.h" #import "SULog.h" #import "SUCodeSigningVerifier.h" #include NSString *const SUUpdaterDidFinishLoadingAppCastNotification = @"SUUpdaterDidFinishLoadingAppCastNotification"; NSString *const SUUpdaterDidFindValidUpdateNotification = @"SUUpdaterDidFindValidUpdateNotification"; NSString *const SUUpdaterDidNotFindUpdateNotification = @"SUUpdaterDidNotFindUpdateNotification"; NSString *const SUUpdaterWillRestartNotification = @"SUUpdaterWillRestartNotificationName"; NSString *const SUUpdaterAppcastItemNotificationKey = @"SUUpdaterAppcastItemNotificationKey"; NSString *const SUUpdaterAppcastNotificationKey = @"SUUpdaterAppCastNotificationKey"; @interface SUUpdater () @property (strong) NSTimer *checkTimer; @property (strong) NSBundle *sparkleBundle; - (instancetype)initForBundle:(NSBundle *)bundle; - (void)startUpdateCycle; - (void)checkForUpdatesWithDriver:(SUUpdateDriver *)updateDriver; - (void)scheduleNextUpdateCheck; - (void)registerAsObserver; - (void)unregisterAsObserver; - (void)updateDriverDidFinish:(NSNotification *)note; @property (readonly, copy) NSURL *parameterizedFeedURL; @property (strong) SUUpdateDriver *driver; @property (strong) SUHost *host; @end @implementation SUUpdater @synthesize delegate; @synthesize checkTimer; @synthesize userAgentString = customUserAgentString; @synthesize httpHeaders; @synthesize driver; @synthesize host; @synthesize sparkleBundle; @synthesize decryptionPassword; static NSMutableDictionary *sharedUpdaters = nil; static NSString *const SUUpdaterDefaultsObservationContext = @"SUUpdaterDefaultsObservationContext"; + (SUUpdater *)sharedUpdater { return [self updaterForBundle:[NSBundle mainBundle]]; } // SUUpdater has a singleton for each bundle. We use the fact that NSBundle instances are also singletons, so we can use them as keys. If you don't trust that you can also use the identifier as key + (SUUpdater *)updaterForBundle:(NSBundle *)bundle { if (bundle == nil) bundle = [NSBundle mainBundle]; id updater = [sharedUpdaters objectForKey:[NSValue valueWithNonretainedObject:bundle]]; if (updater == nil) { updater = [[[self class] alloc] initForBundle:bundle]; } return updater; } // This is the designated initializer for SUUpdater, important for subclasses - (instancetype)initForBundle:(NSBundle *)bundle { self = [super init]; if (bundle == nil) bundle = [NSBundle mainBundle]; // Use explicit class to use the correct bundle even when subclassed self.sparkleBundle = [NSBundle bundleForClass:[SUUpdater class]]; if (!self.sparkleBundle) { SULog(@"Error: SUUpdater can't find Sparkle.framework it belongs to"); return nil; } // Register as observer straight away to avoid exceptions on -dealloc when -unregisterAsObserver is called: if (self) { [self registerAsObserver]; } id updater = [sharedUpdaters objectForKey:[NSValue valueWithNonretainedObject:bundle]]; if (updater) { self = updater; } else if (self) { if (sharedUpdaters == nil) { sharedUpdaters = [[NSMutableDictionary alloc] init]; } [sharedUpdaters setObject:self forKey:[NSValue valueWithNonretainedObject:bundle]]; host = [[SUHost alloc] initWithBundle:bundle]; // This runs the permission prompt if needed, but never before the app has finished launching because the runloop won't run before that [self performSelector:@selector(startUpdateCycle) withObject:nil afterDelay:0]; } return self; } -(void)showAlertText:(NSString *)text informativeText:(NSString *)informativeText { NSAlert *alert = [[NSAlert alloc] init]; alert.messageText = text; alert.informativeText = informativeText; [self.driver showAlert:alert]; } -(void)checkIfConfiguredProperly { BOOL hasPublicDSAKey = [self.host publicDSAKey] != nil; BOOL isMainBundle = [self.host.bundle isEqualTo:[NSBundle mainBundle]]; BOOL hostIsCodeSigned = [SUCodeSigningVerifier hostApplicationIsCodeSigned]; NSURL *feedURL = [self feedURL]; BOOL servingOverHttps = [[[feedURL scheme] lowercaseString] isEqualToString:@"https"]; if (!hasPublicDSAKey) { if (!isMainBundle) { [self showAlertText:SULocalizedString(@"Insecure update error!", nil) informativeText:SULocalizedString(@"For security reasons, you need to sign your updates with a DSA key. See Sparkle's documentation for more information.", nil)]; } else { if (!hostIsCodeSigned) { [self showAlertText:SULocalizedString(@"Insecure update error!", nil) informativeText:SULocalizedString(@"For security reasons, you need to code sign your application or sign your updates with a DSA key. See https://sparkle-project.org/documentation/ for more information.", nil)]; } else if (!servingOverHttps) { [self showAlertText:SULocalizedString(@"Insecure update error!", nil) informativeText:SULocalizedString(@"For security reasons, you need to serve your updates over HTTPS and/or sign your updates with a DSA key. See https://sparkle-project.org/documentation/ for more information.", nil)]; } } } #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 if (!servingOverHttps) { BOOL atsExceptionsExist = nil != [self.host objectForInfoDictionaryKey:@"NSAppTransportSecurity"]; if (isMainBundle && !atsExceptionsExist) { [self showAlertText:SULocalizedString(@"Insecure feed URL is blocked in macOS 10.11", nil) informativeText:[NSString stringWithFormat:SULocalizedString(@"You must change the feed URL (%@) to use HTTPS or disable App Transport Security.\n\nFor more information:\nhttps://sparkle-project.org/documentation/app-transport-security/", nil), [feedURL absoluteString]]]; } else if (!isMainBundle) { SULog(@"WARNING: Serving updates over HTTP may be blocked in macOS 10.11. Please change the feed URL (%@) to use HTTPS. For more information:\nhttps://sparkle-project.org/documentation/app-transport-security/", feedURL); } } #endif } // This will be used when the updater is instantiated in a nib such as MainMenu - (instancetype)init { return [self initForBundle:[NSBundle mainBundle]]; } - (NSString *)description { return [NSString stringWithFormat:@"%@ <%@, %@>", [self class], [self.host bundlePath], [self.host installationPath]]; } - (void)startUpdateCycle { BOOL shouldPrompt = NO; BOOL hasLaunchedBefore = [self.host boolForUserDefaultsKey:SUHasLaunchedBeforeKey]; // If the user has been asked about automatic checks, don't bother prompting if ([self.host objectForUserDefaultsKey:SUEnableAutomaticChecksKey]) { shouldPrompt = NO; } // Does the delegate want to take care of the logic for when we should ask permission to update? else if ([self.delegate respondsToSelector:@selector(updaterShouldPromptForPermissionToCheckForUpdates:)]) { shouldPrompt = [self.delegate updaterShouldPromptForPermissionToCheckForUpdates:self]; } // Has he been asked already? And don't ask if the host has a default value set in its Info.plist. else if ([self.host objectForKey:SUEnableAutomaticChecksKey] == nil) { // Now, we don't want to ask the user for permission to do a weird thing on the first launch. // We wait until the second launch, unless explicitly overridden via SUPromptUserOnFirstLaunchKey. shouldPrompt = [self.host objectForKey:SUPromptUserOnFirstLaunchKey] || hasLaunchedBefore; } if (!hasLaunchedBefore) { [self.host setBool:YES forUserDefaultsKey:SUHasLaunchedBeforeKey]; } if (shouldPrompt) { NSArray *profileInfo = [self.host systemProfile]; // Always say we're sending the system profile here so that the delegate displays the parameters it would send. if ([self.delegate respondsToSelector:@selector(feedParametersForUpdater:sendingSystemProfile:)]) { profileInfo = [profileInfo arrayByAddingObjectsFromArray:[self.delegate feedParametersForUpdater:self sendingSystemProfile:YES]]; } [SUUpdatePermissionPrompt promptWithHost:self.host systemProfile:profileInfo delegate:self]; // We start the update checks and register as observer for changes after the prompt finishes } else { // We check if the user's said they want updates, or they haven't said anything, and the default is set to checking. [self scheduleNextUpdateCheck]; } } - (void)updatePermissionPromptFinishedWithResult:(SUPermissionPromptResult)result { [self setAutomaticallyChecksForUpdates:(result == SUAutomaticallyCheck)]; // Schedule checks, but make sure we ignore the delayed call from KVO [self resetUpdateCycle]; } - (void)updateDriverDidFinish:(NSNotification *)note { if ([note object] == self.driver && [self.driver finished]) { self.driver = nil; [self updateLastUpdateCheckDate]; [self scheduleNextUpdateCheck]; } } - (NSDate *)lastUpdateCheckDate { return [self.host objectForUserDefaultsKey:SULastCheckTimeKey]; } - (void)updateLastUpdateCheckDate { [self willChangeValueForKey:@"lastUpdateCheckDate"]; [self.host setObject:[NSDate date] forUserDefaultsKey:SULastCheckTimeKey]; [self didChangeValueForKey:@"lastUpdateCheckDate"]; } - (void)scheduleNextUpdateCheck { if (self.checkTimer) { [self.checkTimer invalidate]; self.checkTimer = nil; // Timer is non-repeating, may have invalidated itself, so we had to retain it. } if (![self automaticallyChecksForUpdates]) return; // How long has it been since last we checked for an update? NSDate *lastCheckDate = [self lastUpdateCheckDate]; if (!lastCheckDate) { lastCheckDate = [NSDate distantPast]; } NSTimeInterval intervalSinceCheck = [[NSDate date] timeIntervalSinceDate:lastCheckDate]; // Now we want to figure out how long until we check again. NSTimeInterval delayUntilCheck, updateCheckInterval = [self updateCheckInterval]; if (updateCheckInterval < SUMinimumUpdateCheckInterval) updateCheckInterval = SUMinimumUpdateCheckInterval; if (intervalSinceCheck < updateCheckInterval) delayUntilCheck = (updateCheckInterval - intervalSinceCheck); // It hasn't been long enough. else delayUntilCheck = 0; // We're overdue! Run one now. self.checkTimer = [NSTimer scheduledTimerWithTimeInterval:delayUntilCheck target:self selector:@selector(checkForUpdatesInBackground) userInfo:nil repeats:NO]; // Timer is non-repeating, may have invalidated itself, so we had to retain it. } - (void)checkForUpdatesInBackground { // Do not use reachability for a preflight check. This can be deceptive and a bad idea. Apple does not recommend doing it. SUUpdateDriver *theUpdateDriver = [[([self automaticallyDownloadsUpdates] ? [SUAutomaticUpdateDriver class] : [SUScheduledUpdateDriver class])alloc] initWithUpdater:self]; [self checkForUpdatesWithDriver:theUpdateDriver]; } - (BOOL)mayUpdateAndRestart { return (!self.delegate || ![self.delegate respondsToSelector:@selector(updaterShouldRelaunchApplication:)] || [self.delegate updaterShouldRelaunchApplication:self]); } - (IBAction)checkForUpdates:(id)__unused sender { if (self.driver && [self.driver isInterruptible]) { [self.driver abortUpdate]; } [self checkForUpdatesWithDriver:[[SUUserInitiatedUpdateDriver alloc] initWithUpdater:self]]; } - (void)checkForUpdateInformation { [self checkForUpdatesWithDriver:[[SUProbingUpdateDriver alloc] initWithUpdater:self]]; } - (void)installUpdatesIfAvailable { if (self.driver && [self.driver isInterruptible]) { [self.driver abortUpdate]; } SUUIBasedUpdateDriver *theUpdateDriver = [[SUUserInitiatedUpdateDriver alloc] initWithUpdater:self]; theUpdateDriver.automaticallyInstallUpdates = YES; [self checkForUpdatesWithDriver:theUpdateDriver]; } - (void)checkForUpdatesWithDriver:(SUUpdateDriver *)d { if ([self updateInProgress]) { return; } if (self.checkTimer) { [self.checkTimer invalidate]; self.checkTimer = nil; } // Timer is non-repeating, may have invalidated itself, so we had to retain it. [self updateLastUpdateCheckDate]; if( [self.delegate respondsToSelector: @selector(updaterMayCheckForUpdates:)] && ![self.delegate updaterMayCheckForUpdates: self] ) { [self scheduleNextUpdateCheck]; return; } self.driver = d; // If we're not given a driver at all, just schedule the next update check and bail. if (!self.driver) { [self scheduleNextUpdateCheck]; return; } [self checkIfConfiguredProperly]; NSURL *theFeedURL = [self parameterizedFeedURL]; if (theFeedURL) // Use a NIL URL to cancel quietly. [self.driver checkForUpdatesAtURL:theFeedURL host:self.host]; else [self.driver abortUpdate]; } - (void)registerAsObserver { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDriverDidFinish:) name:SUUpdateDriverFinishedNotification object:nil]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey] options:(NSKeyValueObservingOptions)0 context:(__bridge void *)(SUUpdaterDefaultsObservationContext)]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey] options:(NSKeyValueObservingOptions)0 context:(__bridge void *)(SUUpdaterDefaultsObservationContext)]; } - (void)unregisterAsObserver { @try { [[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:[@"values." stringByAppendingString:SUScheduledCheckIntervalKey]]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:[@"values." stringByAppendingString:SUEnableAutomaticChecksKey]]; } @catch (NSException *) { SULog(@"Error: [SUUpdater unregisterAsObserver] called, but the updater wasn't registered as an observer."); } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == (__bridge void *)(SUUpdaterDefaultsObservationContext)) { // Allow a small delay, because perhaps the user or developer wants to change both preferences. This allows the developer to interpret a zero check interval as a sign to disable automatic checking. // Or we may get this from the developer and from our own KVO observation, this will effectively coalesce them. [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil]; [self performSelector:@selector(resetUpdateCycle) withObject:nil afterDelay:1]; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (void)resetUpdateCycle { [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil]; [self scheduleNextUpdateCheck]; } - (void)setAutomaticallyChecksForUpdates:(BOOL)automaticallyCheckForUpdates { [self.host setBool:automaticallyCheckForUpdates forUserDefaultsKey:SUEnableAutomaticChecksKey]; // Hack to support backwards compatibility with older Sparkle versions, which supported // disabling updates by setting the check interval to 0. if (automaticallyCheckForUpdates && (NSInteger)[self updateCheckInterval] == 0) { [self setUpdateCheckInterval:SUDefaultUpdateCheckInterval]; } [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil]; // Provide a small delay in case multiple preferences are being updated simultaneously. [self performSelector:@selector(resetUpdateCycle) withObject:nil afterDelay:1]; } - (BOOL)automaticallyChecksForUpdates { // Don't automatically update when the check interval is 0, to be compatible with 1.1 settings. if ((NSInteger)[self updateCheckInterval] == 0) { return NO; } return [self.host boolForKey:SUEnableAutomaticChecksKey]; } - (void)setAutomaticallyDownloadsUpdates:(BOOL)automaticallyUpdates { [self.host setBool:automaticallyUpdates forUserDefaultsKey:SUAutomaticallyUpdateKey]; } - (BOOL)automaticallyDownloadsUpdates { // If the host doesn't allow automatic updates, don't ever let them happen if (!self.host.allowsAutomaticUpdates) { return NO; } // Otherwise, automatically downloading updates is allowed. Does the user want it? return [self.host boolForUserDefaultsKey:SUAutomaticallyUpdateKey]; } - (void)setFeedURL:(NSURL *)feedURL { if (![NSThread isMainThread]) [NSException raise:@"SUThreadException" format:@"This method must be called on the main thread"]; [self.host setObject:[feedURL absoluteString] forUserDefaultsKey:SUFeedURLKey]; } - (NSURL *)feedURL { if (![NSThread isMainThread]) [NSException raise:@"SUThreadException" format:@"This method must be called on the main thread"]; // A value in the user defaults overrides one in the Info.plist (so preferences panels can be created wherein users choose between beta / release feeds). NSString *appcastString = [self.host objectForKey:SUFeedURLKey]; if ([self.delegate respondsToSelector:@selector(feedURLStringForUpdater:)]) appcastString = [self.delegate feedURLStringForUpdater:self]; if (!appcastString) // Can't find an appcast string! [NSException raise:@"SUNoFeedURL" format:@"You must specify the URL of the appcast as the %@ key in either the Info.plist or the user defaults!", SUFeedURLKey]; NSCharacterSet *quoteSet = [NSCharacterSet characterSetWithCharactersInString:@"\"\'"]; // Some feed publishers add quotes; strip 'em. NSString *castUrlStr = [appcastString stringByTrimmingCharactersInSet:quoteSet]; if (!castUrlStr || [castUrlStr length] == 0) return nil; else return [NSURL URLWithString:castUrlStr]; } - (NSString *)userAgentString { if (customUserAgentString) { return customUserAgentString; } NSString *version = [self.sparkleBundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey]; NSString *userAgent = [NSString stringWithFormat:@"%@/%@ Sparkle/%@", [self.host name], [self.host displayVersion], version ? version : @"?"]; NSData *cleanedAgent = [userAgent dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; return [[NSString alloc] initWithData:cleanedAgent encoding:NSASCIIStringEncoding]; } - (void)setSendsSystemProfile:(BOOL)sendsSystemProfile { [self.host setBool:sendsSystemProfile forUserDefaultsKey:SUSendProfileInfoKey]; } - (BOOL)sendsSystemProfile { return [self.host boolForKey:SUSendProfileInfoKey]; } - (NSURL *)parameterizedFeedURL { NSURL *baseFeedURL = [self feedURL]; // Determine all the parameters we're attaching to the base feed URL. BOOL sendingSystemProfile = [self sendsSystemProfile]; // Let's only send the system profiling information once per week at most, so we normalize daily-checkers vs. biweekly-checkers and the such. NSDate *lastSubmitDate = [self.host objectForUserDefaultsKey:SULastProfileSubmitDateKey]; if (!lastSubmitDate) { lastSubmitDate = [NSDate distantPast]; } const NSTimeInterval oneWeek = 60 * 60 * 24 * 7; sendingSystemProfile &= (-[lastSubmitDate timeIntervalSinceNow] >= oneWeek); NSArray *parameters = @[]; if ([self.delegate respondsToSelector:@selector(feedParametersForUpdater:sendingSystemProfile:)]) { parameters = [parameters arrayByAddingObjectsFromArray:[self.delegate feedParametersForUpdater:self sendingSystemProfile:sendingSystemProfile]]; } if (sendingSystemProfile) { parameters = [parameters arrayByAddingObjectsFromArray:[self.host systemProfile]]; [self.host setObject:[NSDate date] forUserDefaultsKey:SULastProfileSubmitDateKey]; } if ([parameters count] == 0) { return baseFeedURL; } // Build up the parameterized URL. NSMutableArray *parameterStrings = [NSMutableArray array]; for (NSDictionary *currentProfileInfo in parameters) { [parameterStrings addObject:[NSString stringWithFormat:@"%@=%@", [[[currentProfileInfo objectForKey:@"key"] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [[[currentProfileInfo objectForKey:@"value"] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; } NSString *separatorCharacter = @"?"; if ([baseFeedURL query]) { separatorCharacter = @"&"; // In case the URL is already http://foo.org/baz.xml?bat=4 } NSString *appcastStringWithProfile = [NSString stringWithFormat:@"%@%@%@", [baseFeedURL absoluteString], separatorCharacter, [parameterStrings componentsJoinedByString:@"&"]]; // Clean it up so it's a valid URL return [NSURL URLWithString:appcastStringWithProfile]; } - (void)setUpdateCheckInterval:(NSTimeInterval)updateCheckInterval { [self.host setObject:@(updateCheckInterval) forUserDefaultsKey:SUScheduledCheckIntervalKey]; if ((NSInteger)updateCheckInterval == 0) { // For compatibility with 1.1's settings. [self setAutomaticallyChecksForUpdates:NO]; } [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetUpdateCycle) object:nil]; // Provide a small delay in case multiple preferences are being updated simultaneously. [self performSelector:@selector(resetUpdateCycle) withObject:nil afterDelay:1]; } - (NSTimeInterval)updateCheckInterval { // Find the stored check interval. User defaults override Info.plist. NSNumber *intervalValue = [self.host objectForKey:SUScheduledCheckIntervalKey]; if (intervalValue) return [intervalValue doubleValue]; else return SUDefaultUpdateCheckInterval; } - (void)dealloc { [self unregisterAsObserver]; if (checkTimer) { [checkTimer invalidate]; } // Timer is non-repeating, may have invalidated itself, so we had to retain it. } - (BOOL)validateMenuItem:(NSMenuItem *)item { if ([item action] == @selector(checkForUpdates:)) { return ![self updateInProgress]; } return YES; } - (BOOL)updateInProgress { return self.driver && ([self.driver finished] == NO); } - (NSBundle *)hostBundle { return [self.host bundle]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUpdater_Private.h ================================================ // // SUUpdater_Private.h // Sparkle // // Created by Andy Matuschak on 5/9/11. // Copyright 2011 Andy Matuschak. All rights reserved. // #import "SUUpdater.h" @interface SUUpdater (Private) @property (readonly) BOOL mayUpdateAndRestart; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUserInitiatedUpdateDriver.h ================================================ // // SUUserInitiatedUpdateDriver.h // Sparkle // // Created by Andy Matuschak on 5/30/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUUSERINITIATEDUPDATEDRIVER_H #define SUUSERINITIATEDUPDATEDRIVER_H #import #import "SUUIBasedUpdateDriver.h" @interface SUUserInitiatedUpdateDriver : SUUIBasedUpdateDriver @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUUserInitiatedUpdateDriver.m ================================================ // // SUUserInitiatedUpdateDriver.m // Sparkle // // Created by Andy Matuschak on 5/30/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUUserInitiatedUpdateDriver.h" #import "SUStatusController.h" #import "SUHost.h" @interface SUUserInitiatedUpdateDriver () @property (strong) SUStatusController *checkingController; @property (assign, getter=isCanceled) BOOL canceled; @end @implementation SUUserInitiatedUpdateDriver @synthesize checkingController; @synthesize canceled; - (void)closeCheckingWindow { if (self.checkingController) { [[self.checkingController window] close]; self.checkingController = nil; } } - (void)cancelCheckForUpdates:(id)__unused sender { [self closeCheckingWindow]; self.canceled = YES; } - (void)checkForUpdatesAtURL:(NSURL *)URL host:(SUHost *)aHost { self.checkingController = [[SUStatusController alloc] initWithHost:aHost]; [[self.checkingController window] center]; // Force the checking controller to load its window. [self.checkingController beginActionWithTitle:SULocalizedString(@"Checking for updates...", nil) maxProgressValue:0.0 statusText:nil]; [self.checkingController setButtonTitle:SULocalizedString(@"Cancel", nil) target:self action:@selector(cancelCheckForUpdates:) isDefault:NO]; [self.checkingController showWindow:self]; [super checkForUpdatesAtURL:URL host:aHost]; // For background applications, obtain focus. // Useful if the update check is requested from another app like System Preferences. if ([aHost isBackgroundApplication]) { [NSApp activateIgnoringOtherApps:YES]; } } - (void)appcastDidFinishLoading:(SUAppcast *)ac { if (self.isCanceled) { [self abortUpdate]; return; } [self closeCheckingWindow]; [super appcastDidFinishLoading:ac]; } - (void)abortUpdateWithError:(NSError *)error { [self closeCheckingWindow]; [super abortUpdateWithError:error]; } - (void)abortUpdate { [self closeCheckingWindow]; [super abortUpdate]; } - (BOOL)itemContainsValidUpdate:(SUAppcastItem *)ui { // We don't check to see if this update's been skipped, because the user explicitly *asked* if he had the latest version. return [[self class] hostSupportsItem:ui] && [self isItemNewer:ui]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUVersionComparisonProtocol.h ================================================ // // SUVersionComparisonProtocol.h // Sparkle // // Created by Andy Matuschak on 12/21/07. // Copyright 2007 Andy Matuschak. All rights reserved. // #ifndef SUVERSIONCOMPARISONPROTOCOL_H #define SUVERSIONCOMPARISONPROTOCOL_H #if __has_feature(modules) @import Cocoa; #else #import #endif #import "SUExport.h" /*! Provides version comparison facilities for Sparkle. */ @protocol SUVersionComparison /*! An abstract method to compare two version strings. Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent. */ - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUVersionDisplayProtocol.h ================================================ // // SUVersionDisplayProtocol.h // EyeTV // // Created by Uli Kusterer on 08.12.09. // Copyright 2009 Elgato Systems GmbH. All rights reserved. // #if __has_feature(modules) @import Cocoa; #else #import #endif #import "SUExport.h" /*! Applies special display formatting to version numbers. */ @protocol SUVersionDisplay /*! Formats two version strings. Both versions are provided so that important distinguishing information can be displayed while also leaving out unnecessary/confusing parts. */ - (void)formatVersion:(NSString **)inOutVersionA andVersion:(NSString **)inOutVersionB; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUWindowController.h ================================================ // // SUWindowController.h // Sparkle // // Created by Andy Matuschak on 2/13/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifndef SUWINDOWCONTROLLER_H #define SUWINDOWCONTROLLER_H #import @class SUHost; @interface SUWindowController : NSWindowController // We use this instead of plain old NSWindowController initWithWindowNibName so that we'll be able to find the right path when running in a bundle loaded from another app. - (instancetype)initWithWindowNibName:(NSString *)nibName; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/SUWindowController.m ================================================ // // SUWindowController.m // Sparkle // // Created by Andy Matuschak on 2/13/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUWindowController.h" #import "SUHost.h" @implementation SUWindowController - (instancetype)initWithWindowNibName:(NSString *)nibName { self = [super initWithWindowNibName:nibName owner:self]; return self; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/Sparkle-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType FMWK CFBundleShortVersionString ${CURRENT_PROJECT_VERSION} CFBundleSignature ???? CFBundleVersion ${CURRENT_PROJECT_VERSION} NSPrincipalClass ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/Sparkle.h ================================================ // // Sparkle.h // Sparkle // // Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) // Copyright 2006 Andy Matuschak. All rights reserved. // #ifndef SPARKLE_H #define SPARKLE_H #if __has_feature(modules) @import Cocoa; #else #import #endif // This list should include the shared headers. It doesn't matter if some of them aren't shared (unless // there are name-space collisions) so we can list all of them to start with: #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUStandardVersionComparator.h" #import "SUUpdater.h" #import "SUVersionComparisonProtocol.h" #import "SUVersionDisplayProtocol.h" #import "SUErrors.h" #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/Sparkle.pch ================================================ // // Sparkle.pch // Sparkle // // Created by Andy Matuschak on 7/23/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #ifdef __OBJC__ #if !defined(__clang_major__) || __clang_major__ < 7 #define __nonnull #define __nullable #endif #import #import "SUConstants.h" #import "SUErrors.h" #define SULocalizedString(key, comment) NSLocalizedStringFromTableInBundle(key, @"Sparkle", (NSBundle *__nonnull)([NSBundle bundleWithIdentifier:SUBundleIdentifier] ? [NSBundle bundleWithIdentifier:SUBundleIdentifier] : [NSBundle mainBundle]), comment) // Work around a warning generated by some versions of Xcode that we still support, // when using NSParameterAssert #define SUParameterAssert(condition) \ NSAssert((condition), @"Invalid parameter not satisfying: %@", @(#condition)) #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ar.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ar.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ar.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this. This is the information that would be sent: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ar.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "تم تنزيل %1$@ %2$@ وهو جاهز للاستخدام، هل ترغب بتثبيت التحديث وإعادة تشغيل %1$@ الآن؟"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "لا يمكن تحديث %1$@ إذا شُغّل من وحدة تخزين للقراءة فقط كصورة قرص أو قارئ الأقراص الضوئية. انقل %1$@ إلى مجلد التطبيقات وأعد تشغيله من هناك ثم حاول مجددًا."; "%@ %@ is currently the newest version available." = "الإصدار %2$@ هو أحدث إصدار متوفر حاليًا لتطبيق %1$@"; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "لديك الإصدار %3$@ من تطبيق %1$@، هل ترغب بتزيل الإصدار %2$@ الآن"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "تم تنزيل %@"; "%@ of %@" = "%1$@ من %2$@"; "A new version of %@ is available!" = "يتوفر إصدار جديد من تطبيق %@"; "A new version of %@ is ready to install!" = "الإصدار الجديد من تطبيق %@ جاهز للتثبيت"; "An error occurred in retrieving update information. Please try again later." = "حال خطأ دون الحصول على معلومات حول التحديث، الرجاء المحاولة لاحقًا."; "An error occurred while downloading the update. Please try again later." = "حدث خطأ أثناء تنزيل التحديث، الرجاء المحاولة لاحقًا."; "An error occurred while extracting the archive. Please try again later." = "حدث خطأ أثناء استخراج الأرشيف، الرجاء المحاولة لاحقًا."; "An error occurred while installing the update. Please try again later." = "حدث خطأ أثناء تثبيت التحديث، الرجاء المحاولة لاحقًا."; "An error occurred while parsing the update feed." = "حدث خطأ أثناء تحليل المعلومات المزوّدَة حول التحديث."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "حدث خطأ أثناء إعادة تشغيل %1$@، إلا أن الإصدار الجديد سيكون متوفرًا في المرة القادمة التي تشغل فيها %1$@"; /* the unit for bytes */ "B" = "بايت"; "Cancel" = "إلغاء"; "Cancel Update" = "إلغاء التحديث"; "Checking for updates..." = "يجري التحقق من وجود تحديثات..."; /* Take care not to overflow the status window. */ "Downloading update..." = "يجري تنزيل التحديث..."; /* Take care not to overflow the status window. */ "Extracting update..." = "يجري استخراج التحديث..."; /* the unit for gigabytes */ "GB" = "غيغا بايت"; "Install and Relaunch" = "تثبيت وإعادة تشغيل التطبيق"; /* Take care not to overflow the status window. */ "Installing update..." = "يجري تثبيت التحديث..."; /* the unit for kilobytes */ "KB" = "كيلو بايت"; /* the unit for megabytes */ "MB" = "ميغا بايت"; /* OK button. */ "OK" = "موافق"; /* Status message on progress window once download has finished. */ "Ready to Install" = "جاهز للتثبيت"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "هل ترغب في أن يتحقق %1$@ من وجود تحديثات تلقائيًا؟ يمكنك التحقق من وجود تحديثات يدويًا في أي وقت من قائمة %1$@."; "Update Error!" = "حدث خطأ أثناء التحديث"; "Updating %@" = "تحديث %@"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." ="لديك أحدث إصدار من تطبيق %@"; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "لديك أحدث إصدار"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "مزيد من التفاصيل..."; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ca.lproj/SUAutomaticUpdateAlert.strings ================================================ /* NSWindow () : (oid:5) */ "" = ""; /* NSButton (Automatically download and install updates in the future) : (oid:17) */ "Automatically download and install updates in the future" = "Baixa i instal·la les actualitzacions automàticament en el futur"; /* NSButton (Relaunch Later) : (oid:16) */ "Relaunch Later" = "Reinicia el programa més tard"; /* NSButton (Relaunch Now) : (oid:15) */ "Relaunch Now" = "Reinicia el programa ara"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ca.lproj/SUUpdateAlert.strings ================================================ /* NSButton (Automatically download and install updates in the future) : (oid:92) */ "Automatically download and install updates in the future" = "Descarrega i instal·la les actualitzacions automàticament en el futur"; /* NSButton (Install Update) : (oid:76) */ "Install Update" = "Instal·la l'actualització"; /* NSTextField (Release Notes:) : (oid:17) */ "Release Notes:" = "Notes d'aquesta versió:"; /* NSButton (Remind Me Later) : (oid:22) */ "Remind Me Later" = "Recorda-m'ho més tard"; /* NSButton (Skip This Version) : (oid:23) */ "Skip This Version" = "Omet aquesta versió"; /* NSWindow (Software Update) : (oid:5) */ "Software Update" = "Actualització del programari"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ca.lproj/Sparkle.strings ================================================ "%@ of %@" = "%1$@ de %2$@"; "%1$@ can't be updated when it's running from a disk image. Move %1$@ to your Applications folder, relaunch it, and try again." = "%1$@ no es pot actualitzar quan funciona des d'un disc d'imatge. Moveu %1$@ al vostre directori Aplicacions, reinicieu-lo, i torneu a provar-ho."; "%@ %@ has been installed and will be ready to use next time %@ starts! Would you like to relaunch now?" = "%@ %@ ha estat instal·lat i estarà llest per a ser utilitzat la propera vegada que s'iniciï %@! Voleu reiniciar ara?"; "%@ %@ is currently the newest version available." = "%@ %@ és la versió disponible més actual."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%@ %@ està disponible (ara teniu %@). Voleu actualitzar?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help." = "%@ no té permís per escriure al directori d'aplicacions! Esteu executant el programa des d'un disc d'imatge? Si no és així, demaneu ajuda al vostre administrador."; "A new version of %@ has been installed!" = "S'ha instal·lat una nova versió de %@!"; "A new version of %@ is available!" = "Hi ha una nova versió de %@ disponible!"; "An error occurred during installation. Please try again later." = "Hi ha hagut un error durant la instal·lació. Torneu-ho a provar més tard."; "An error occurred in retrieving update information. Please try again later." = "Hi ha hagut un error obtenint la informació d'actualització. Torneu a provar-ho més tard."; "An error occurred while extracting the archive. Please try again later." = "Hi ha hagut un error al extreure l'arxiu? Torneu a provar-ho més tard."; "An error occurred while trying to download the file. Please try again later." = "Hi ha hagut un error al intentar descarregar el fitxer. Torneu a provar-ho més tard."; "An update is already in progress!" = "Ja hi ha una actualització en progrés!"; "Cancel" = "Cancel·la"; "Check for updates on startup?" = "Comprovar si hi ha actualitzacions a l'inici?"; "Downloading update..." = "Descarregant l'actualització…"; "Extracting update..." = "Extraient l'actualització…"; "Install and Relaunch" = "Instal·la i reinicia"; "Installing update..." = "Instal·lant l'actualització…"; "No" = "No"; "OK" = "D'acord"; "Ready to Install" = "A punt per instal·lar"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Voleu que %1$@ comprovi si hi ha noves actualitzacions al iniciar? Si no, podeu inciar la comprovació manualment des del menú %1$@."; "Update Error!" = "Error d'actualització!"; "Updating %@" = "Actualitzant %@"; "Yes" = "Sí"; "You're up-to-date!" = "Ja esteu al dia!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/cs.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/cs.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/cs.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Informace z anonymního systémového profilu pomáhají vývojářům lépe plánovat budoucí vývoj aplikace. Budete-li mít nějaký dotaz, obraťte se na nás. Tyto informace by měly být odeslány: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/cs.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ byl instalován a je připraven k použití po příštím spuštění. Přejete si aplikaci %1$@ nyní nainstalovat a znovu spustit?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Aplikace %1$@ nemůže být aktualizována, protože je spuštěna z média na které nelze zapisovat. Může to být CD/DVD-ROM, obraz disku nebo jen nemáte právo zápisu na disk. Přesuňte aplikaci %1$@ do vaší složky Aplikace, spusťte ji z tohoto umístěni znovu."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ je nejnovější dostupná verze"; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "Je k dispozici %1$@ %2$@ - máte %3$@. Přejete si nyní zkopírovat aktualizaci?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ je nyní k dispozici – vy máte %3$@. Přejete si zobrazit o této aktualizaci další informace?"; "%@ downloaded" = "zkopírováno %@"; "%@ of %@" = "%1$@ z %2$@"; "A new version of %@ is available!" = "Je dostupná nová verze aplikace %@!"; "A new version of %@ is ready to install!" = "Nová verze %@ je připravena k instalaci!"; "An error occurred in retrieving update information. Please try again later." = "Při získávání údajů o aktualizaci se vyskytla chyba. Zkuste to prosím později."; "An error occurred while downloading the update. Please try again later." = "Při kopírování souboru se vyskytla chyba. Zkuste to prosím později."; "An error occurred while extracting the archive. Please try again later." = "Při rozbalování archivu se vyskytla chyba. Zkuste to prosím později."; "An error occurred while installing the update. Please try again later." = "Při instalaci aktualizace se vyskytla chyba. Zkuste to prosím později."; "An error occurred while parsing the update feed." = "Při zpracovávání údajů o aktualizaci se vyskytla chyba. Zkuste to prosím později."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Vyskytla se chyba při novém spouštění aplikace %1$@, ale nová verze bude dostupná při dalším spuštění %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Zrušit"; "Cancel Update" = "Zrušit aktualizaci"; "Checking for updates..." = "Ověřuje se dostupnost aktualizací…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Kopíruje se aktualizace…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Rozbaluje se aktualizace…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Instalovat a znovu spustit"; /* Take care not to overflow the status window. */ "Installing update..." = "Instaluje se aktualizace…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "Budiž"; "Ready to Install" = "Připraveno k instalaci"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Přejete si, aby aplikace %1$@ automaticky ověřovala dostupnost aktualizací? Tuto volbu můžete kdykoliv změnit v nabídce %1$@."; "Update Error!" = "Chyba při aktualizaci!"; "Updating %@" = "Aktualizuje se %@"; "You already have the newest version of %@." = "Máte nainstalovanou nejnovější verzi aplikace @."; "You're up-to-date!" = "Vaše verze je aktuální!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/da.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/da.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/da.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this. This is the information that would be sent: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/da.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ er hentet og klar til brug! Vil du installere og genstarte %1$@ nu?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ kan ikke opdateres når det køres fra en kun læsbar enhed. Flyt %1$@ til mappen Programmer, genstart derfra og prøv igen."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ er den aktuelle version."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ er tilgængelig! Du har %3$@. Skal den hentes nu?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ hentet"; "%@ of %@" = "%1$@ af %2$@"; "A new version of %@ is available!" = "En ny version af %@ er tilgængelig!"; "A new version of %@ is ready to install!" = "En ny version af %@ er klar til installering!"; "An error occurred in retrieving update information. Please try again later." = "Kunne ikke modtage informationer om opdateringer. Kontroller at du har forbindelse til internettet eller prøv igen senere."; "An error occurred while downloading the update. Please try again later." = "Opdateringen kunne ikke hentes. Prøv igen senere."; "An error occurred while extracting the archive. Please try again later." = "Arkivet kunne ikke udpakkes. Prøv igen senere."; "An error occurred while installing the update. Please try again later." = "Opdateringen kunne ikke installeres. Prøv igen senere."; "An error occurred while parsing the update feed." = "An error occurred while parsing the update feed."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "%1$@ kunne ikke genstartes. Den nye version vil være tilgængelig, næste gang %1$@ startes."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Annuller"; "Cancel Update" = "Annuller opdatering"; "Checking for updates..." = "Søger efter opdateringer…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Henter opdatering…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Udpakker arkiver…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Installer og genstart"; /* Take care not to overflow the status window. */ "Installing update..." = "Installerer opdatering…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "OK"; /* Status message on progress window once download has finished. */ "Ready to Install" = "Klar til installering"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Skal %1$@ søge efter opdateringer automatisk? Du kan altid søge efter opdateringer manuelt fra programmets menu."; "Update Error!" = "Der opstod en fejl!"; "Updating %@" = "Opdaterer %@"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "Du har den seneste version af %@."; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "Der er ingen opdateringer"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Læs mere…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/de.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/de.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/de.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Das anonymisierte Systemprofil unterstützt uns bei der zukünftigen Entwicklung. Bitte kontaktieren Sie uns, wenn Sie Fragen hierzu haben. Diese Informationen würden an uns gesendet: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/de.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ wurde heruntergeladen und steht zur Verwendung bereit! Dies ist eine wichtige Aktualisierung. Möchten Sie %1$@ jetzt durch die neue Version ersetzen und neu starten?"; "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ wurde heruntergeladen und steht zur Verwendung bereit! Möchten Sie %1$@ jetzt durch die neue Version ersetzen und neu starten?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ kann nicht aktualisiert werden, wenn es von einem Volumen ohne Schreibzugriff (z.B. Disk Image oder CD/DVD) gestartet wurde. Kopieren Sie %1$@ in den Programme Ordner, aktualisieren Sie es von dort."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ ist zur Zeit die neueste verfügbare Version."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ ist verfügbar (Sie verwenden Version %3$@). Möchten Sie die neue Version jetzt herunterladen?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ ist verfügbar (Sie verwenden Version %3$@). Möchten Sie mehr über diese Aktualisierung erfahren?"; "%@ downloaded" = "%@ heruntergeladen"; "%@ of %@" = "%1$@ von %2$@"; "A new version of %@ is available!" = "Eine neue Version von %@ ist verfügbar!"; "A new version of %@ is ready to install!" = "Eine neue Version von %@ ist zur Installation bereit!"; "An error occurred in retrieving update information. Please try again later." = "Bei der Aktualisierung trat ein Fehler auf. Bitte versuchen Sie es später erneut."; "An error occurred while downloading the update. Please try again later." = "Beim Herunterladen ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut."; "An error occurred while extracting the archive. Please try again later." = "Beim Entpacken der Daten ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut."; "An error occurred while installing the update. Please try again later." = "Bei der Installation der Aktualisierung ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut."; "An error occurred while parsing the update feed." = "Beim Lesen der Aktualisierungsinformationen ist ein Fehler aufgetreten."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Beim erneuten Starten von %1$@ ist ein Fehler aufgetreten. Die neue Version von %1$@ steht beim nächsten Programmstart zur Verfügung."; "An important update to %@ is ready to install" = "Eine wichtige Aktualisierung von %@ steht zur Installation bereit"; /* the unit for bytes */ "B" = "B"; "Cancel" = "Abbrechen"; "Cancel Update" = "Aktualisierung abbrechen"; "Checking for updates..." = "Suche nach Aktualisierungen …"; /* Take care not to overflow the status window. */ "Downloading update..." = "Aktualisierung wird heruntergeladen …"; /* Take care not to overflow the status window. */ "Extracting update..." = "Aktualisierung entpacken …"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Installieren und erneut starten"; /* Take care not to overflow the status window. */ "Installing update..." = "Aktualisierung installieren …"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Bereit zum Installieren"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Soll %1$@ automatisch nach Aktualisierungen suchen? Sie können im %1$@-Menü auch manuell nach Aktualisierungen suchen."; "The update is improperly signed." = "Die Aktualisierung ist nicht ordnungsgemäß signiert."; "Update Error!" = "Fehler beim Aktualisieren!"; "Updating %@" = "Aktualisierung von %@"; "You already have the newest version of %@." = "Sie haben bereits die neueste Version von %@."; "You're up-to-date!" = "Sie sind auf dem neuesten Stand!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Mehr über …"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/el.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/el.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/el.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Το ανώνυμο προφίλ συστήματος χρησιμοποιείται για να μας βοηθήσει σε μελλοντική ανάπτυξη του προγράμματος. Παρακαλώ επικοινωνήστε μαζί μας άν έχετε ερωτήσεις. Αυτές είναι οι πληροφορίες που θα σταλούν: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/el.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "Το %1$@ %2$@ έχει ληφθεί και είναι έτοιμο προς χρήση! Θα θέλατε να το εγκαταστήσετε και να επανεκκινήσετε το %1$@ τώρα;"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Το %1$@ δεν μπορεί να ενημερωθεί όσο τρέχει από έναν δίσκο ανάγνωσης-μόνο ή έναν οπτικό δίσκο. Μεταφέρετε το %1$@ στον φάκελο εφαρμογών σας, επανεκκινήστε το από εκεί, και ξαναδοκιμάστε."; "%@ %@ is currently the newest version available." = "Το %1$@ %2$@ είναι η τελευταία διαθέσιμη έκδοση."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "Το %1$@ %2$@ είναι πλέον διαθέσιμο--έχετε το %3$@. Θέλετε να το κατεβάσετε τώρα;"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ λήφθησαν"; "%@ of %@" = "%1$@ από %2$@"; "A new version of %@ is available!" = "Μία νέα έκδοση του %@ είναι διαθέσιμη!"; "A new version of %@ is ready to install!" = "Μία νέα έκδοση του %@ είναι έτοιμη για εγκατάσταση!"; "An error occurred in retrieving update information. Please try again later." = "Υπήρξε ένα σφάλμα κατά την ανάκτηση των πληροφοριών ενημέρωσης. Παρακαλώ δοκιμάστε αργότερα."; "An error occurred while downloading the update. Please try again later." = "Υπήρξε ένα σφάλμα κατά την λήψη της ενημέρωσης. Παρακαλώ δοκιμάστε αργότερα."; "An error occurred while extracting the archive. Please try again later." = "Υπήρξε ένα σφάλμα κατά την εξαγωγή του αρχείου. Παρακαλώ δοκιμάστε αργότερα."; "An error occurred while installing the update. Please try again later." = "Υπήρξε ένα σφάλμα κατά την εγκατάσταση της ενημέρωσης. Παρακαλώ δοκιμάστε αργότερα."; "An error occurred while parsing the update feed." = "Υπήρξε ένα σφάλμα κατά την ανάλυση του feed ενημερώσεων."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Υπήρξε ένα σφάλμα κατά την επανεκκίνηση του %1$@, αλλά η νέα έκδοση θα είναι διαθέσιμη την επόμενη φορά που θα τρέξετε το %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Ακύρωση"; "Cancel Update" = "Ακύρωση Ενημέρωσης"; "Checking for updates..." = "Έλεγχος για ενημερώσεις..."; /* Take care not to overflow the status window. */ "Downloading update..." = "Λήψη ενημέρωσης..."; /* Take care not to overflow the status window. */ "Extracting update..." = "Εξαγωγή ενημέρωσης..."; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Εγκατάσταση και Επανεκκίνηση"; /* Take care not to overflow the status window. */ "Installing update..." = "Εγκατάσταση ενημέρωσης..."; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Έτοιμο προς Εγκατάσταση"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Θέλετε το %1$@ να ελέγχει αυτόματα για ενημερώσεις; Μπορείτε πάντα να ελέγχετε για ενημερώσεις χειροκίνητα από το μενού %1$@."; "Update Error!" = "Σφάλμα Ενημέρωσης!"; "Updating %@" = "Ενημέρωση %@"; "You already have the newest version of %@." = "Έχετε ήδη την τελευταία έκδοση του %@."; "You're up-to-date!" = "Είστε ενημερωμένοι!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/en.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/en.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/en.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this. This is the information that would be sent: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/en.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?"; "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ can’t be updated when it’s running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ is currently the newest version available."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ is now available—you have %3$@. Would you like to download it now?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available—you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ downloaded"; "%@ of %@" = "%1$@ of %2$@"; "A new version of %@ is available!" = "A new version of %@ is available!"; "A new version of %@ is ready to install!" = "A new version of %@ is ready to install!"; "An error occurred in retrieving update information. Please try again later." = "An error occurred in retrieving update information. Please try again later."; "An error occurred while downloading the update. Please try again later." = "An error occurred while downloading the update. Please try again later."; "An error occurred while extracting the archive. Please try again later." = "An error occurred while extracting the archive. Please try again later."; "An error occurred while installing the update. Please try again later." = "An error occurred while installing the update. Please try again later."; "An error occurred while parsing the update feed." = "An error occurred while parsing the update feed."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@."; "An important update to %@ is ready to install" = "An important update to %@ is ready to install"; /* the unit for bytes */ "B" = "B"; "Cancel" = "Cancel"; "Cancel Update" = "Cancel Update"; "Checking for updates..." = "Checking for updates…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Downloading update…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Extracting update…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Install and Relaunch"; /* Take care not to overflow the status window. */ "Installing update..." = "Installing update…"; /* the unit for kilobytes */ "KB" = "KB"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Learn More…"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "OK"; /* Status message on progress window once download has finished. */ "Ready to Install" = "Ready to Install"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu."; "The update is improperly signed." = "The update is improperly signed."; "Update Error!" = "Update Error!"; "Updating %@" = "Updating %@"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "You already have the newest version of %@."; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "You’re up-to-date!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/es.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/es.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/es.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey La información de perfil de sistema anónimo se usa para ayudarnos a planear el trabajo de desarrollo futuro. Por favor, póngase en cantacto con nosotros si tiene preguntas sobre esto. Esta es la información que se nos enviaría: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/es.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ se han descargado y están listos para utilizar. ¿Le gustaría instalar e iniciar %1$@ ahora?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ no se puede actualizar cuando se ejecuta desde un volumen de sólo lectura como imagen de disco o medio óptico. Mueva %1$@ a su carpeta de Aplicaciones y trate de iniciarlo desde allí."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ es la última versión disponible."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ está disponible (usted tiene la %3$@). ¿Desea descargarla ahora?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ descargado"; "%@ of %@" = "%1$@ de %2$@"; "A new version of %@ is available!" = "¡Hay una nueva versión de %@ !"; "A new version of %@ is ready to install!" = "¡Una versión nueva de %@ está lista para instalar!"; "An error occurred in retrieving update information. Please try again later." = "Ocurrió un error al recopilar información sobre la actualización. Por favor, inténtelo de nuevo más tarde."; "An error occurred while downloading the update. Please try again later." = "Ocurrió un error mientras se descargaba la actualización. Por favor intente de nuevo más tarde."; "An error occurred while extracting the archive. Please try again later." = "Ocurrió un error al extraer el archivo. Por favor, inténtelo de nuevo más tarde."; "An error occurred while installing the update. Please try again later." = "Ocurrió un error mientras se instalaba la actualización. Por favor intente de nuevo más tarde."; "An error occurred while parsing the update feed." = "Ocurrió un error mientras se analizaba la entrada de la actualización."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Ocurrió un error mientras se re-iniciaba %1$@, pero la nueva versión estará disponible la próxima vez que ejecute %1$@."; /* the unit for bytes */ "B" = "O"; "Cancel" = "Cancelar"; "Cancel Update" = "Cancelar actualización"; "Checking for updates..." = "Buscando actualizaciones …"; /* Take care not to overflow the status window. */ "Downloading update..." = "Descargando actualización…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Extrayendo actualización…"; /* the unit for gigabytes */ "GB" = "GO"; "Install and Relaunch" = "Instalar y volver a arrancar"; /* Take care not to overflow the status window. */ "Installing update..." = "Instalando actualización…"; /* the unit for kilobytes */ "KB" = "KO"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Preparado para instalar"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "¿Debería automáticamente %1$@ buscar las actualizaciones? Puede hacerlo también manualmente desde el menú %1$@."; "Update Error!" = "¡Error de actualización!"; "Updating %@" = "Actualizando %@"; "You already have the newest version of %@." = "Ya tiene la versión más reciente de %@."; "You're up-to-date!" = "Ya tiene la versión más reciente"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Más información…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fi.lproj/SUAutomaticUpdateAlert.strings ================================================ /* NSWindow () : (oid:5) */ "" = ""; /* NSButton (Automatically download and install updates in the future) : (oid:17) */ "Automatically download and install updates in the future" = "Hae ja asenna päivitykset jatkossa automaattisesti"; /* NSButton (Relaunch Later) : (oid:16) */ "Relaunch Later" = "Käynnistä uudelleen myöhemmin"; /* NSButton (Relaunch Now) : (oid:15) */ "Relaunch Now" = "Käynnistä uudelleen nyt"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fi.lproj/SUUpdateAlert.strings ================================================ /* NSButton (Automatically download and install updates in the future) : (oid:92) */ "Automatically download and install updates in the future" = "Hae ja asenna päivitykset jatkossa automaattisesti"; /* NSButton (Install Update) : (oid:76) */ "Install Update" = "Asenna päivitys"; /* NSTextField (Release Notes:) : (oid:17) */ "Release Notes:" = "Tietoa päivityksestä:"; /* NSButton (Remind Me Later) : (oid:22) */ "Remind Me Later" = "Muistuta myöhemmin"; /* NSButton (Skip This Version) : (oid:23) */ "Skip This Version" = "Ohita tämä versio"; /* NSWindow (Software Update) : (oid:5) */ "Software Update" = "Ohjelmiston pävitys"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fi.lproj/Sparkle.strings ================================================ "%@ of %@" = "%1$@ / %2$@"; "%@ %@ has been installed and will be ready to use next time %@ starts! Would you like to relaunch now?" = "%1$@ %2$@ on asennettu ja on valmiina käyttöön seuraavan kerran kun %3$@ käynnistetään! Haluatko käynnistää ohjelman uudelleen?"; "%@ %@ is currently the newest version available." = "%1$@ %2$@ on uusin saatavilla oleva versio."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ on nyt saatavilla (sinulla on %3$@). Haluatko ladata sen nyt?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help." = "Käyttäjällä %@ ei ole oikeuksia ohjelman kansioon! Ajatko ohjelmaa levytiedostosta? Jos et, kysy apua järjestelmän ylläpitäjältä."; "A new version of %@ has been installed!" = "Uusi versio ohjelmasta %@ on asennettu!"; "A new version of %@ is available!" = "Uusi versio ohjelmasta %@ on saatavilla!"; "An error occurred during installation. Please try again later." = "Asennuksen yhteydessä tapahtui virhe. Yritä myöhemmin uudelleen."; "An error occurred in retrieving update information. Please try again later." = "Päivitystietojen haussa tapahtui virhe. Yritä myöhemmin uudelleen."; "An error occurred while extracting the archive. Please try again later." = "Pakkauksen purkamisen yhteydessä tapahtui virhe. Yritä myöhemmin uudelleen."; "An error occurred while trying to download the file. Please try again later." = "Tiedoston haussa tapahtui virhe. Yritä myöhemmin uudelleen."; "An update is already in progress!" = "Päivitys on jo menossa!"; "Cancel" = "Kumoa"; "Check for updates on startup?" = "Tarkista päivityksiä käynnistyksen yhteydessä?"; "Downloading update..." = "Haen päivitystä…"; "Extracting update..." = "Puran päivitystä…"; "Install and Relaunch" = "Asenna ja käynnistä ohjelma uudelleen"; "Installing update..." = "Asennan päivityksen…"; "No" = "Ei"; "OK" = "OK"; "Ready to Install" = "Valmiina asentamaan"; "Update Error!" = "Virhe päivityksessä!"; "Updating %@" = "Päivitän %@"; "Would you like %@ to check for updates on startup? If not, you can initiate the check manually from the application menu." = "Haluatko tarkistaa päivitykset ohjelmaan %@ käynnistyksen yhteydessä? Jos et, voit käynnnistää tarkistuksen käsin ohjelmavalikosta."; "Yes" = "Kyllä"; "You're up-to-date!" = "Sinulla on viimeisin versio!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fr.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fr.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fr.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Les informations anonymes de profil système nous aident à planifier les futurs développements. Contactez-nous pour toute question à ce sujet. Ci-dessous figurent les informations qui seront transmises : SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/fr.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ a été téléchargé. Voulez-vous l’installer et relancer %1$@ maintenant ?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ ne peut pas être mis à jour quand il fonctionne à partir d’un volume en lecture seule, comme une image disque ou un lecteur optique. Déplacez %1$@ dans votre dossier Applications, relancez-le à partir de là et réessayez."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ est la version la plus récente disponible."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ est disponible ; vous utilisez la version %3$@. Voulez-vous le télécharger maintenant ?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ téléchargé"; "%@ of %@" = "%1$@ sur %2$@"; "A new version of %@ is available!" = "Une nouvelle version de %@ est disponible !"; "A new version of %@ is ready to install!" = "Une nouvelle version de %@ est prête à être installée !"; "An error occurred in retrieving update information. Please try again later." = "Une erreur est survenue en récupérant les informations de mise à jour. Veuillez réessayer plus tard."; "An error occurred while downloading the update. Please try again later." = "Une erreur est survenue pendant le téléchargement de la mise à jour. Veuillez réessayer plus tard."; "An error occurred while extracting the archive. Please try again later." = "Une erreur est survenue pendant l’extraction des données de l’archive. Veuillez réessayer plus tard."; "An error occurred while installing the update. Please try again later." = "Une erreur est survenue pendant l’installation de la mise à jour. Veuillez réessayer plus tard."; "An error occurred while parsing the update feed." = "Une erreur est survenue pendant l’analyse de la mise à jour."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Une erreur est survenue en relançant %1$@, mais la nouvelle version sera disponible à la prochaine ouverture de %1$@."; /* the unit for bytes */ "B" = "octets"; "Cancel" = "Annuler"; "Cancel Update" = "Annuler la mise à jour"; "Checking for updates..." = "Recherche de mises à jour…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Téléchargement de la mise à jour…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Extraction de la mise à jour…"; /* the unit for gigabytes */ "GB" = "Go"; "Install and Relaunch" = "Installer et relancer"; /* Take care not to overflow the status window. */ "Installing update..." = "Installation de la mise à jour…"; /* the unit for kilobytes */ "KB" = "ko"; /* the unit for megabytes */ "MB" = "Mo"; "OK" = "OK"; "Ready to Install" = "Prêt pour l’installation"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "%1$@ doit-il rechercher automatiquement les mises à jour ? La mise à jour est toujours possible manuellement depuis le menu %1$@."; "Update Error!" = "Erreur pendant la mise à jour !"; "Updating %@" = "Mise à jour de %@"; "You already have the newest version of %@." = "Vous possédez déjà la version la plus récente de %@."; "You're up-to-date!" = "Votre logiciel est à jour !"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "En savoir plus…"; "The update is improperly signed." = "La signature numérique de la mise à jour est incorrecte"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/he.lproj/SUAutomaticUpdateAlert.strings ================================================ /* NSWindow () : (oid:5) */ "" = ""; /* NSButton (Automatically download and install updates in the future) : (oid:17) */ "Automatically download and install updates in the future" = "הורד והתקן עדכונים אוטומטית גם בעתיד"; /* NSButton (Relaunch Later) : (oid:16) */ "Relaunch Later" = "אתחל מאוחר יותר"; /* NSButton (Relaunch Now) : (oid:15) */ "Relaunch Now" = "אתחל עכשיו"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/he.lproj/SUUpdateAlert.strings ================================================ /* NSButton (Automatically download and install updates in the future) : (oid:92) */ "Automatically download and install updates in the future" = "הורד והתקן עדכונים אוטומטית גם בעתיד"; /* NSButton (Install Update) : (oid:76) */ "Install Update" = "התקן עדכון"; /* NSTextField (Release Notes:) : (oid:17) */ "Release Notes:" = "פרטי גרסה:"; /* NSButton (Remind Me Later) : (oid:22) */ "Remind Me Later" = "הזכר לי מאוחר יותר"; /* NSButton (Skip This Version) : (oid:23) */ "Skip This Version" = "דלג על גרסה זו"; /* NSWindow (Software Update) : (oid:5) */ "Software Update" = "עדכון תכנה"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/he.lproj/Sparkle.strings ================================================ "%@ of %@" = "%1$@ of %2$@"; "%@ %@ has been installed and will be ready to use next time %@ starts! Would you like to relaunch now?" = "%1$@ %2$@ הותקנה ותהיה מוכנה לפעולה בפעם הבאה שתריץ את %3$@ ! ברצונך לאתחל?"; "%@ %@ is currently the newest version available." = "%1$@ %2$@ היא הגרסה האחונה הזמינה."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ זמין כעת (לך יש %3$@). ברצונך להוריד כעת?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help." = "ל %@ אין הרשאה לכתוב לתקיית התוכניות! האם אתה פועל מ- disc image? אם לא, בקש עזרה ממנהל הרשת."; "A new version of %@ has been installed!" = "גרסה חדשה של %@ הותקנה!"; "A new version of %@ is available!" = "גרסה חדשה של %@ זמינה!"; "An error occurred during installation. Please try again later." = "שגיאה בהתקנה. אנא נסה שנית במועד מרוחר יותר."; "An error occurred in retrieving update information. Please try again later." = "שגיאה בקבלת מידע על עדכונים. אנא נסה שנית במועד מרוחר יותר."; "An error occurred while extracting the archive. Please try again later." = "שגיאה בפתיחת הקובת המקווץ. אנא נסה שנית במועד מרוחר יותר."; "An error occurred while trying to download the file. Please try again later." = "שגיאה בהורדת הקובץ. אנא נסה שנית במועד מאוחר יותר."; "An update is already in progress!" = "עדכון כבר מתבצע"; "Cancel" = "ביטול"; "Check for updates on startup?" = "לבדוק עדכונים באתחול?"; "Downloading update..." = "מוריד עדכון…"; "Extracting update..." = "פותח עדכון…"; "Install and Relaunch" = "התקן ואתחל"; "Installing update..." = "מתקין עידכון…"; "No" = "לא"; "OK" = "אישור"; "Ready to Install" = "מוכן להתקנה!"; "Update Error!" = "שגיאה בעדכון!"; "Updating %@" = "מעדכן %@"; "Would you like %@ to check for updates on startup? If not, you can initiate the check manually from the application menu." = "האם ברצונך ש%@ יבדוק עדכונים באתחול? אם לא, תוכל לבדוק ידנית מתפריט התכנה."; "Yes" = "כן"; "You're up-to-date!" = "התכנה עדכנית!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/is.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/is.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/is.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Upplýsingar úr nafnlausum kerfisskýrslum eru notaðar til að hjálpa okkur við framtíðarþróun hugbúnaðarins. Ekki hika við að hafa samband ef spurningar vakna um þetta. Þetta eru upplýsingarnar sem yrðu sendar: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/is.lproj/Sparkle.strings ================================================ "%@ of %@" = "%@ af %@"; "%@ %@ has been installed and will be ready to use next time %@ starts! Would you like to relaunch now?" = "%@ %@ hefur verið sett inn og verður tiltækt við næstu ræsingu %@. Viltu endurræsa núna?"; "%@ %@ is currently the newest version available." = "%@ %@ er nýjasta útgáfan sem er fáanleg þessa stundina."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "Útgafa %2$@ af %1$@ er nú fáanlegt en þú ert með %3$@. Viltu sækja hana núna?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help." = "%@ hefur ekki heimild til að skrifa í möppu forritsins! Ertu að keyra forritið af diskmynd? Ef ekki skaltu ráðfæra þig við kerfisstjórann."; "A new version of %@ has been installed!" = "Ný útgáfa af %@ hefur verið sett inn!"; "A new version of %@ is available!" = "Ný útgáfa af %@ er fáanleg!"; "An error occurred during installation. Please try again later." = "Villa kom upp við innsetningu. Vinsamlegast reynið síðar."; "An error occurred in retrieving update information. Please try again later." = "Villa kom upp við að sækja uppfærsluupplýsingar. Vinsamlegast reynið síðar."; "An error occurred while extracting the archive. Please try again later." = "Villa kom upp við afþjöppun skráarsafns. Vinsamlegast reynið síðar."; "An error occurred while trying to download the file. Please try again later." = "Villa kom upp þegar reynt var að sækja skrána. Vinsamlegast reynið síðar"; "An update is already in progress!" = "Uppfærsla er þegar í gangi!"; "Cancel" = "Hætta við"; "Check for updates on startup?" = "Athuga með uppfærslur í ræsingu?"; "Downloading update..." = "Sækja nýja útgáfu…"; "Extracting update..." = "Afþjappa uppfærslu…"; "Install and Relaunch" = "Setja upp og ræsa aftur"; "Installing update..." = "Set inn uppfærslu…"; "Ready to Install" = "Innsetning reiðubúin"; "No" = "Nei"; "OK" = "Í lagi"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Viltu að forritið %1$@ athugi með uppfærslur þegar það er ræst? Ef ekki getur þú athugað handvirkt af %1$@-valblaðinu."; "Update Error!" = "Villa við uppfærslu!"; "Updating %@" = "Uppfæri %@"; "Yes" = "Já"; "You're up-to-date!" = "Það er allt uppfært hjá þér!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/it.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/it.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/it.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Le informazioni del profilo di sistema anomino sono utilizzate per aiutarci in futuri lavori di sviluppo. Contattaci se hai dei quesiti sull’argomento. Queste sono le informazioni che verrebbero inviate: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/it.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ è stato scaricato ed è pronto per essere utilizzato! Desideri installare e riavviare %1$@ ora?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Impossibile aggiornare %1$@ quando viene eseguito da un volume di sola lettura come un’immagine disco o un’unità ottica. Spostare %1$@ nella Cartella Applicazioni, riavviarlo e riprovare."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ è la versione più recente attualmente disponibile."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ è disponbile; disponi della versione %3$@. Desideri eseguire l’aggiornamento ora?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ scaricato"; "%@ of %@" = "%1$@ di %2$@"; "A new version of %@ is available!" = "E’ disponibile una nuova versione di %@!"; "A new version of %@ is ready to install!" = "Una nuova versione di %@ è pronta per essere installata!"; "An error occurred in retrieving update information. Please try again later." = "Si è verificato un errore durante il recupero delle informazioni sull’aggiornamento. Riprova in seguito."; "An error occurred while downloading the update. Please try again later." = "Si è verificato un errore durante lo scaricamento dell’aggiornamento. Riprova in seguito."; "An error occurred while extracting the archive. Please try again later." = "Si è verificato un errore durante l’estrazione dell’archivio. Riprova in seguito."; "An error occurred while installing the update. Please try again later." = "Si è verificato un errore durante l’installazione dell’aggiornamento. Riprova in seguito."; "An error occurred while parsing the update feed." = "Si è verificato un errore durante la lettura del feed di aggiornamento."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Si è verificato un errore al riavvio di %1$@. La nuova versione sarà comunque disponibile alla prossima esecuzione di %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Annulla"; "Cancel Update" = "Annulla Aggiornamento"; "Checking for updates..." = "Controllo aggiornamenti in corso…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Scaricamento dell’aggiornamento…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Estrazione dell’aggiornamento…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Installa e Riavvia"; /* Take care not to overflow the status window. */ "Installing update..." = "Installazione aggiornamento in corso…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Pronto per l’installazione"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Desideri che %1$@ verifichi gli aggiornamenti automaticamente? Puoi effettuare la verifica manualmente dal menu di %1$@."; "Update Error!" = "Errore di Aggiornamento!"; "Updating %@" = "Aggiornamento di %@"; "You already have the newest version of %@." = "Disponi della versione più recente di %@."; "You're up-to-date!" = "La tua applicazione è aggiornata!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Ulteriori informazioni…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ja.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ja.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ja.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey 匿名のシステムプロファイル情報は、今後我が社の企画開発を計画する上で参考にさせていただきます。この件に関してご質問があれば、ご連絡下さい。 以下の情報を送信してください: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ja.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ がダウンロードされました! これは重要なアップデートです。今すぐ %1$@ をインストールして再起動しますか?"; "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ がダウンロードされました! 今すぐ %1$@ をインストールして再起動しますか?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ は、ディスクイメージや光学式ドライブなどの読み出し専用ボリュームから起動している場合アップデートできません。%1$@ をアプリケーションフォルダに移動し、そこから再起動したあとやり直してください。"; "%@ %@ is currently the newest version available." = "%1$@ %2$@ は現在入手できる最新バージョンです。"; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ が入手できます(使用中のバージョンは %3$@ です)。今すぐダウンロードしますか?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ が入手できます(使用中のバージョンは %3$@ です)。このアップデートの詳しい情報を Web で確認しますか?"; "%@ downloaded" = "%@ ダウンロード済み"; "%@ of %@" = "%1$@ / %2$@"; "A new version of %@ is available!" = "新しいバージョンの %@ が入手できます!"; "A new version of %@ is ready to install!" = "新しいバージョンの %@ が今すぐインストールできます!"; "An error occurred in retrieving update information. Please try again later." = "アップデート情報の取得中にエラーが発生しました。あとでやり直してください。"; "An error occurred while downloading the update. Please try again later." = "アップデートをダウンロード中にエラーが発生しました。あとでやり直してください。"; "An error occurred while extracting the archive. Please try again later." = "アーカイブの展開中にエラーが発生しました。あとでやり直してください。"; "An error occurred while installing the update. Please try again later." = "アップデートをインストール中にエラーが発生しました。あとでやり直してください。"; "An error occurred while parsing the update feed." = "アップデートフィードを解析中にエラーが発生しました。"; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "%1$@ を再起動中にエラーが発生しましたが、次回 %1$@ の実行時に新しいバージョンが利用できます。"; "An important update to %@ is ready to install" = "%@ の重要なアップデートがインストールできます。"; /* the unit for bytes */ "B" = "バイト"; "Cancel" = "キャンセル"; "Cancel Update" = "アップデートを中止"; "Checking for updates..." = "アップデートを確認しています…"; /* Take care not to overflow the status window. */ "Downloading update..." = "アップデートをダウンロードしています…"; /* Take care not to overflow the status window. */ "Extracting update..." = "アップデートを展開しています…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "インストールして再起動"; /* Take care not to overflow the status window. */ "Installing update..." = "アップデートをインストールしています…"; /* the unit for kilobytes */ "KB" = "KB"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "詳しい情報…"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "OK"; /* Status message on progress window once download has finished. */ "Ready to Install" = "インストールできます"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = " %1$@ のアップデートを自動で確認しますか? アップデートは %1$@ メニューから手動でいつでも確認することができます。"; "The update is improperly signed." = "このアップデートは無効な署名が含まれています。"; "Update Error!" = "アップデートエラー!"; "Updating %@" = "%@ をアップデート中"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "すでに最新バージョンの %@ を使用しています。"; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "最新版を使用しています!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ko.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ko.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ko.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey 익명으로 보내지는 시스템 정보로 차후 프로그램 개발에 도움이 될 수 있습니다. 질문이 있으시면 연락 주십시오. 아래 정보가 전송될 것입니다. SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ko.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@이(가) 다운로드 되었습니다. 프로그램을 업데이트하고 재실행 하시겠습니까?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@이(가) 디스크 이미지나 CD 드라이브 같은 읽기 전용 볼륨에서 실행되고 있으므로 업데이트 할 수 없습니다. %1$@을(를) 응용프로그램 폴더로 이동하여 다시 실행해 주십시오."; "%@ %@ is currently the newest version available." = "%1$@ %2$@이(가) 현재 최신 버전입니다."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@이(가) 업데이트 되었습니다. (현재 버전 : %3$@) 다운로드 하시겠습니까?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ 다운로드 완료"; "%@ of %@" = "%1$@ / %2$@"; "A new version of %@ is available!" = "%@ 새 버전이 있습니다."; "A new version of %@ is ready to install!" = "%@ 새 버전을 설치할 준비가 되었습니다."; "An error occurred in retrieving update information. Please try again later." = "업데이트 정보를 수집하는 중 오류가 발생하였습니다. 나중에 다시 시도해 주십시오."; "An error occurred while downloading the update. Please try again later." = "업데이트를 다운로드 하는 중 오류가 발생하였습니다. 나중에 다시 시도해 주십시오."; "An error occurred while extracting the archive. Please try again later." = "압축 파일을 푸는 중 오류가 발생하였습니다. 나중에 다시 시도해 주십시오."; "An error occurred while installing the update. Please try again later." = "업데이트를 설치하는 중 오류가 발생하였습니다. 나중에 다시 시도해 주십시오."; "An error occurred while parsing the update feed." = "업데이트 피드를 분석하는 중 오류가 발생하였습니다."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "%1$@을(를) 재실행하는 중 오류가 발생하였습니다. 업데이트는 완료되었으므로 다음 실행시에는 새 버전으로 실행됩니다."; /* the unit for bytes */ "B" = "B"; "Cancel" = "취소"; "Cancel Update" = "업데이트 취소"; "Checking for updates..." = "업데이트 확인 중…"; /* Take care not to overflow the status window. */ "Downloading update..." = "다운로드 중…"; /* Take care not to overflow the status window. */ "Extracting update..." = "압축 푸는 중…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "설치 & 재실행"; /* Take care not to overflow the status window. */ "Installing update..." = "설치 중…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "확인"; "Ready to Install" = "설치 준비 완료"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "%1$@ 업데이트 확인을 자동으로 할까요? %1$@ 메뉴에서 수동 설정을 할 수 있습니다."; "Update Error!" = "업데이트 오류!"; "Updating %@" = "%@ 업데이트 중"; "You already have the newest version of %@." = "이미 %@ 최신 버전입니다."; "You're up-to-date!" = "최신 버전입니다."; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nb.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nb.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nb.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Den anonyme systemprofilen hjelper oss med å planlegge fremtidig utviklingsarbeid. Ta gjerne kontakt med oss hvis du har spørsmål om dette.
 Følgende innhold vil bli sendt: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nb.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ er lastet ned og er klar til bruk! Dette er en viktig oppdatering; ønsker du å installere og restarte %1$@ nå?"; "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ er lastet ned og er klar til bruk! Ønsker du å installere og restarte %1$@ nå?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ kan ikke oppdateres fra en 'bare lesbar' enhet som f.eks. en cd. Flytt %1$@ til Programmer-katalogen, start på ny og prøv igjen."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ er nyeste versjon."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ er nå tilgjengelig—du har %3$@. Ønsker du å laste ned og installere nå?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ lastet ned"; "%@ of %@" = "%1$@ av %2$@"; "A new version of %@ is available!" = "En ny versjon av %@ er tilgjengelig!"; "A new version of %@ is ready to install!" = "En ny versjon av %@ er klar for installering!"; "An error occurred in retrieving update information. Please try again later." = "En feil oppstod ved henting av oppdateringsinformasjon. Vennligst prøv igjen senere."; "An error occurred while downloading the update. Please try again later." = "En feil oppstod under nedlasting av oppdateringen. Vennligst prøv igjen senere."; "An error occurred while extracting the archive. Please try again later." = "En feil oppstod under utpakking av oppdateringen. Vennligst prøv igjen senere."; "An error occurred while installing the update. Please try again later." = "En feil oppstod under installering av opddateringen. Vennligst prøv igjen senere."; "An error occurred while parsing the update feed." = "En feil oppstod under lesing av oppdateringsstrømmen."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "En feil oppstod under omstart av %1$@, men den nyeste versjonen vil være tilgjengelig neste gang du starter %1$@."; "An important update to %@ is ready to install" = "En viktig oppdatering for %@ er klar til å installeres"; /* the unit for bytes */ "B" = "B"; "Cancel" = "Avbryt"; "Cancel Update" = "Avbryt oppdateringen"; "Checking for updates..." = "Søker etter oppdateringer…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Laster ned oppdateringen…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Pakker ut oppdateringen…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Installer og start på ny"; /* Take care not to overflow the status window. */ "Installing update..." = "Installerer oppdateringen…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "OK"; /* Status message on progress window once download has finished. */ "Ready to Install" = "Klar til å installere"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Skal %1$@ søke automatisk etter oppdateringer? Du kan når som helst søke manuelt fra %1$@-menyen."; "Update Error!" = "Feil ved oppdateringen!"; "Updating %@" = "Oppdaterer %@"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "Du har allerede nyeste versjon av %@."; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "Ingen nye oppdateringer"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Mer info…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nl.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nl.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nl.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Aan de hand van anonieme informatie over het systeemprofiel kunnen wij toekomstige ontwikkelingswerkzaamheden beter plannen. Neem contact met ons op als u hierover vragen hebt. Dit is de informatie die wordt verzonden: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/nl.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ is gedownload en is klaar voor gebruik! Wilt u %1$@ nu installeren en herstarten?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ kan niet worden geupdate als het van een alleen-lezen volume, zoals een schijfkopie of CD, geopend is. Verplaats %1$@ naar de Map ’Programma’s’, herstart van daar, en probeer opnieuw."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ is momenteel de recentste versie."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ is nu beschikbaar—u heeft %3$@. Wilt u het nu downloaden?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ gedownload"; "%@ of %@" = "%1$@ van %2$@"; "A new version of %@ is available!" = "Een nieuwe versie van %@ is beschikbaar!"; "A new version of %@ is ready to install!" = "Een nieuwe versie van %@ is klaar om te installeren!"; "An error occurred in retrieving update information. Please try again later." = "Bij het ophalen van update-informatie deed zich een fout voor. Probeer alstublieft later opnieuw."; "An error occurred while downloading the update. Please try again later." = "Bij het downloaden deed zich een fout voor. Probeer alstublieft later opnieuw."; "An error occurred while extracting the archive. Please try again later." = "Bij het uitpakken van het archief deed zich een fout voor. Probeer alstublieft later opnieuw."; "An error occurred while installing the update. Please try again later." = "Bij het installeren van de update deed zich een fout voor. Probeer alstublieft later opnieuw."; "An error occurred while parsing the update feed." = "Bij het afhandelen van de update feed deed zich een fout voor."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Bij het herstarten van %1$@ deed zich een fout voor. Maar de nieuwe versie is beschikbaar de volgende keer u %1$@ opstart."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Annuleren"; "Cancel Update" = "Updaten annuleren"; "Checking for updates..." = "Controleren op updates …"; /* Take care not to overflow the status window. */ "Downloading update..." = "Downloaden update …"; /* Take care not to overflow the status window. */ "Extracting update..." = "Bezig met uitpakken van de update …"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Installeer en herstart"; /* Take care not to overflow the status window. */ "Installing update..." = "Update installeren …"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "OK"; /* Status message on progress window once download has finished. */ "Ready to Install" = "Klaar om te installeren"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Zou %1$@ automatisch op updates moeten controleren? U kunt altijd nog handmatig controleren op updates via het %1$@ menu."; "Update Error!" = "Update-fout!"; "Updating %@" = "%@ updaten"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "U heeft al de nieuwste versie van %@."; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "U heeft de nieuwste versie!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Meer informatie …"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/no.lproj/SUAutomaticUpdateAlert.strings ================================================ /* NSWindow () : (oid:5) */ "" = ""; /* NSButton (Automatically download and install updates in the future) : (oid:17) */ "Automatically download and install updates in the future" = "Last ned og installer oppdateringer automatisk i fremtiden"; /* NSButton (Relaunch Later) : (oid:16) */ "Relaunch Later" = "Omstart senere"; /* NSButton (Relaunch Now) : (oid:15) */ "Relaunch Now" = "Omstart nå"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/no.lproj/SUUpdateAlert.strings ================================================ /* NSButton (Automatically download and install updates in the future) : (oid:92) */ "Automatically download and install updates in the future" = "Automatisk nedlastning og installering i fremtiden"; /* NSButton (Install Update) : (oid:76) */ "Install Update" = "Installer oppdatering"; /* NSTextField (Release Notes:) : (oid:17) */ "Release Notes:" = "Vedrørende utgivelsen:"; /* NSButton (Remind Me Later) : (oid:22) */ "Remind Me Later" = "Påminn meg senere"; /* NSButton (Skip This Version) : (oid:23) */ "Skip This Version" = "Hopp over denne versjonen"; /* NSWindow (Software Update) : (oid:5) */ "Software Update" = "Programvareoppdatering"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/no.lproj/Sparkle.strings ================================================ "%@ of %@" = "%1$@ of %2$@"; "%@ %@ has been installed and will be ready to use next time %@ starts! Would you like to relaunch now?" = "%1$@ %2$@ har blitt installert og er klar til bruk neste gang %3$@ starter opp! Vil du starte på nytt nå?"; "%@ %@ is currently the newest version available." = "%1$@ %2$@ er den nyeste tilgjengelige versjonen."; "%@ %@ is now available (you have %@). Would you like to download it now?" = "%1$@ %2$@ er nå tilgjengelig (du har %3$@). Ønsker du å laste den ned nå?"; "%@ does not have permission to write to the application's directory! Are you running off a disk image? If not, ask your system administrator for help." = "%@ har ikke tilgangsrettigheter til å skrive til programmets filbane! Starter du fra en diskfil? Hvis ikke, spør din systemadministrator om hjelp."; "A new version of %@ has been installed!" = "En ny versjon av %@ har blitt installert!"; "A new version of %@ is available!" = "En ny versjon av %@ er tilgjengelig!"; "An error occurred during installation. Please try again later." = "Det skjedde en feil under installasjonen. Vennligst prøv igjen senere."; "An error occurred in retrieving update information. Please try again later." = "Det skjedde en feil ved henting av oppdateringsinformasjon. Vennligst prøv igjen senere."; "An error occurred while extracting the archive. Please try again later." = "Det skjedde en feil ved utpakking av filarkivet. Vennligst prøv igjen senere."; "An error occurred while trying to download the file. Please try again later." = "Det skjedde en feil under forsøket på å laste ned filen. Vennligst prøv igjen senere."; "An update is already in progress!" = "Oppdateringen er allerede igang!"; "Cancel" = "Avbryt"; "Check for updates on startup?" = "Vil du se etter oppdateringer ved oppstart?"; "Downloading update..." = "Laster ned oppdatering..."; "Extracting update..." = "Pakker ut oppdatering..."; "Install and Relaunch" = "Installer og start på nytt"; "Installing update..." = "Installerer oppdatering..."; "No" = "Nei"; "OK" = "OK"; "Ready to Install" = "Klar til å installere"; "Update Error!" = "Oppdateringsfeil!"; "Updating %@" = "Oppdaterer %@"; "Would you like %@ to check for updates on startup? If not, you can initiate the check manually from the application menu." = "Ønsker du at %@ skal se etter oppdateringer under oppstart? Hvis ikke kan du sette igang sjekkingen manuelt ifra programmenyen."; "Yes" = "Ja"; "You're up-to-date!" = "Du er oppdatert!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pl.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pl.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pl.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this. This is the information that would be sent: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pl.lproj/Sparkle.strings ================================================ /* "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?"; */ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ został pobrany i jest gotowy do użycia! Czy chcesz teraz zainstalować i ponownie uruchomić %1$@?"; /* "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again."; */ "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ nie może zostać uaktualniony gdy jest uruchomiony z woluminu tylko do odczytu jak obraz dysku lub napęd optyczny. Przenieś %1$@ do folderu Programy, uruchom z nowej lokacji i spróbuj ponownie."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ jest najnowszą dostępną wersją."; "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ jest już dostępny (aktualnie posiadasz %3$@). Czy chcesz go teraz pobrać?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ jest już dostępny (aktualnie posiadasz %3$@). Czy chcesz otworzyć stronę z informacjami o tym uaktualnieniu?"; "%@ downloaded" = "Pobrano %@"; "%@ of %@" = "%1$@ z %2$@"; "A new version of %@ is available!" = "Dostępna jest nowa wersja %@!"; "A new version of %@ is ready to install!" = "Nowa wersja %@ gotowa do zainstalowania!"; "An error occurred in retrieving update information. Please try again later." = "Błąd podczas pobierania informacji o uaktualnieniach. Spróbuj ponownie później."; "An error occurred while downloading the update. Please try again later." = "Błąd podczas pobierania uaktualnienia. Spróbuj ponownie później."; "An error occurred while extracting the archive. Please try again later." = "Błąd podczas rozpakowywania archiwum. Spróbuj ponownie później"; "An error occurred while installing the update. Please try again later." = "Błąd podczas instalowania uaktualnienia. Spróbuj ponownie później"; "An error occurred while parsing the update feed." = "Błąd podczas wczytywania danych o uaktualnieniu."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Błąd podczas ponownego uruchomienia %1$@, ale nowa wersja będzie dostępna przy ponownym ręcznym uruchomieniu %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Anuluj"; "Cancel Update" = "Anuluj uaktualnianie"; "Checking for updates..." = "Sprawdzam uaktualnienia…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Pobieram uaktualnienie…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Rozpakowuję uaktualnienie…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Zainstaluj i uruchom ponownie"; /* Take care not to overflow the status window. */ "Installing update..." = "Instalowanie uaktualnienia…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Gotowy do instalacji"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Czy %1$@ ma automatycznie sprawdzać uaktualnienia? Zawsze możesz ręcznie sprawdzać z menu %1$@."; "Update Error!" = "Błąd uaktualniania!"; "Updating %@" = "Uaktualniam %@"; "You already have the newest version of %@." = "Posiadasz najnowszą wersję %@."; "You're up-to-date!" = "Jesteś na bieżąco!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_BR.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_BR.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_BR.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey As informações anônimas do sistema são usadas para nos ajudar a planejar o desenvolvimento futuro do aplicativo. Contate-nos caso tenha dúvidas sobre este procedimento. As seguintes informações seriam enviadas: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_BR.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ foi transferido e está pronto para uso! Deseja instalar e reiniciar o %1$@ agora?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ não pode ser atualizado enquanto for executado a partir de um volume somente de leitura, como uma imagem de disco ou CD/DVD. Mova o %1$@ para a pasta Aplicativos, reinicie-o e tente novamente."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ é a versão mais recente disponível."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ está disponível – sua versão é %3$@. Deseja transferi-lo agora?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ está disponível – sua versão é %3$@. Deseja saber mais sobre esta atualização na web?"; "%@ downloaded" = "%@ transferidos"; "%@ of %@" = "%1$@ de %2$@"; "A new version of %@ is available!" = "Uma nova versão do %@ está disponível!"; "A new version of %@ is ready to install!" = "Uma nova versão do %@ está pronta para ser instalada!"; "An error occurred in retrieving update information. Please try again later." = "Ocorreu um erro ao obter informações da atualização. Tente novamente mais tarde."; "An error occurred while downloading the update. Please try again later." = "Ocorreu um erro ao transferir a atualização. Tente novamente mais tarde."; "An error occurred while extracting the archive. Please try again later." = "Ocorreu um erro ao extrair o arquivo comprimido. Tente novamente mais tarde."; "An error occurred while installing the update. Please try again later." = "Ocorreu um erro ao instalar a atualização. Tente novamente mais tarde."; "An error occurred while parsing the update feed." = "Ocorreu um erro ao analisar o feed de atualização."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Ocorreu um erro ao reabrir o %1$@. A nova versão estará disponível da próxima vez que você abrir o %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Cancelar"; "Cancel Update" = "Cancelar Atualização"; "Checking for updates..." = "Buscando atualizações…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Transferindo atualização…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Extraindo atualização…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Instalar e Reabrir"; /* Take care not to overflow the status window. */ "Installing update..." = "Instalando atualização…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Pronto para Instalar"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Deseja que o %1$@ busque atualizações automaticamente? Você pode buscar atualizações manualmente, através do menu %1$@."; "Update Error!" = "Erro de Atualização!"; "Updating %@" = "Atualizando %@"; "You already have the newest version of %@." = "Você já possui a versão mais recente do %@."; "You're up-to-date!" = "Você está atualizado!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_PT.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_PT.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_PT.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey A informação anónima do perfil de sistema é usada para no futuro nos ajudar a planear o trabalho de desenvolvimento. Por favor contacte-nos se tiver alguma questão acerca deste assunto. Esta é a informação que seria enviada: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/pt_PT.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "O %1$@ %2$@ foi transferido e está pronto a instalar! Gostaria de o fazer agora e reiniciar o %1$@ posteriormente?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "O %1$@ não pode ser actualizado quando estiver a ser executado a partir de um volume apenas de leitura como uma imagem de disco ou disco óptico. Mova o %1$@ para a sua pasta Aplicações, reinicie-o aí e tente novamente."; "%@ %@ is currently the newest version available." = "O %1$@ %2$@ é neste momento a versão mais recente disponível."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "O %1$@ %2$@ está agora disponível e tem a versão %3$@. Gostaria de o transferir agora?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ transferido"; "%@ of %@" = "%1$@ de %2$@"; "A new version of %@ is available!" = "Uma nova versão do %@ está dísponível!"; "A new version of %@ is ready to install!" = "Uma nova versão do %@ está pronta a instalar!"; "An error occurred in retrieving update information. Please try again later." = "Ocorreu um erro ao recolher informação sobre as actualizações disponíveis. Por favor tente novamente mais tarde."; "An error occurred while downloading the update. Please try again later." = "Ocorreu um erro ao transferir a actualização. Por favor novamente tente mais tarde."; "An error occurred while extracting the archive. Please try again later." = "Ocorreu um erro ao extrair o arquivo. Por favor novamente tente mais tarde."; "An error occurred while installing the update. Please try again later." = "Ocorreu um erro ao instalar a actualização. Por favor novamente tente mais tarde."; "An error occurred while parsing the update feed." = "Ocorrer um erro ao processar o feed de actualização."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Ocorreu um erro ao reiniciar o %1$@, mas a nova versão estará disponível na próxima vez que iniciar o %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Cancelar"; "Cancel Update" = "Cancelar actualização"; "Checking for updates..." = "A procurar actualizações…"; /* Take care not to overflow the status window. */ "Downloading update..." = "A transferir actualização…"; /* Take care not to overflow the status window. */ "Extracting update..." = "A extrair actualização…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Instalar e reiniciar"; /* Take care not to overflow the status window. */ "Installing update..." = "A instalar actualização…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Pronto para instalar"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Deverá o %1$@ procurar por actualizações automaticamente? Pode sempre procurar actualizações manualmente a partir do menu do %1$@."; "Update Error!" = "Erro na actualização!"; "Updating %@" = "A actualizar o %@"; "You already have the newest version of %@." = "Já tem a versão mais recente do %@."; "You're up-to-date!" = "Está actualizado!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ro.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ro.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ro.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this. This is the information that would be sent: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ro.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! This is an important update; would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ a fost descărcată și este gata de utilizare! Aceasta este o actualizare importantă; dorești să o instalezi și să relansați %1$@ acum?"; "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ a fost descărcată și este gata de utilizare! Dorești să o instalezi și să relansați %1$@ acum?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ nu poate fi actualizată atunci când a fost pornită de pe un volum read-only ca o imagine disc sau o unitate optică. Mută %1$@ în directorul Aplicații, repornește-o de acolo și încearcă din nou."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ este cea ultima versiune disponibilă."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ este disponibilă—tu ai %3$@. Dorești să o descărcarci acum?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ descărcat"; "%@ of %@" = "%1$@ din %2$@"; "A new version of %@ is available!" = "O nouă versiune pentru %@ este disponibilă!"; "A new version of %@ is ready to install!" = "O nouă versiune pentru %@ este gata de instalare!"; "An error occurred in retrieving update information. Please try again later." = "A apărut o eroare în timpul preluari informaţiilor pentru actualizare. Te rog încercă din nou mai târziu."; "An error occurred while downloading the update. Please try again later." = "A apărut o eroare în timp ce se descărca actualizărea. Te rog încercă din nou mai târziu."; "An error occurred while extracting the archive. Please try again later." = "A apărut o eroare în timpul dezarhivării. Te rog încercă din nou mai târziu."; "An error occurred while installing the update. Please try again later." = "A apărut o eroare în timpul instalări actualizări. Te rog încercă din nou mai târziu."; "An error occurred while parsing the update feed." = "A apărut o eroare în timpul citiri feed-ului de actualizare."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "A apărut o eroare în timpul relansării aplicației %1$@, dar noua versiune va fii disponibilă data viitoare când vei reporni %1$@."; "An important update to %@ is ready to install" = "O actualizare importantă pentru %@ este gata pentru a fi instalată"; /* the unit for bytes */ "B" = "B"; "Cancel" = "Anulează"; "Cancel Update" = "Anulează actualizarea"; "Checking for updates..." = "Verifică de actualizări…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Descarcă actualizarea…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Dezarhivează actualizarea…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Instalează și Redeschide"; /* Take care not to overflow the status window. */ "Installing update..." = "Instalează actualizarea…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "OK"; /* Status message on progress window once download has finished. */ "Ready to Install" = "Pregatit pentru a instala"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "%1$@ ar trebui ca să caute în mod automat pentru actualizări? Puteți verifica mereu pentru actualizări din meniul %1$@."; "Update Error!" = "Eroare Actualizare!"; "Updating %@" = "Actualizează %@"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "Ai cea mai nouă versiune de %@."; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "Ai ultima versiune!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Află mai multe…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ru.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ru.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ru.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Использование анонимного профиля системы помогает нам в планировании будущей работы по разработке. Если у вас есть какие-либо вопросы по этой теме, обращайтесь к нам. Это информация, предназначенная для отправления: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/ru.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ загружен и готов к использованию! Хотите установить и перезапустить %1$@?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Во время работы %1$@ с тома, предназначенного только для чтения, как например, образа диска или накопителя на оптических дисках, его невозможно обновить. Переместите %1$@ в Папку приложений, перезапустите его оттуда и повторите попытку."; "%@ %@ is currently the newest version available." = "В настоящий момент %1$@ %2$@ является новейшей версией."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ теперь доступен – вы имеете %3$@. Хотите загрузить его сейчас?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ загружено"; "%@ of %@" = "%1$@ из %2$@"; "A new version of %@ is available!" = "Имеется новая версия %@!"; "A new version of %@ is ready to install!" = "Новая версия %@ готова к установке!"; "An error occurred in retrieving update information. Please try again later." = "Произошла ошибка при извлечении информации обновления. Повторите попытку позже."; "An error occurred while downloading the update. Please try again later." = "Произошла ошибка при загрузке обновления. Повторите попытку позже."; "An error occurred while extracting the archive. Please try again later." = "Произошла ошибка при извлечении архива. Повторите попытку позже."; "An error occurred while installing the update. Please try again later." = "Произошла ошибка при установке обновления. Повторите попытку позже."; "An error occurred while parsing the update feed." = "Ошибка произошла при извлечении канала обновления."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Произошла ошибка при перезапуске %1$@, но при последующем запуске %1$@ будет доступна новая версия."; /* the unit for bytes */ "B" = "Б"; "Cancel" = "Отменить"; "Cancel Update" = "Отменить обновление"; "Checking for updates..." = "Проверяю наличие обновлений…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Загружаю обновление…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Извлекаю обновление…"; /* the unit for gigabytes */ "GB" = "ГБ"; "Install and Relaunch" = "Установить и перезапустить"; /* Take care not to overflow the status window. */ "Installing update..." = "Устанавливаю обновление…"; /* the unit for kilobytes */ "KB" = "КБ"; /* the unit for megabytes */ "MB" = "МБ"; "OK" = "OK"; "Ready to Install" = "Готов к установке"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Должен ли %1$@ выполнять автоматическую проверку обновлений? Вы всегда можете выполнять проверку обновлений вручную в меню %1$@."; "Update Error!" = "Ошибка при обновлении!"; "Updating %@" = "Обновляю %@"; "You already have the newest version of %@." = "Вы уже имеете самую новую версию %@."; "You're up-to-date!" = "У вас все обновлено!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Узнать больше…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sk.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sk.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sk.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonymný profil systému nám umožní zlepšiť plánovanie budúceho vývoja aplikácie. Ak máte ohľadom tohto akékoľvek otázky, neváhajte a kontaktujte nás. Odosielané budú nasledujúce informácie: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sk.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "Aplikácia %1$@ %2$@ bola prevzatá a je pripravená na použitie! Chcete teraz nainštalovať a následne znovu spustiť %1$@?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Aplikáciu %1$@ nemožno aktualizovať, ak je spustená zo zväzku s právami len na čítanie (napríklad z obrazu disku alebo optického disku). Presuňte aplikáciu %1$@ do priečinka Applications, spustite ju odtiaľ a potom znova skúste aktualizáciu."; "%@ %@ is currently the newest version available." = "%1$@ %2$@\nje najnovšia dostupná verzia."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "Je dostupná aplikácia %1$@ %2$@ — máte %3$@. Chcete ju prevziať teraz?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ prevzaté"; "%@ of %@" = "%1$@ z %2$@"; "A new version of %@ is available!" = "Je dostupná nová verzia aplikácie %@!"; "A new version of %@ is ready to install!" = "Nová verzia aplikácie %@ je pripravená na inštaláciu!"; "An error occurred in retrieving update information. Please try again later." = "Pri získavaní informácie o aktualizácii sa vyskytla chyba. Skúste neskôr."; "An error occurred while downloading the update. Please try again later." = "Pri preberaní aktualizácie sa vyskytla chyba. Skúste neskôr."; "An error occurred while extracting the archive. Please try again later." = "Pri rozbaľovaní archívu sa vyskytla chyba. Skúste neskôr."; "An error occurred while installing the update. Please try again later." = "Pri inštalácii aktualizácie sa vyskytla chyba. Skúste neskôr."; "An error occurred while parsing the update feed." = "Pri analyzovaní aktualizácie sa vyskytla chyba."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Pri spustení %1$@ sa vyskytla chyba, avšak nová verzia bude dostupná pri opätovnom štarte %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Zrušiť"; "Cancel Update" = "Zrušiť aktualizáciu"; "Checking for updates..." = "Kontrolujú sa aktualizácie…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Preberá sa aktualizácia…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Rozbaľuje sa aktualizácia…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Inštalovať a znovu spustiť"; /* Take care not to overflow the status window. */ "Installing update..." = "Inštaluje sa aktualizácia…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Pripravené na inštaláciu"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Môže %1$@ automaticky kontrolovať aktualizácie? Kedykoľvek ich môžete z ponuky %1$@ skontrolovať aj manuálne."; "Update Error!" = "Chyba aktualizácie!"; "Updating %@" = "Aktualizuje sa %@"; "You already have the newest version of %@." = "Máte aktuálnu verziu aplikácie %@."; "You're up-to-date!" = "Máte aktuálnu verziu!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sl.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sl.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sl.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Anonimni profil sistema se uporablja za načrtovanje nadaljnega razvoja programa. V primeru vprašanj nas lahko kontaktirate. Pošljejo se sledeče informacije: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sl.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ je bil uspešno prenešen s spleta in je pripravljen na namestitev. Ga želite namestiti in ponovno zagnati takoj?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Programa %1$@ ni mogoče posodobiti, ker ga poganjate iz lokacije, kamor pisanje ni dovoljeno (pogosto je to slika diska dmg ali optična enota). Poskusite %1$@ premakniti v direktorij z aplikacijami (Applications), ga ponovno zagnati in šele nato posodobiti."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ je najnovejša verzija programa."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "Na voljo je %1$@ %2$@ — vi imate %3$@. Ga želite prenesti s spleta sedaj?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "prenešenih je %@"; "%@ of %@" = "%1$@ od %2$@"; "A new version of %@ is available!" = "Na voljo je nova verzija programa %@."; "A new version of %@ is ready to install!" = "Najnovejša verzija programa %@ je že nameščena."; "An error occurred in retrieving update information. Please try again later." = "Med iskanjem posodobitev je prišlo do napake. Prosimo poskusite ponovno čez nekaj časa."; "An error occurred while downloading the update. Please try again later." = "Med prenašanje posodobitve s spleta je prišlo do napake. Prosimo poskusite ponovno čez nekaj časa."; "An error occurred while extracting the archive. Please try again later." = "Med odpiranjem arhiva je prišlo do napake. Prosimo poskusite ponovno čez nekaj časa."; "An error occurred while installing the update. Please try again later." = "Med nameščanjem posodobitve je prišlo do napake. Prosimo poskusite ponovno čez nekaj časa."; "An error occurred while parsing the update feed." = "Napaka pri interpretaciji RSS vira s posodobitvami."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Med ponovnim zagonom programa %1$@ je sicer prišlo do napake, vendar bo nova verzija na voljo, ko boste naslednjič pognali program."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Prekliči"; "Cancel Update" = "Prekliči posodabljanje"; "Checking for updates..." = "Iskanje posodobitev …"; /* Take care not to overflow the status window. */ "Downloading update..." = "Prenašanje posodobitve …"; /* Take care not to overflow the status window. */ "Extracting update..." = "Razpakiranje posodobitve …"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Namesti in ponovno zaženi"; /* Take care not to overflow the status window. */ "Installing update..." = "Nameščanje posodobitve …"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "V redu"; "Ready to Install" = "Pripravljen na namestitev"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Naj %1$@ samodejno preverja, če so na voljo posodobitve? To lahko kadarkoli preverite tudi sami iz menija za %1$@."; "Update Error!" = "Napaka pri posodabljanju"; "Updating %@" = "Posodabljam %@"; "You already have the newest version of %@." = "Najnovejša verzija programa %@ je že nameščena."; "You're up-to-date!" = "Uporabljate zadnjo verzijo."; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sv.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sv.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sv.lproj/SUUpdatePermissionPrompt.xib ================================================ Leta efter uppdateringar automatiskt? visibleKey visibleValue displayValue displayKey Anonym systemprofilinformation används för att hjälpa oss att planera framtida utvecklingsarbete. Vänligen kontakta oss ifall du har några frågot om detta. Detta är informationen som skulle sändas: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/sv.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ har hämtats och är klar att använda! Vill du installera det och starta om %1$@ nu?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ kan inte uppdateras när det körs från en skrivskyddad volym som en skivavbild eller en optisk enhet. Flytta %1$@ till mappen Program, starta om det därifrån, och försök igen."; "%@ %@ is currently the newest version available." = "%1$@ %2$@ är för närvarande den senaste tillgängliga versionen."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ finns nu tillgänglig—du har %3$@. Vill hämta uppdateringen nu?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ hämtat"; "%@ of %@" = "%1$@ av %2$@"; "A new version of %@ is available!" = "En ny version av %@ finns tillgänglig!"; "A new version of %@ is ready to install!" = "En ny version av %@ är redo att installeras!"; "An error occurred in retrieving update information. Please try again later." = "Ett fel uppstod vid hämtning av information om uppdateringar. Försök igen senare."; "An error occurred while downloading the update. Please try again later." = "Ett fel uppstod vid hämtning av uppdateringen. Försök igen senare."; "An error occurred while extracting the archive. Please try again later." = "Ett fel uppstod vid extrahering av arkivet. Försök igen senare."; "An error occurred while installing the update. Please try again later." = "Ett fel uppstod vid installationen av uppdateringen. Försök igen senare."; "An error occurred while parsing the update feed." = "Ett fel uppstod vid tolkning av uppdateringslänken."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Ett fel uppstod när %1$@ skulle startas om, men den nya versionen kommer att finnas tillgänglig nästa gång du kör %1$@."; /* the unit for bytes */ "B" = "B"; "Cancel" = "Avbryt"; "Cancel Update" = "Avbryt uppdatering"; "Checking for updates..." = "Letar efter uppdateringar…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Hämtar uppdatering…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Extraherar uppdatering…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "Installera och starta om"; /* Take care not to overflow the status window. */ "Installing update..." = "Installerar uppdatering…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "OK"; "Ready to Install" = "Redo att installera"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Ska %1$@ leta efter uppdateringar automatiskt? Du kan alltid leta efter uppdateringar manuellt från %1$@-menyn."; "Update Error!" = "Uppdateringsfel!"; "Updating %@" = "Uppdaterar %@"; "You already have the newest version of %@." = "Du har redan den senaste versionen av %@."; "You're up-to-date!" = "Du är uppdaterad!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Mer info…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/th.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/th.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/th.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey ข้อมูลระบบแบบนิรนามช่วยในการวางแผนพัฒนาแอปพลิเคชันของเราในอนาคต กรุณาติดต่อเราถ้าคุณมีข้อสงสัยในเรื่องนี้ นี่คือข้อมูลที่จะถูกส่งไป: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/th.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ ได้ถูกดาวน์โหลดและพร้อมใช้งานแล้ว คุณต้องการติดตั้งและเริ่ม %1$@ ใหม่เดี๋ยวนี้หรือไม่"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ ไม่สามารถรับการอัพเดทได้เมื่อถูกเรียกจากดิสก์แบบอ่านอย่างเดียวหรือซีดีรอม ย้าย %1$@ ไปยังโฟลเดอร์แอปพลิเคชัน และเรียกใช้งาน จากนั้นลองใหม่อีกครั้ง"; "%@ %@ is currently the newest version available." = "%1$@ %2$@ เป็นเวอร์ชั่นใหม่ล่าสุดแล้ว"; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ พร้อมให้ดาวน์โหลดแล้ว (คุณมีเวอร์ชั่น %3$@) ต้องการดาวน์โหลดเลยหรือไม่"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "ดาวน์โหลด %@ เสร็จสิ้น"; "%@ of %@" = "%1$@ จาก %2$@"; "A new version of %@ is available!" = "%@ มีเวอร์ชั่นใหม่!"; "A new version of %@ is ready to install!" = "เวอร์ชั่นใหม่ของ %@ พร้อมสำหรับการติดตั้งแล้ว"; "An error occurred in retrieving update information. Please try again later." = "เกิดข้อผิดพลาดระหว่างการรับข้อมูลอัพเดท กรุณาลองใหม่ในภายหลัง"; "An error occurred while downloading the update. Please try again later." = "เกิดข้อผิดพลาดระหว่างพยายามดาวน์โหลดอัพเดท กรุณาลองใหม่ในภายหลัง"; "An error occurred while extracting the archive. Please try again later." = "เกิดข้อผิดพลาดระหว่างการแตกไฟล์ที่ถูกบีบอัด กรุณาลองใหม่ในภายหลัง"; "An error occurred while installing the update. Please try again later." = "เกิดข้อผิดพลาดระหว่างการติดตั้งอัพเดท กรุณาลองใหม่ในภายหลัง"; "An error occurred while parsing the update feed." = "เกิดข้อผิดพลาดระหว่างการประมวลผลฟีดอัพเดท กรุณาลองใหม่ในภายหลัง"; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "เกิดข้อผิดพลาดระหว่างการเริ่ม %1$@ ใหม่ อย่างไรก็ตามเวอร์ชั่นใหม่จะใช้ได้เมื่อคุณเรียก %1$@ ครั้งถัดไป"; /* the unit for bytes */ "B" = "B"; "Cancel" = "ยกเลิก"; "Cancel Update" = "ยกเลิกอัพเดท"; "Checking for updates..." = "ตรวจสอบอัพเดท…"; /* Take care not to overflow the status window. */ "Downloading update..." = "กำลังดาวน์โหลดอัพเดท…"; /* Take care not to overflow the status window. */ "Extracting update..." = "กำลังแตกไฟล์อัพเดท…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "ติดตั้งและเริ่มแอปพลิเคชันใหม่"; /* Take care not to overflow the status window. */ "Installing update..." = "กำลังติดตั้งอัพเดท…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; /* OK button. */ "OK" = "ตกลง"; /* Status message on progress window once download has finished. */ "Ready to Install" = "พร้อมติดตั้ง"; /* Message that is optionally shown at startup to allow users to turn on/off update checks. */ "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "คุณต้องการให้ %1$@ ตรวจสอบอัพเดทโดยอัตโนมัติหรือไม่ คุณสามารถเริ่มการตรวจสอบอัพเดทด้วยตนเองได้ทุกเมื่อจากเมนูของ %1$@"; "Update Error!" = "อัพเดทผิดพลาด!"; "Updating %@" = "กำลังอัพเดท %@"; /* 'Error' message when the user checks for updates but is already current or the feed doesn't contain any updates. (not necessarily shown in UI) */ "You already have the newest version of %@." = "คุณมีเวอร์ชั่นล่าสุดของ %@ แล้ว"; /* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */ "You're up-to-date!" = "คุณมีเวอร์ชั่นล่าสุดแล้ว!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "เรียนรู้เพิ่มเติม…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/tr.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/tr.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/tr.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Gönderdiğiniz anonim sistem bilgileri bu yazılımın geliştirilmesi için kullanılacaktır. Konu ile ilgili ayrıntılı bilgi için lütfen bizimle bağlantıya geçiniz. Göndereceğiniz Bilgiler: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/tr.lproj/Sparkle.strings ================================================ /* de_DE v0.1 - No comment provided by engineer. */ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ indirildi ve kullanıma hazır! Şimdi yüklemek istiyor musunuz? Uygulama yeniden başlatılacaktır."; /* de_DE v0.1 - No comment provided by engineer. */ "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "%1$@ uygulaması disk kalıbı içerisindeyken güncellenemez. Lütfen %1$@ uygulamasını Uygulamalar dizinine kopyalayıp yeniden başlatınız."; /* de_DE v0.1 - No comment provided by engineer. */ "%@ %@ is currently the newest version available." = "%1$@ %2$@ mevcut en yeni sürümdür."; /* de_DE v0.1 - No comment provided by engineer. */ /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ çıktı (Kullandığınız Sürüm: %3$@). Şimdi yeni sürümü indirmek ister misiniz?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ indirildi"; /* de_DE v0.1 - No comment provided by engineer. */ "%@ of %@" = "%1$@ / %2$@"; /* de_DE v0.1 - No comment provided by engineer. */ "A new version of %@ is available!" = "%@ uygulaması için yeni bir sürüm var!"; /* de_DE v0.1 - No comment provided by engineer. */ "A new version of %@ is ready to install!" = "%@ uygulamasının yeni bir sürümü yüklemeye hazır!"; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred in retrieving update information. Please try again later." = "Güncelleme bilgilerini alırken bir hata oluştu. Lütfen daha sonra tekrar deneyiniz."; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred while downloading the update. Please try again later." = "Güncelleme indirilirken bir hata oluştu. Lütfen sonra tekrar deneyiniz."; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred while extracting the archive. Please try again later." = "İndirilen arşivi açarken bir hata oluştu. Lütfen daha sonra tekrar deneyiniz."; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred while installing the update. Please try again later." = "Güncellemeyi yüklerken bir hata oluştu. Lütfen daha sonra tekrar deneyiniz."; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred while parsing the update feed." = "Güncelleme kaynağını incelerken bir hata oluştu."; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred while parsing the update feed: %@" = "Güncelleme kaynağını incelerken bir hata oluştu: %@"; /* de_DE v0.1 - No comment provided by engineer. */ "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "%1$@ uygulamasını açarken bir hata oluştu; ancak güncelleme %1$@ uygulamasını yeniden başlattığınızda hazır olacaktır."; /* de_DE v0.1 - the unit for bytes */ "B" = "B"; /* de_DE v0.1 - No comment provided by engineer. */ "Cancel" = "Vazgeç"; /* de_DE v0.1 - No comment provided by engineer. */ "Cancel Update" = "Güncellemeyi iptal et"; /* de_DE v0.1 - No comment provided by engineer. */ "Checking for updates..." = "Güncellemelere bakılıyor..."; /* de_DE v0.1 - No comment provided by engineer. */ "Downloading update..." = "Güncelleme indiriliyor..."; /* de_DE v0.1 - No comment provided by engineer. */ "Extracting update..." = "Güncelleme ayıklanıyor..."; /* de_DE v0.1 - the unit for gigabytes */ "GB" = "GB"; /* de_DE v0.1 - No comment provided by engineer. */ "Install and Relaunch" = "Kur ve yeniden başlat"; "Installing update..." = "Güncelleme kuruluyor..."; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "Tamam"; "Ready to Install" = "Kuruluma hazır"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "%1$@ uygulaması için güncellemeler otomatik olarak aransın mı? Güncelleme işlemini el ile uygulama menüsünden de başlatabilirsiniz."; "Update Error!" = "Güncelleme hatası."; "Updating %@" = "%@ güncelleniyor"; "You already have the newest version of %@." = "%@ uygulamasının en güncel sürümü sizde mevcut."; "You're up-to-date!" = "Uygulama güncel!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/uk.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/uk.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/uk.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey Використання анонімного профілю системи допомагає нам у планування майбутньої розробки. Якщо у вас виникли питання щодо цього, звертайтесь до нас. Інформація, що буде надіслано: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/uk.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ завантажений і готовий до використання! Бажаєте встановити і перезавантажити %1$@?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "Під час роботи з %1$@ з тому, що призначений лише для читання, наприклад, образу диска чи оптичного диску, його неможливо оновити. Перемістіть %1$@ у папку з програмами, перезавантажте його звідти і спробуйте ще раз."; "%@ %@ is currently the newest version available." = "У данний момент %1$@ %2$@ є останньою версією."; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ доступна – ви маєте %3$@. Бажаєте завантажити її зараз?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ завантажено"; "%@ of %@" = "%1$@ із %2$@"; "A new version of %@ is available!" = "Доступна нова версія %@!"; "A new version of %@ is ready to install!" = "Нова версія %@ готова до встановлення!"; "An error occurred in retrieving update information. Please try again later." = "Виникла помилка при отриманні інформації про оновлення. Спробуйте ще раз пізніше."; "An error occurred while downloading the update. Please try again later." = "Виникла помилка при завантаження оновлення. Спробуйте ще раз пізніше."; "An error occurred while extracting the archive. Please try again later." = "Виникла помилка при розпаковуванні архіву. Спробуйте ще раз пізніше."; "An error occurred while installing the update. Please try again later." = "Виникла помилка при встановленні оновлення. Спробуйте ще раз пізніше."; "An error occurred while parsing the update feed." = "Виникла помилка розбору каналу оновлень."; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "Виникла помилка при перезавантаженні %1$@, але наступного разу при завантаження %1$@ буде доступна нова версія."; /* the unit for bytes */ "B" = "Б"; "Cancel" = "Відмінити"; "Cancel Update" = "Відмінити оновлення"; "Checking for updates..." = "Перевіряю наявність оновлень…"; /* Take care not to overflow the status window. */ "Downloading update..." = "Завантажую оновлення…"; /* Take care not to overflow the status window. */ "Extracting update..." = "Розпаковую оновлення…"; /* the unit for gigabytes */ "GB" = "ГБ"; "Install and Relaunch" = "Встановити та перезавантажити"; /* Take care not to overflow the status window. */ "Installing update..." = "Встановлюю оновлення…"; /* the unit for kilobytes */ "KB" = "КБ"; /* the unit for megabytes */ "MB" = "МБ"; "OK" = "OK"; "Ready to Install" = "Готовий до встановлення"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "Чи повинен %1$@ автоматично виконувати перевірку на оновлення? Ви завжди можете самостійно перевірити оновлення у меню %1$@."; "Update Error!" = "Помилка оновлення!"; "Updating %@" = "Оновлюю %@"; "You already have the newest version of %@." = "Ви вже користуєтесь останньою версією програми %@."; "You're up-to-date!" = "У вас остання версія!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "Дізнатись більше…"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_CN.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_CN.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_CN.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey 无记名系统概况信息被用于帮助我们安排将来的开发工作。如果对此存在疑问请联系我们。 这是将要被发送的信息:: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_CN.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ 已下载完毕并可以使用!您想要现在安装 %1$@ 吗?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "无法更新 %1$@,因为它运行于一个只读宗卷如磁盘映像或光盘。移动 %1$@ 到应用程序文件夹并再试一次。"; "%@ %@ is currently the newest version available." = "%1$@ %2$@ 是当前的最新版本。"; "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ 可供下载,您现在的版本是 %3$@。要现在下载吗?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ 已下载"; "%@ of %@" = "%1$@ / %2$@"; "A new version of %@ is available!" = "新版本的 %@ 已经发布"; "A new version of %@ is ready to install!" = "新版本的 %@ 可以安装了"; "An error occurred in retrieving update information. Please try again later." = "获取升级信息时出现错误,请稍后再试。"; "An error occurred while downloading the update. Please try again later." = "下载过程中出现错误,请稍后再试。"; "An error occurred while extracting the archive. Please try again later." = "解压过程中出现错误,请稍后再试。"; "An error occurred while installing the update. Please try again later." = "安装更新的过程中出现错误,请稍后再试。"; "An error occurred while parsing the update feed." = "解析更新信息时出现错误,请稍后再试。"; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "重新启动 %1$@ 时出现错误,但在下次运行 %1$@ 时新版本将可使用。"; /* the unit for bytes */ "B" = "B"; "Cancel" = "取消"; "Cancel Update" = "取消更新"; "Checking for updates..." = "正在检查更新…"; /* Take care not to overflow the status window. */ "Downloading update..." = "正在下载更新…"; /* Take care not to overflow the status window. */ "Extracting update..." = "正在解压更新…"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "安装并重启应用"; /* Take care not to overflow the status window. */ "Installing update..." = "正在安装更新…"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "好"; "Ready to Install" = "可以开始安装了"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "您想让 %1$@ 在启动时自动检查更新么?您也可以在程序菜单 %1$@ 中手动检查。"; "Update Error!" = "更新错误!"; "Updating %@" = "正在更新 %@"; "You already have the newest version of %@." = "您已经在使用最新版本的 %@。"; "You're up-to-date!" = "您使用的就是最新版!"; /* Alternative name for "Install" button if we have a paid update or other update without a download but with a URL. */ "Learn More..." = "下载更新包"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_TW.lproj/SUAutomaticUpdateAlert.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_TW.lproj/SUUpdateAlert.xib ================================================ NSNegateBoolean NSNegateBoolean NSNegateBoolean ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_TW.lproj/SUUpdatePermissionPrompt.xib ================================================ visibleKey visibleValue displayValue displayKey 匿名系統描述資訊可用來協助我們計畫未來的開發工作。若您有任何相關問題,請與我們聯繫。 以下是會傳送的資訊: SUIncludeProfile SUSendProfileInfo ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle/zh_TW.lproj/Sparkle.strings ================================================ "%1$@ %2$@ has been downloaded and is ready to use! Would you like to install it and relaunch %1$@ now?" = "%1$@ %2$@ 已下載且可供使用!您是否要現在安裝並重新啟動%1$@?"; "%1$@ can't be updated when it's running from a read-only volume like a disk image or an optical drive. Move %1$@ to your Applications folder, relaunch it from there, and try again." = "當 %1$@ 正從唯讀卷宗(如磁碟映像檔或光碟機)執行時,無法進行更新。請將 %1$@ 移至您的“應用程式”檔案夾,從該處重新啟動,然後再試一次。 "; "%@ %@ is currently the newest version available." = "%1$@ %2$@ 已是目前最新的版本。"; /* Description text for SUUpdateAlert when the update is downloadable. */ "%@ %@ is now available--you have %@. Would you like to download it now?" = "%1$@ %2$@ 現在已可取得,您的版本則是 %3$@。您要現在下載嗎?"; /* Description text for SUUpdateAlert when the update informational with no download. */ "%@ %@ is now available--you have %@. Would you like to learn more about this update on the web?" = "%1$@ %2$@ is now available--you have %3$@. Would you like to learn more about this update on the web?"; "%@ downloaded" = "%@ 已下載"; "%@ of %@" = "%1$@ / %2$@"; "A new version of %@ is available!" = "已有新版本的 %@ 可供下載!"; "A new version of %@ is ready to install!" = "新版本的 %@ 已準備安裝!"; "An error occurred in retrieving update information. Please try again later." = "擷取更新資訊時發生錯誤。請稍後再試一次。"; "An error occurred while downloading the update. Please try again later." = "下載更新項目時發生錯誤。請稍後再試一次。"; "An error occurred while extracting the archive. Please try again later." = "解壓縮封存檔時發生錯誤。請稍後再試一次。"; "An error occurred while installing the update. Please try again later." = "安裝更新項目時發生錯誤。請稍後再試一次。"; "An error occurred while parsing the update feed." = "解析更新 feed 時發生錯誤。"; "An error occurred while relaunching %1$@, but the new version will be available next time you run %1$@." = "重新啟動 %1$@ 時發生錯誤,但下次您執行 %1$@ 時將可使用新版本。"; /* the unit for bytes */ "B" = "B"; "Cancel" = "取消"; "Cancel Update" = "取消更新"; "Checking for updates..." = "正在檢查更新項目⋯"; /* Take care not to overflow the status window. */ "Downloading update..." = "正在下載更新項目⋯"; /* Take care not to overflow the status window. */ "Extracting update..." = "正在解壓縮更新項目⋯"; /* the unit for gigabytes */ "GB" = "GB"; "Install and Relaunch" = "安裝與重新啟動"; /* Take care not to overflow the status window. */ "Installing update..." = "正在安裝更新項目⋯"; /* the unit for kilobytes */ "KB" = "KB"; /* the unit for megabytes */ "MB" = "MB"; "OK" = "好"; "Ready to Install" = "準備安裝"; "Should %1$@ automatically check for updates? You can always check for updates manually from the %1$@ menu." = "%1$@ 是否應自動檢查更新項目?您可隨時從 %1$@ 選單手動檢查更新項目。"; "Update Error!" = "更新發生錯誤!"; "Updating %@" = "正在更新 %@"; "You already have the newest version of %@." = "您已有最新版本的 %@。"; "You're up-to-date!" = "你已有最新版本!"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle.podspec ================================================ Pod::Spec.new do |s| s.name = "Sparkle" s.version = "1.14.0" s.summary = "A software update framework for macOS" s.description = "Sparkle is an easy-to-use software update framework for Cocoa developers." s.homepage = "http://sparkle-project.org" s.documentation_url = "http://sparkle-project.org/documentation/" s.screenshot = "http://sparkle-project.org/images/screenshot-noshadow@2x.png" s.license = { :type => 'MIT', :file => 'LICENSE' } s.authors = { 'Andy Matuschak' => 'andy@andymatuschak.org', 'Kornel Lesiński' => 'pornel@pornel.net', 'C.W. Betts' => 'computers57@hotmail.com', 'Jake Petroules' => 'jake.petroules@petroules.com', 'Mayur Pawashe' => 'zorgiepoo@gmail.com', } s.platform = :osx, '10.7' s.source = { :http => "https://github.com/sparkle-project/Sparkle/releases/download/#{s.version}/Sparkle-#{s.version}.tar.bz2" } s.public_header_files = 'Sparkle.framework/Versions/A/Headers/*.h' s.vendored_frameworks = 'Sparkle.framework' s.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '"${PODS_ROOT}/Sparkle"', 'LD_RUNPATH_SEARCH_PATHS' => '@loader_path/../Frameworks' } s.requires_arc = true end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXAggregateTarget section */ 1495005F195FB89400BC5B5B /* All */ = { isa = PBXAggregateTarget; buildConfigurationList = 14950060195FB89500BC5B5B /* Build configuration list for PBXAggregateTarget "All" */; buildPhases = ( ); dependencies = ( 14950064195FB8A600BC5B5B /* PBXTargetDependency */, 14950066195FB8A600BC5B5B /* PBXTargetDependency */, 14950068195FB8A600BC5B5B /* PBXTargetDependency */, 1495006A195FB8A600BC5B5B /* PBXTargetDependency */, 1495006C195FB8A600BC5B5B /* PBXTargetDependency */, ); name = All; productName = All; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ 1420DF50196247F900203BB0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1420DF4F196247F900203BB0 /* Images.xcassets */; }; 1420DF51196247F900203BB0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1420DF4F196247F900203BB0 /* Images.xcassets */; }; 142E0E0019A6954400E4312B /* Sparkle.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 142E0E0219A6A14700E4312B /* Sparkle.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 142E0E0419A6A26F00E4312B /* Autoupdate.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 55C14BB7136EEF1500649790 /* Autoupdate.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 142E0E0919A83AAC00E4312B /* SUBinaryDeltaTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 142E0E0819A83AAC00E4312B /* SUBinaryDeltaTest.m */; }; 14652F7C19A9725300959E44 /* SUBinaryDeltaCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */; }; 14652F7D19A9726700959E44 /* SUBinaryDeltaApply.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */; }; 14652F7E19A9728A00959E44 /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */; }; 14652F8019A9740F00959E44 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5F8F609C4CEB300B25A18 /* Security.framework */; }; 14652F8219A9746000959E44 /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C14F05136EF6DB00649790 /* SULog.m */; }; 14652F8419A978C200959E44 /* SUExport.h in Headers */ = {isa = PBXBuildFile; fileRef = 14652F8319A9759F00959E44 /* SUExport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14732BC01960F2C200593899 /* test_app_only_dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 14732BBF1960F0AC00593899 /* test_app_only_dsa_pub.pem */; }; 14732BD019610A0D00593899 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; 14732BD119610A1200593899 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; 14732BD319610A1800593899 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14732BD219610A1800593899 /* XCTest.framework */; }; 1495006E195FCE1100BC5B5B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; 1495006F195FCE1800BC5B5B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; 14950070195FCE3A00BC5B5B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; 14950071195FCE3D00BC5B5B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; 14950072195FCE4B00BC5B5B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; 14950073195FCE4E00BC5B5B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; 14958C6E19AEBC950061B14F /* signed-test-file.txt in Resources */ = {isa = PBXBuildFile; fileRef = 14958C6B19AEBC530061B14F /* signed-test-file.txt */; }; 14958C6F19AEBC980061B14F /* test-pubkey.pem in Resources */ = {isa = PBXBuildFile; fileRef = 14958C6C19AEBC610061B14F /* test-pubkey.pem */; }; 3772FEA913DE0B6B00F79537 /* SUVersionDisplayProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 3772FEA813DE0B6B00F79537 /* SUVersionDisplayProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55C14BD4136EEFCE00649790 /* Autoupdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C14BD3136EEFCE00649790 /* Autoupdate.m */; }; 55C14BD9136EF00C00649790 /* SUStatus.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55C14BD8136EF00C00649790 /* SUStatus.xib */; }; 55C14BEE136EF20D00649790 /* SUAutomaticUpdateAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55C14BDA136EF20D00649790 /* SUAutomaticUpdateAlert.xib */; }; 55C14BEF136EF21700649790 /* SUStatus.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55C14BD8136EF00C00649790 /* SUStatus.xib */; }; 55C14C04136EF26100649790 /* SUUpdateAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55C14BF0136EF26100649790 /* SUUpdateAlert.xib */; }; 55C14C19136EF2C700649790 /* SUUpdatePermissionPrompt.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55C14C05136EF2C700649790 /* SUUpdatePermissionPrompt.xib */; }; 55C14F00136EF6B700649790 /* SUConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A5F09CA6EB100B7442F /* SUConstants.m */; }; 55C14F06136EF6DB00649790 /* SULog.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C14F04136EF6DB00649790 /* SULog.h */; }; 55C14F07136EF6DB00649790 /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C14F05136EF6DB00649790 /* SULog.m */; }; 55C14F08136EF6DB00649790 /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C14F05136EF6DB00649790 /* SULog.m */; }; 55C14F0C136EF6EA00649790 /* SUHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 61EF67550E25B58D00F754E0 /* SUHost.m */; }; 55C14F0D136EF6F200649790 /* SUInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5000DAE88B40026945C /* SUInstaller.m */; }; 55C14F20136EF84300649790 /* SUStatusController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6196CFE409C71ADE000DC222 /* SUStatusController.m */; }; 55C14F21136EF84D00649790 /* SUPlainInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5040DAE8AB80026945C /* SUPlainInstaller.m */; }; 55C14F22136EF86000649790 /* SUStandardVersionComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */; }; 55C14F23136EF86700649790 /* SUSystemProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A2279B0D1CEE7600430CCD /* SUSystemProfiler.m */; }; 55C14F24136EF86F00649790 /* SUPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */; }; 55C14F2A136EF9A900649790 /* SUWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61180BC90D64138900B4E0D1 /* SUWindowController.m */; }; 55C14F32136EFC2400649790 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55C14F31136EFC2400649790 /* SystemConfiguration.framework */; }; 55C14F9A136F045400649790 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5F8F609C4CEB300B25A18 /* Security.framework */; }; 55C14FC7136F05E100649790 /* Sparkle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8220A321A7F00D8810D /* Sparkle.strings */; }; 55E6F33319EC9F6C00005E76 /* SUErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 55E6F33219EC9F6C00005E76 /* SUErrors.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5A4094481C74EA5200983BE0 /* SUAppcastTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA4DCD01C73E5510078F128 /* SUAppcastTest.swift */; }; 5A6D31EE1BF53245009C5157 /* SUFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */; }; 5A6D31EF1BF5325F009C5157 /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 5AD0FA7F1C73F2E2004BCEFF /* testappcast.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5AD0FA7E1C73F2E2004BCEFF /* testappcast.xml */; }; 5AE459001C34118500E3BB47 /* SUUpdaterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 14950074195FDF5900BC5B5B /* SUUpdaterTest.m */; }; 5AE459021C34118500E3BB47 /* SUVersionComparisonTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 61227A150DB548B800AB99EA /* SUVersionComparisonTest.m */; }; 5AF6C74F1AEA46D10014A3AB /* test.sparkle_guided.pkg in Resources */ = {isa = PBXBuildFile; fileRef = 5AF6C74E1AEA46D10014A3AB /* test.sparkle_guided.pkg */; }; 5AF6C7541AEA49840014A3AB /* SUInstallerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AF6C74C1AEA40760014A3AB /* SUInstallerTest.m */; }; 5AF9DC3C1981DBEE001EA135 /* SUDSAVerifierTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AF9DC3B1981DBEE001EA135 /* SUDSAVerifierTest.m */; }; 5D06E8E90FD68CDB005AE3F6 /* bsdiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */; }; 5D06E8EA0FD68CDB005AE3F6 /* SUBinaryDeltaTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E30FD68CC7005AE3F6 /* SUBinaryDeltaTool.m */; }; 5D06E8EB0FD68CE4005AE3F6 /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */; }; 5D06E8EC0FD68CE4005AE3F6 /* SUBinaryDeltaApply.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */; }; 5D06E8ED0FD68CE4005AE3F6 /* SUBinaryDeltaCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */; }; 5D06E8FD0FD68D6B005AE3F6 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */; }; 5D06E8FF0FD68D6D005AE3F6 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */; }; 5D06E9050FD68D7D005AE3F6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; 5D06E9390FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D06E9370FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h */; }; 5D06E93A0FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E9380FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m */; }; 5D1AF58A0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */; }; 5D1AF58B0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */; }; 5D1AF5900FD767AD0065DB48 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */; }; 5D1AF59A0FD767E50065DB48 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5990FD767E50065DB48 /* libz.dylib */; }; 5D1AF82B0FD768180065DB48 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5990FD767E50065DB48 /* libz.dylib */; }; 5F1510A21C96E591006E1629 /* testnamespaces.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5F1510A11C96E591006E1629 /* testnamespaces.xml */; }; 610134730DD250470049ACDF /* SUUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 610134710DD250470049ACDF /* SUUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 610134740DD250470049ACDF /* SUUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 610134720DD250470049ACDF /* SUUpdateDriver.m */; }; 6101347B0DD2541A0049ACDF /* SUProbingUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 6101347C0DD2541A0049ACDF /* SUProbingUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 6101347A0DD2541A0049ACDF /* SUProbingUpdateDriver.m */; }; 6102FE460E077FCE00F85D09 /* SUPipedUnarchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 6102FE440E077FCE00F85D09 /* SUPipedUnarchiver.h */; settings = {ATTRIBUTES = (); }; }; 6102FE4A0E07803800F85D09 /* SUDiskImageUnarchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 6102FE480E07803800F85D09 /* SUDiskImageUnarchiver.h */; settings = {ATTRIBUTES = (); }; }; 6102FE4B0E07803800F85D09 /* SUDiskImageUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 6102FE490E07803800F85D09 /* SUDiskImageUnarchiver.m */; }; 6102FE5B0E08C7EC00F85D09 /* SUUnarchiver_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 6102FE590E08C7EC00F85D09 /* SUUnarchiver_Private.h */; settings = {ATTRIBUTES = (); }; }; 610EC1E00CF3A5FE00AE239E /* NTSynchronousTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 610EC1BF0CF3914D00AE239E /* NTSynchronousTask.m */; }; 611142E910FB1BE5009810AA /* bspatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 611142E810FB1BE5009810AA /* bspatch.h */; }; 61177A1F0D1112E900749C97 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6117796E0D1112E000749C97 /* IOKit.framework */; }; 61180BCA0D64138900B4E0D1 /* SUWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 61180BC80D64138900B4E0D1 /* SUWindowController.h */; settings = {ATTRIBUTES = (); }; }; 61180BCB0D64138900B4E0D1 /* SUWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61180BC90D64138900B4E0D1 /* SUWindowController.m */; }; 6120721209CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = 6120721009CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h */; settings = {ATTRIBUTES = (); }; }; 6120721309CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 6120721109CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m */; }; 61299A2F09CA2DAB00B7442F /* SUDSAVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 61299A2D09CA2DAB00B7442F /* SUDSAVerifier.h */; settings = {ATTRIBUTES = (); }; }; 61299A3009CA2DAB00B7442F /* SUDSAVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A2E09CA2DAB00B7442F /* SUDSAVerifier.m */; settings = {COMPILER_FLAGS = "-Wno-deprecated-declarations"; }; }; 61299A5C09CA6D4500B7442F /* SUConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 61299A5B09CA6D4500B7442F /* SUConstants.h */; settings = {ATTRIBUTES = (); }; }; 61299A6009CA6EB100B7442F /* SUConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A5F09CA6EB100B7442F /* SUConstants.m */; }; 61299A8D09CA790200B7442F /* SUUnarchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61299A8B09CA790200B7442F /* SUUnarchiver.h */; settings = {ATTRIBUTES = (Private, ); }; }; 61299A8E09CA790200B7442F /* SUUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A8C09CA790200B7442F /* SUUnarchiver.m */; }; 61299B3609CB04E000B7442F /* Sparkle.h in Headers */ = {isa = PBXBuildFile; fileRef = 61299B3509CB04E000B7442F /* Sparkle.h */; settings = {ATTRIBUTES = (Public, ); }; }; 612DCBAF0D488BC60015DBEA /* SUUpdatePermissionPrompt.h in Headers */ = {isa = PBXBuildFile; fileRef = 612DCBAD0D488BC60015DBEA /* SUUpdatePermissionPrompt.h */; settings = {ATTRIBUTES = (); }; }; 612DCBB00D488BC60015DBEA /* SUUpdatePermissionPrompt.m in Sources */ = {isa = PBXBuildFile; fileRef = 612DCBAE0D488BC60015DBEA /* SUUpdatePermissionPrompt.m */; }; 6158A1C5137904B300487EC1 /* SUUpdater_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 6158A1C4137904B300487EC1 /* SUUpdater_Private.h */; }; 615AE3D00D64DC40001CA7BD /* SUModelTranslation.plist in Resources */ = {isa = PBXBuildFile; fileRef = 615AE3CF0D64DC40001CA7BD /* SUModelTranslation.plist */; }; 6160E7E10D3B4A8800E9CD71 /* NTSynchronousTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 610EC1C00CF3914D00AE239E /* NTSynchronousTask.h */; settings = {ATTRIBUTES = (); }; }; 618FA5010DAE88B40026945C /* SUInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 618FA4FF0DAE88B40026945C /* SUInstaller.h */; settings = {ATTRIBUTES = (); }; }; 618FA5020DAE88B40026945C /* SUInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5000DAE88B40026945C /* SUInstaller.m */; }; 618FA5050DAE8AB80026945C /* SUPlainInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 618FA5030DAE8AB80026945C /* SUPlainInstaller.h */; settings = {ATTRIBUTES = (); }; }; 618FA5060DAE8AB80026945C /* SUPlainInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5040DAE8AB80026945C /* SUPlainInstaller.m */; }; 618FA5220DAE8E8A0026945C /* SUPackageInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 618FA5200DAE8E8A0026945C /* SUPackageInstaller.h */; settings = {ATTRIBUTES = (); }; }; 618FA5230DAE8E8A0026945C /* SUPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */; }; 6196CFF909C72148000DC222 /* SUStatusController.h in Headers */ = {isa = PBXBuildFile; fileRef = 6196CFE309C71ADE000DC222 /* SUStatusController.h */; settings = {ATTRIBUTES = (); }; }; 6196CFFA09C72149000DC222 /* SUStatusController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6196CFE409C71ADE000DC222 /* SUStatusController.m */; }; 61A2259E0D1C495D00430CCD /* SUVersionComparisonProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61A225A40D1C4AC000430CCD /* SUStandardVersionComparator.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61A225A50D1C4AC000430CCD /* SUStandardVersionComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */; }; 61A2279C0D1CEE7600430CCD /* SUSystemProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A2279A0D1CEE7600430CCD /* SUSystemProfiler.h */; settings = {ATTRIBUTES = (); }; }; 61A2279D0D1CEE7600430CCD /* SUSystemProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A2279B0D1CEE7600430CCD /* SUSystemProfiler.m */; }; 61A354550DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A354530DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 61A354560DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A354540DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.m */; }; 61AAE8280A321A7F00D8810D /* Sparkle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8220A321A7F00D8810D /* Sparkle.strings */; }; 61B078CE15A5FB6100600039 /* SUCodeSigningVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B078CC15A5FB6100600039 /* SUCodeSigningVerifier.h */; }; 61B078CF15A5FB6100600039 /* SUCodeSigningVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B078CD15A5FB6100600039 /* SUCodeSigningVerifier.m */; }; 61B5F8ED09C4CE3C00B25A18 /* SUUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5F8E309C4CE3C00B25A18 /* SUUpdater.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61B5F8EE09C4CE3C00B25A18 /* SUUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F8E409C4CE3C00B25A18 /* SUUpdater.m */; }; 61B5F8F709C4CEB300B25A18 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5F8F609C4CEB300B25A18 /* Security.framework */; }; 61B5F90F09C4CF3A00B25A18 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; }; 61B5F92E09C4CFD800B25A18 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 61B5F92A09C4CFD800B25A18 /* InfoPlist.strings */; }; 61B5F92F09C4CFD800B25A18 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61B5F92C09C4CFD800B25A18 /* MainMenu.xib */; }; 61B5F93009C4CFDC00B25A18 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5F92409C4CFC900B25A18 /* main.m */; }; 61B5FBB709C4FAFF00B25A18 /* SUAppcast.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5FB9509C4F04600B25A18 /* SUAppcast.m */; }; 61B5FC0D09C4FC8200B25A18 /* SUAppcast.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FB9409C4F04600B25A18 /* SUAppcast.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61B5FC4C09C4FD5E00B25A18 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5FC3F09C4FD4000B25A18 /* WebKit.framework */; }; 61B5FC6F09C51F4900B25A18 /* SUAppcastItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5FC5409C5182000B25A18 /* SUAppcastItem.m */; }; 61B5FC7009C51F4A00B25A18 /* SUAppcastItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FC5309C5182000B25A18 /* SUAppcastItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61B5FCDE09C52A9F00B25A18 /* SUUpdateAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5FCA109C5228F00B25A18 /* SUUpdateAlert.m */; }; 61B5FCDF09C52A9F00B25A18 /* SUUpdateAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B5FCA009C5228F00B25A18 /* SUUpdateAlert.h */; settings = {ATTRIBUTES = (); }; }; 61B93A3C0DD02D7000DCD2F8 /* SUUIBasedUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B93A390DD02D6400DCD2F8 /* SUUIBasedUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 61B93A3D0DD02D7000DCD2F8 /* SUUIBasedUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B93A3A0DD02D6400DCD2F8 /* SUUIBasedUpdateDriver.m */; }; 61B93B270DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B93B250DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 61B93B280DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B93B260DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.m */; }; 61B93C090DD112FF00DCD2F8 /* SUScheduledUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61B93C070DD112FF00DCD2F8 /* SUScheduledUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 61B93C0A0DD112FF00DCD2F8 /* SUScheduledUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B93C080DD112FF00DCD2F8 /* SUScheduledUpdateDriver.m */; }; 61CFB3290E385186007A1735 /* Sparkle.pch in Headers */ = {isa = PBXBuildFile; fileRef = 61CFB3280E385186007A1735 /* Sparkle.pch */; }; 61D85D6D0E10B2ED00F9B4A9 /* SUPipedUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 6129C0B90E0B79810062CE76 /* SUPipedUnarchiver.m */; }; 61EF67560E25B58D00F754E0 /* SUHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 61EF67550E25B58D00F754E0 /* SUHost.m */; }; 61EF67590E25C5B400F754E0 /* SUHost.h in Headers */ = {isa = PBXBuildFile; fileRef = 61EF67580E25C5B400F754E0 /* SUHost.h */; }; 61F83F720DBFE140006FDD30 /* SUBasicUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 61F83F700DBFE137006FDD30 /* SUBasicUpdateDriver.m */; }; 61F83F740DBFE141006FDD30 /* SUBasicUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 61F83F6F0DBFE137006FDD30 /* SUBasicUpdateDriver.h */; settings = {ATTRIBUTES = (); }; }; 61FA52880E2D9EA400EF58AD /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 720B16441C66433D006985FB /* UITests-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 720B16421C66433D006985FB /* UITests-Info.plist */; }; 720B16451C66433D006985FB /* SUTestApplicationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 720B16431C66433D006985FB /* SUTestApplicationTest.swift */; }; 7210C7681B9A9A1500EB90AC /* SUUnarchiverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7210C7671B9A9A1500EB90AC /* SUUnarchiverTest.swift */; }; 721BC2111D1DFF05002BC71E /* SUFileOperationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */; }; 721CF1A71AD7643600D9AC09 /* bsdiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */; }; 721CF1A81AD7644100D9AC09 /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */; }; 721CF1A91AD7644C00D9AC09 /* sais.c in Sources */ = {isa = PBXBuildFile; fileRef = 7223E7611AD1AEFF008E3161 /* sais.c */; }; 721CF1AA1AD7647000D9AC09 /* libxar.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */; }; 721CF1AB1AD764EB00D9AC09 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */; }; 7223E7631AD1AEFF008E3161 /* sais.c in Sources */ = {isa = PBXBuildFile; fileRef = 7223E7611AD1AEFF008E3161 /* sais.c */; }; 722954B71D04ADAF00ECF9CA /* fileop.m in Sources */ = {isa = PBXBuildFile; fileRef = 722954B61D04ADAF00ECF9CA /* fileop.m */; }; 722954BC1D04AE1100ECF9CA /* SUFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */; }; 722954BD1D04AE3800ECF9CA /* SUConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A5F09CA6EB100B7442F /* SUConstants.m */; }; 722954C11D04D9ED00ECF9CA /* fileop in Copy fileop */ = {isa = PBXBuildFile; fileRef = 722954B41D04ADAF00ECF9CA /* fileop */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 722954C41D04E66F00ECF9CA /* SUFileOperationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 722954C21D04E66F00ECF9CA /* SUFileOperationConstants.h */; }; 722954C51D04E66F00ECF9CA /* SUFileOperationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */; }; 722954C61D04E66F00ECF9CA /* SUFileOperationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */; }; 722954C71D04E66F00ECF9CA /* SUFileOperationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */; }; 722954C91D04EA5C00ECF9CA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; 722954CA1D04EA6000ECF9CA /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; 722954CB1D04EA6900ECF9CA /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B5F8F609C4CEB300B25A18 /* Security.framework */; }; 722954CC1D04EA7000ECF9CA /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 723B252D1CEAB3A600909873 /* bscommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 723B252B1CEAB3A600909873 /* bscommon.c */; }; 723B252E1CEAB3A600909873 /* bscommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 723B252B1CEAB3A600909873 /* bscommon.c */; }; 723B252F1CEAB3A600909873 /* bscommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 723B252B1CEAB3A600909873 /* bscommon.c */; }; 723B25301CEAB3A600909873 /* bscommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 723B252C1CEAB3A600909873 /* bscommon.h */; }; 72544FFC1D0991A4000CFB2C /* SUFileOperationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */; }; 7268AC631AD634C200C3E0C1 /* SUBinaryDeltaCreate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7268AC621AD634C200C3E0C1 /* SUBinaryDeltaCreate.m */; }; 726F2CDE1BC9BE5B001971A4 /* SUFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */; }; 726F2CE51BC9C33D001971A4 /* SUOperatingSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 726F2CE31BC9C33D001971A4 /* SUOperatingSystem.h */; }; 726F2CE61BC9C33D001971A4 /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 726F2CE71BC9C3E2001971A4 /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 726F2CE81BC9C48F001971A4 /* SUConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A5F09CA6EB100B7442F /* SUConstants.m */; }; 726F2CE91BC9C499001971A4 /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 726F2CEB1BC9C733001971A4 /* SUConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 61299A5F09CA6EB100B7442F /* SUConstants.m */; }; 7275F9C11B5F1F2900B1D19E /* SUFileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 7275F9BF1B5F1F2900B1D19E /* SUFileManager.h */; }; 7275F9C21B5F1F2900B1D19E /* SUFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */; }; 7275F9C31B5F1F2900B1D19E /* SUFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */; }; 72A4A2401BB6567D00E7820D /* SUFileManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A4A23F1BB6567D00E7820D /* SUFileManagerTest.swift */; }; 72AC6B261B9AAC8800F62325 /* SparkleTestCodeSignApp.tar.gz in Resources */ = {isa = PBXBuildFile; fileRef = 72AC6B251B9AAC8800F62325 /* SparkleTestCodeSignApp.tar.gz */; }; 72AC6B281B9AAD6700F62325 /* SparkleTestCodeSignApp.tar in Resources */ = {isa = PBXBuildFile; fileRef = 72AC6B271B9AAD6700F62325 /* SparkleTestCodeSignApp.tar */; }; 72AC6B2A1B9AAF3A00F62325 /* SparkleTestCodeSignApp.tar.bz2 in Resources */ = {isa = PBXBuildFile; fileRef = 72AC6B291B9AAF3A00F62325 /* SparkleTestCodeSignApp.tar.bz2 */; }; 72AC6B2C1B9AB0EE00F62325 /* SparkleTestCodeSignApp.tar.xz in Resources */ = {isa = PBXBuildFile; fileRef = 72AC6B2B1B9AB0EE00F62325 /* SparkleTestCodeSignApp.tar.xz */; }; 72AC6B2E1B9B218C00F62325 /* SparkleTestCodeSignApp.dmg in Resources */ = {isa = PBXBuildFile; fileRef = 72AC6B2D1B9B218C00F62325 /* SparkleTestCodeSignApp.dmg */; }; 72D4DAA11AD7632900B211E2 /* SUBinaryDeltaCreate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7268AC621AD634C200C3E0C1 /* SUBinaryDeltaCreate.m */; }; 72E45CF31B640CDD005C701A /* SUTestApplicationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 72E45CF21B640CDD005C701A /* SUTestApplicationDelegate.m */; }; 72E45CF71B640DAE005C701A /* SUUpdateSettingsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 72E45CF51B640DAE005C701A /* SUUpdateSettingsWindowController.m */; }; 72E45CF81B640DAE005C701A /* SUUpdateSettingsWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 72E45CF61B640DAE005C701A /* SUUpdateSettingsWindowController.xib */; }; 72E45CFC1B641961005C701A /* sparkletestcast.xml in Resources */ = {isa = PBXBuildFile; fileRef = 72E45CFB1B641961005C701A /* sparkletestcast.xml */; }; 72E45CFE1B641D0D005C701A /* test_app_only_dsa_priv_dont_ever_do_this_for_real.pem in Resources */ = {isa = PBXBuildFile; fileRef = 72E45CFD1B641D0D005C701A /* test_app_only_dsa_priv_dont_ever_do_this_for_real.pem */; }; 72E45DDD1B65C3BD005C701A /* sign_update in Resources */ = {isa = PBXBuildFile; fileRef = 72E45DDC1B65C3BD005C701A /* sign_update */; }; 72E641801D08D754000404CA /* fileop in Resources */ = {isa = PBXBuildFile; fileRef = 722954B41D04ADAF00ECF9CA /* fileop */; }; 767B61AC1972D488004E0C3C /* SUGuidedPackageInstaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 767B61AA1972D488004E0C3C /* SUGuidedPackageInstaller.h */; }; 767B61AD1972D488004E0C3C /* SUGuidedPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 767B61AB1972D488004E0C3C /* SUGuidedPackageInstaller.m */; }; 767B61AE1972D488004E0C3C /* SUGuidedPackageInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 767B61AB1972D488004E0C3C /* SUGuidedPackageInstaller.m */; }; A5BF4F1D1BC7668B007A052A /* SUTestWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = A5BF4F1C1BC7668B007A052A /* SUTestWebServer.m */; }; C23E885B1BE7B24F0050BB73 /* SparkleTestCodeSignApp.enc.dmg in Resources */ = {isa = PBXBuildFile; fileRef = C23E88591BE7AF890050BB73 /* SparkleTestCodeSignApp.enc.dmg */; }; F8761EB11ADC5068000C9034 /* SUCodeSigningVerifierTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F8761EB01ADC5068000C9034 /* SUCodeSigningVerifierTest.m */; }; F8761EB31ADC50EB000C9034 /* SparkleTestCodeSignApp.zip in Resources */ = {isa = PBXBuildFile; fileRef = F8761EB21ADC50EB000C9034 /* SparkleTestCodeSignApp.zip */; }; F8761EB61ADC5E7A000C9034 /* SUCodeSigningVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B078CD15A5FB6100600039 /* SUCodeSigningVerifier.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 1454BA1519637EDB00344E57 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 61B5F90109C4CEE200B25A18; remoteInfo = "Sparkle Test App"; }; 1454BA1719637EE900344E57 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 1420DF491962329200203BB0; remoteInfo = Documentation; }; 14732BCA1960F73500593899 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 14732BCE1960F73500593899 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 5D06E8CF0FD68C7C005AE3F6; remoteInfo = BinaryDelta; }; 14950063195FB8A600BC5B5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 14950065195FB8A600BC5B5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 61B5F90109C4CEE200B25A18; remoteInfo = "Sparkle Test App"; }; 14950067195FB8A600BC5B5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 612279D80DB5470200AB99EA; remoteInfo = "Sparkle Unit Tests"; }; 14950069195FB8A600BC5B5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 5D06E8CF0FD68C7C005AE3F6; remoteInfo = BinaryDelta; }; 1495006B195FB8A600BC5B5B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 55C14BB6136EEF1500649790; remoteInfo = Autoupdate; }; 55C14F96136F044100649790 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 55C14BB6136EEF1500649790; remoteInfo = Autoupdate; }; 5D06E8D50FD68C86005AE3F6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 61B5F91B09C4CF7200B25A18 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 61FA528C0E2D9EB200EF58AD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = Sparkle; }; 722954BE1D04D9CB00ECF9CA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 722954B31D04ADAF00ECF9CA; remoteInfo = fileop; }; 726B2B621C645FC900388755 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 61B5F90109C4CEE200B25A18; remoteInfo = "Sparkle Test App"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 142E0E0119A6A13300E4312B /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 142E0E0219A6A14700E4312B /* Sparkle.framework in Copy Files */, ); name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; 142E0E0319A6A24100E4312B /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 12; files = ( 142E0E0419A6A26F00E4312B /* Autoupdate.app in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; 61B5FB4D09C4E9FA00B25A18 /* Copy Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 142E0E0019A6954400E4312B /* Sparkle.framework in Copy Frameworks */, ); name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; 722954B21D04ADAF00ECF9CA /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; 722954C01D04D9D600ECF9CA /* Copy fileop */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 6; files = ( 722954C11D04D9ED00ECF9CA /* fileop in Copy fileop */, ); name = "Copy fileop"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 004A8652192A492B00C9730D /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Sparkle.strings; sourceTree = ""; }; 004A8653192A492B00C9730D /* ro */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ro; path = ro.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 004A8654192A492B00C9730D /* ro */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ro; path = ro.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 004A8655192A492B00C9730D /* ro */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ro; path = ro.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 0263187214FEBB31005EBF43 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Sparkle.strings; sourceTree = ""; }; 0263187514FEBB42005EBF43 /* uk */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = uk; path = uk.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 0263187614FEBB42005EBF43 /* uk */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = uk; path = uk.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 0263187714FEBB42005EBF43 /* uk */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = uk; path = uk.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 1420DF4D196247B500203BB0 /* build-docs.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-docs.sh"; sourceTree = ""; }; 1420DF4E196247B500203BB0 /* Doxyfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Doxyfile; sourceTree = ""; }; 1420DF4F196247F900203BB0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 142E0E0819A83AAC00E4312B /* SUBinaryDeltaTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaTest.m; sourceTree = ""; }; 14652F7919A93E5F00959E44 /* set-git-version-info.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "set-git-version-info.sh"; sourceTree = ""; }; 14652F8319A9759F00959E44 /* SUExport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SUExport.h; sourceTree = ""; }; 146EC84E19A68CF8004A50C5 /* Sparkle.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Sparkle.podspec; sourceTree = SOURCE_ROOT; }; 14732BB1195FF6B700593899 /* .clang-format */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".clang-format"; sourceTree = SOURCE_ROOT; }; 14732BB91960EEEE00593899 /* SampleAppcast.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = SampleAppcast.xml; sourceTree = ""; }; 14732BBA1960EF7100593899 /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = SOURCE_ROOT; }; 14732BBC1960EFB500593899 /* README.markdown */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.markdown; sourceTree = SOURCE_ROOT; }; 14732BBF1960F0AC00593899 /* test_app_only_dsa_pub.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = test_app_only_dsa_pub.pem; sourceTree = ""; }; 14732BC11960F3B200593899 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = SOURCE_ROOT; usesTabs = 1; }; 14732BC31960F3FF00593899 /* generate_keys */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = generate_keys; sourceTree = ""; }; 14732BC41960F3FF00593899 /* sign_update */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = sign_update; sourceTree = ""; }; 14732BC91960F70A00593899 /* make-release-package.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "make-release-package.sh"; sourceTree = ""; }; 14732BD219610A1800593899 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 147D6D9E1B66DC3C006607AB /* CheckLocalizations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CheckLocalizations.swift; path = Sparkle/CheckLocalizations.swift; sourceTree = SOURCE_ROOT; }; 147D6DA71B66EC1C006607AB /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Sparkle.strings; sourceTree = ""; }; 147D6DA91B66EC22006607AB /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Sparkle.strings; sourceTree = ""; }; 147D6DAA1B66EC25006607AB /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Sparkle.strings; sourceTree = ""; }; 14950074195FDF5900BC5B5B /* SUUpdaterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdaterTest.m; sourceTree = ""; usesTabs = 0; }; 14958C6B19AEBC530061B14F /* signed-test-file.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "signed-test-file.txt"; sourceTree = ""; }; 14958C6C19AEBC610061B14F /* test-pubkey.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "test-pubkey.pem"; sourceTree = ""; }; 149B78631B7D3A0C00D7D62C /* ConfigCommonCoverage.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigCommonCoverage.xcconfig; sourceTree = ""; }; 149B78641B7D3A4800D7D62C /* ConfigUnitTestCoverage.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigUnitTestCoverage.xcconfig; sourceTree = ""; }; 1A985A321C5C329C0001163A /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A331C5C329D0001163A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A341C5C329E0001163A /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A351C5C329E0001163A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; 1A985A361C5C32A20001163A /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A371C5C32A30001163A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A381C5C32A40001163A /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A391C5C32A40001163A /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A3A1C5C32A50001163A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A3B1C5C32A60001163A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A3C1C5C32A70001163A /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A3D1C5C32A80001163A /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A3E1C5C32A90001163A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A3F1C5C32AA0001163A /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A401C5C32AA0001163A /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A411C5C32AB0001163A /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A421C5C32AC0001163A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A431C5C32AD0001163A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A441C5C32AE0001163A /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A451C5C32AF0001163A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A461C5C32B00001163A /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A471C5C32B10001163A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A481C5C32B10001163A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A491C5C32B20001163A /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A4A1C5C32B50001163A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 1A985A4B1C5C32B60001163A /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-CA"; path = "fr-CA.lproj/InfoPlist.strings"; sourceTree = ""; }; 1A985A4C1C5C32B70001163A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = ""; }; 1A985A4D1C5C32B70001163A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; 1A985A4E1C5C32B80001163A /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A4F1C5C32B90001163A /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A501C5C32BA0001163A /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A511C5C32BB0001163A /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A521C5C32BC0001163A /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = ""; }; 1A985A531C5C32BD0001163A /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; 3772FEA813DE0B6B00F79537 /* SUVersionDisplayProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUVersionDisplayProtocol.h; sourceTree = ""; }; 4607BEA21948443800EF8DA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Sparkle.strings; sourceTree = ""; }; 4607BEA31948443800EF8DA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nb; path = nb.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 4607BEA41948443800EF8DA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nb; path = nb.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 4607BEA51948443800EF8DA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nb; path = nb.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 525A278F133D6AE900FD8D70 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 555CF29A196C52330000B31E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Sparkle.strings; sourceTree = ""; }; 555CF29B196C523E0000B31E /* el */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = el; path = el.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 555CF29C196C52460000B31E /* el */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = el; path = el.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 555CF29D196C524C0000B31E /* el */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = el; path = el.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14BB7136EEF1500649790 /* Autoupdate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Autoupdate.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55C14BB9136EEF1500649790 /* Autoupdate-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Autoupdate-Info.plist"; sourceTree = ""; }; 55C14BD3136EEFCE00649790 /* Autoupdate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Autoupdate.m; sourceTree = ""; }; 55C14BD8136EF00C00649790 /* SUStatus.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SUStatus.xib; sourceTree = ""; }; 55C14BDB136EF20D00649790 /* cs */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = cs; path = cs.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BDC136EF20D00649790 /* da */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = da; path = da.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BDD136EF20D00649790 /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BDE136EF20D00649790 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BDF136EF20D00649790 /* es */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = es; path = es.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE0136EF20D00649790 /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE1136EF20D00649790 /* is */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = is; path = is.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE2136EF20D00649790 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE3136EF20D00649790 /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE4136EF20D00649790 /* ko */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ko; path = ko.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE5136EF20D00649790 /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE6136EF20D00649790 /* pl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pl; path = pl.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE7136EF20D00649790 /* pt_BR */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt_BR; path = pt_BR.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BE9136EF20D00649790 /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = ru.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BEA136EF20D00649790 /* sk */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sk; path = sk.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BEB136EF20D00649790 /* sv */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sv; path = sv.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BEC136EF20D00649790 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_CN; path = zh_CN.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BED136EF20D00649790 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_TW; path = zh_TW.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BF1136EF26100649790 /* cs */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = cs; path = cs.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF2136EF26100649790 /* da */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = da; path = da.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF3136EF26100649790 /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF4136EF26100649790 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF5136EF26100649790 /* es */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = es; path = es.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF6136EF26100649790 /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF7136EF26100649790 /* is */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = is; path = is.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF8136EF26100649790 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BF9136EF26100649790 /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BFA136EF26100649790 /* ko */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ko; path = ko.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BFB136EF26100649790 /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BFC136EF26100649790 /* pl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pl; path = pl.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BFD136EF26100649790 /* pt_BR */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt_BR; path = pt_BR.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14BFF136EF26100649790 /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = ru.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14C00136EF26100649790 /* sk */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sk; path = sk.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14C01136EF26100649790 /* sv */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sv; path = sv.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14C02136EF26100649790 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_CN; path = zh_CN.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14C03136EF26100649790 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_TW; path = zh_TW.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 55C14C06136EF2C700649790 /* cs */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = cs; path = cs.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C07136EF2C700649790 /* da */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = da; path = da.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C08136EF2C700649790 /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C09136EF2C700649790 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C0A136EF2C700649790 /* es */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = es; path = es.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C0B136EF2C700649790 /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C0C136EF2C700649790 /* is */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = is; path = is.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C0D136EF2C700649790 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C0E136EF2C700649790 /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C0F136EF2C700649790 /* ko */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ko; path = ko.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C10136EF2C700649790 /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C11136EF2C700649790 /* pl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pl; path = pl.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C12136EF2C700649790 /* pt_BR */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt_BR; path = pt_BR.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C14136EF2C700649790 /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = ru.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C15136EF2C700649790 /* sk */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sk; path = sk.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C16136EF2C700649790 /* sv */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sv; path = sv.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C17136EF2C700649790 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_CN; path = zh_CN.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14C18136EF2C700649790 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_TW; path = zh_TW.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 55C14F04136EF6DB00649790 /* SULog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SULog.h; sourceTree = ""; }; 55C14F05136EF6DB00649790 /* SULog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SULog.m; sourceTree = ""; }; 55C14F31136EFC2400649790 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 55E6F33219EC9F6C00005E76 /* SUErrors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUErrors.h; sourceTree = ""; }; 5AA4DCD01C73E5510078F128 /* SUAppcastTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SUAppcastTest.swift; sourceTree = ""; }; 5AD0FA7E1C73F2E2004BCEFF /* testappcast.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = testappcast.xml; sourceTree = ""; }; 5AEF45D9189D1CC90030D7DC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Sparkle.strings; sourceTree = ""; }; 5AF6C74C1AEA40760014A3AB /* SUInstallerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUInstallerTest.m; sourceTree = ""; }; 5AF6C74E1AEA46D10014A3AB /* test.sparkle_guided.pkg */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.sparkle_guided.pkg; sourceTree = ""; }; 5AF9DC3B1981DBEE001EA135 /* SUDSAVerifierTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUDSAVerifierTest.m; sourceTree = ""; }; 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BinaryDelta; sourceTree = BUILT_PRODUCTS_DIR; }; 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */ = {isa = PBXFileReference; comments = "-Wno-shorten-64-to-32"; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bsdiff.c; sourceTree = ""; }; 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = ""; }; 5D06E8DF0FD68CC7005AE3F6 /* SUBinaryDeltaApply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaApply.h; sourceTree = ""; }; 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaApply.m; sourceTree = ""; }; 5D06E8E10FD68CC7005AE3F6 /* SUBinaryDeltaCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaCommon.h; sourceTree = ""; }; 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaCommon.m; sourceTree = ""; }; 5D06E8E30FD68CC7005AE3F6 /* SUBinaryDeltaTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaTool.m; sourceTree = ""; usesTabs = 0; }; 5D06E8F10FD68D21005AE3F6 /* ConfigBinaryDelta.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigBinaryDelta.xcconfig; sourceTree = ""; }; 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigBinaryDeltaDebug.xcconfig; sourceTree = ""; }; 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigBinaryDeltaRelease.xcconfig; sourceTree = ""; }; 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = usr/lib/libbz2.dylib; sourceTree = SDKROOT; }; 5D06E9370FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaUnarchiver.h; sourceTree = ""; }; 5D06E9380FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaUnarchiver.m; sourceTree = ""; }; 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxar.1.dylib; path = usr/lib/libxar.1.dylib; sourceTree = SDKROOT; }; 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; }; 5D1AF5990FD767E50065DB48 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 5F1510A11C96E591006E1629 /* testnamespaces.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = testnamespaces.xml; sourceTree = ""; }; 610134710DD250470049ACDF /* SUUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdateDriver.h; sourceTree = ""; }; 610134720DD250470049ACDF /* SUUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdateDriver.m; sourceTree = ""; }; 610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUProbingUpdateDriver.h; sourceTree = ""; }; 6101347A0DD2541A0049ACDF /* SUProbingUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUProbingUpdateDriver.m; sourceTree = ""; }; 6102FE440E077FCE00F85D09 /* SUPipedUnarchiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUPipedUnarchiver.h; sourceTree = ""; }; 6102FE480E07803800F85D09 /* SUDiskImageUnarchiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUDiskImageUnarchiver.h; sourceTree = ""; }; 6102FE490E07803800F85D09 /* SUDiskImageUnarchiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUDiskImageUnarchiver.m; sourceTree = ""; }; 6102FE590E08C7EC00F85D09 /* SUUnarchiver_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUnarchiver_Private.h; sourceTree = ""; }; 610EC1BF0CF3914D00AE239E /* NTSynchronousTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NTSynchronousTask.m; sourceTree = ""; }; 610EC1C00CF3914D00AE239E /* NTSynchronousTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NTSynchronousTask.h; sourceTree = ""; }; 611142E810FB1BE5009810AA /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = ""; }; 61131A050F846CE600E97AF6 /* da */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Sparkle.strings; sourceTree = ""; }; 61131A090F846D0A00E97AF6 /* zh_CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = zh_CN; path = zh_CN.lproj/Sparkle.strings; sourceTree = ""; }; 61131A0A0F846D1100E97AF6 /* zh_TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = zh_TW; path = zh_TW.lproj/Sparkle.strings; sourceTree = ""; }; 6117796E0D1112E000749C97 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 61180BC80D64138900B4E0D1 /* SUWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUWindowController.h; sourceTree = ""; }; 61180BC90D64138900B4E0D1 /* SUWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUWindowController.m; sourceTree = ""; }; 611A904210240DD300CC659E /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Sparkle.strings; sourceTree = ""; }; 611A904610240DF700CC659E /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Sparkle.strings; sourceTree = ""; }; 6120721009CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUAutomaticUpdateAlert.h; sourceTree = ""; }; 6120721109CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUAutomaticUpdateAlert.m; sourceTree = ""; }; 612279D90DB5470200AB99EA /* Sparkle Unit Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Sparkle Unit Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 612279DA0DB5470200AB99EA /* SparkleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SparkleTests-Info.plist"; sourceTree = ""; }; 61227A150DB548B800AB99EA /* SUVersionComparisonTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUVersionComparisonTest.m; sourceTree = ""; }; 61299A2D09CA2DAB00B7442F /* SUDSAVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUDSAVerifier.h; sourceTree = ""; }; 61299A2E09CA2DAB00B7442F /* SUDSAVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUDSAVerifier.m; sourceTree = ""; }; 61299A5B09CA6D4500B7442F /* SUConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUConstants.h; sourceTree = ""; }; 61299A5F09CA6EB100B7442F /* SUConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUConstants.m; sourceTree = ""; }; 61299A8B09CA790200B7442F /* SUUnarchiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUnarchiver.h; sourceTree = ""; }; 61299A8C09CA790200B7442F /* SUUnarchiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUnarchiver.m; sourceTree = ""; }; 61299B3509CB04E000B7442F /* Sparkle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sparkle.h; sourceTree = ""; }; 6129C0B90E0B79810062CE76 /* SUPipedUnarchiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUPipedUnarchiver.m; sourceTree = ""; }; 612DCBAD0D488BC60015DBEA /* SUUpdatePermissionPrompt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdatePermissionPrompt.h; sourceTree = ""; }; 612DCBAE0D488BC60015DBEA /* SUUpdatePermissionPrompt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdatePermissionPrompt.m; sourceTree = ""; }; 613151B20FB4946A000DCD59 /* is */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Sparkle.strings; sourceTree = ""; }; 6149E6EA1601ABAC008A351E /* ar */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Sparkle.strings; sourceTree = ""; }; 6149E6ED1601ABAC008A351E /* ar */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ar; path = ar.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 6149E6EF1601ABAC008A351E /* ar */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ar; path = ar.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 6149E6F01601ABAC008A351E /* ar */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ar; path = ar.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 615409C4103BBC4000125AF1 /* cs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Sparkle.strings; sourceTree = ""; }; 6158A1B81378F64700487EC1 /* pt_PT */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt_PT; path = pt_PT.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 6158A1BA1378F68100487EC1 /* pt_PT */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt_PT; path = pt_PT.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 6158A1BB1378F68100487EC1 /* pt_PT */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt_PT; path = pt_PT.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 6158A1BE1378F8BB00487EC1 /* tr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = tr; path = tr.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 6158A1BF1378F8BB00487EC1 /* tr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = tr; path = tr.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 6158A1C01378F8BB00487EC1 /* tr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = tr; path = tr.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 6158A1C4137904B300487EC1 /* SUUpdater_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdater_Private.h; sourceTree = ""; }; 615AE3CF0D64DC40001CA7BD /* SUModelTranslation.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SUModelTranslation.plist; sourceTree = ""; }; 6186554310D7484E00B1E074 /* pt_PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_PT; path = pt_PT.lproj/Sparkle.strings; sourceTree = ""; }; 618915730E35937600B5E981 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Sparkle.strings; sourceTree = ""; }; 618FA4FF0DAE88B40026945C /* SUInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUInstaller.h; sourceTree = ""; }; 618FA5000DAE88B40026945C /* SUInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUInstaller.m; sourceTree = ""; }; 618FA5030DAE8AB80026945C /* SUPlainInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUPlainInstaller.h; sourceTree = ""; }; 618FA5040DAE8AB80026945C /* SUPlainInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUPlainInstaller.m; sourceTree = ""; }; 618FA5200DAE8E8A0026945C /* SUPackageInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUPackageInstaller.h; sourceTree = ""; }; 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUPackageInstaller.m; sourceTree = ""; }; 6195D4920E404AD700D41A50 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Sparkle.strings; sourceTree = ""; }; 6196CFE309C71ADE000DC222 /* SUStatusController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUStatusController.h; sourceTree = ""; }; 6196CFE409C71ADE000DC222 /* SUStatusController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUStatusController.m; sourceTree = ""; }; 619B17200E1E9D0800E72754 /* de */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Sparkle.strings; sourceTree = ""; }; 61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUVersionComparisonProtocol.h; sourceTree = ""; }; 61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUStandardVersionComparator.h; sourceTree = ""; }; 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUStandardVersionComparator.m; sourceTree = ""; }; 61A2279A0D1CEE7600430CCD /* SUSystemProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUSystemProfiler.h; sourceTree = ""; }; 61A2279B0D1CEE7600430CCD /* SUSystemProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUSystemProfiler.m; sourceTree = ""; }; 61A354530DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUserInitiatedUpdateDriver.h; sourceTree = ""; }; 61A354540DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUserInitiatedUpdateDriver.m; sourceTree = ""; }; 61AAE8230A321A7F00D8810D /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Sparkle.strings; sourceTree = ""; }; 61AAE84F0A321AF700D8810D /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Sparkle.strings; sourceTree = ""; }; 61AAE8590A321B0400D8810D /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Sparkle.strings; sourceTree = ""; }; 61AAE8710A321F7700D8810D /* nl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Sparkle.strings; sourceTree = ""; }; 61B078CC15A5FB6100600039 /* SUCodeSigningVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUCodeSigningVerifier.h; sourceTree = ""; }; 61B078CD15A5FB6100600039 /* SUCodeSigningVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUCodeSigningVerifier.m; sourceTree = ""; }; 61B5F8E309C4CE3C00B25A18 /* SUUpdater.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SUUpdater.h; sourceTree = ""; }; 61B5F8E409C4CE3C00B25A18 /* SUUpdater.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SUUpdater.m; sourceTree = ""; }; 61B5F8F609C4CEB300B25A18 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 61B5F90209C4CEE200B25A18 /* Sparkle Test App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sparkle Test App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 61B5F90409C4CEE200B25A18 /* TestApplication-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestApplication-Info.plist"; sourceTree = ""; }; 61B5F92409C4CFC900B25A18 /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 61B5F92B09C4CFD800B25A18 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 61B5F92D09C4CFD800B25A18 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 61B5FB9409C4F04600B25A18 /* SUAppcast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUAppcast.h; sourceTree = ""; }; 61B5FB9509C4F04600B25A18 /* SUAppcast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUAppcast.m; sourceTree = ""; }; 61B5FC3F09C4FD4000B25A18 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 61B5FC5309C5182000B25A18 /* SUAppcastItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUAppcastItem.h; sourceTree = ""; }; 61B5FC5409C5182000B25A18 /* SUAppcastItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUAppcastItem.m; sourceTree = ""; }; 61B5FCA009C5228F00B25A18 /* SUUpdateAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdateAlert.h; sourceTree = ""; }; 61B5FCA109C5228F00B25A18 /* SUUpdateAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdateAlert.m; sourceTree = ""; }; 61B93A390DD02D6400DCD2F8 /* SUUIBasedUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUIBasedUpdateDriver.h; sourceTree = ""; }; 61B93A3A0DD02D6400DCD2F8 /* SUUIBasedUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUIBasedUpdateDriver.m; sourceTree = ""; }; 61B93B250DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUAutomaticUpdateDriver.h; sourceTree = ""; }; 61B93B260DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUAutomaticUpdateDriver.m; sourceTree = ""; }; 61B93C070DD112FF00DCD2F8 /* SUScheduledUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUScheduledUpdateDriver.h; sourceTree = ""; }; 61B93C080DD112FF00DCD2F8 /* SUScheduledUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUScheduledUpdateDriver.m; sourceTree = ""; }; 61BA66CC14BDFA0400D02D86 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Sparkle.strings; sourceTree = ""; }; 61BA66DA14BDFC5500D02D86 /* sl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sl; path = sl.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 61BA66DB14BDFC5500D02D86 /* sl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sl; path = sl.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 61BA66DC14BDFC5500D02D86 /* sl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sl; path = sl.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 61C268090E2DB5D000175E6C /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; 61CFB3280E385186007A1735 /* Sparkle.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sparkle.pch; sourceTree = ""; }; 61E31A80103299500051D188 /* pt_BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_BR; path = pt_BR.lproj/Sparkle.strings; sourceTree = ""; }; 61EF67550E25B58D00F754E0 /* SUHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUHost.m; sourceTree = ""; }; 61EF67580E25C5B400F754E0 /* SUHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUHost.h; sourceTree = ""; }; 61F3AC1215C22D4A00260CA2 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Sparkle.strings; sourceTree = ""; }; 61F3AC1415C22D5900260CA2 /* th */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = th; path = th.lproj/SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 61F3AC1615C22D5900260CA2 /* th */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = th; path = th.lproj/SUUpdateAlert.xib; sourceTree = ""; }; 61F3AC1715C22D5900260CA2 /* th */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = th; path = th.lproj/SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 61F614540E24A12D009F47E7 /* it */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Sparkle.strings; sourceTree = ""; }; 61F83F6F0DBFE137006FDD30 /* SUBasicUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBasicUpdateDriver.h; sourceTree = ""; }; 61F83F700DBFE137006FDD30 /* SUBasicUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBasicUpdateDriver.m; sourceTree = ""; }; 720B16421C66433D006985FB /* UITests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "UITests-Info.plist"; path = "UITests/UITests-Info.plist"; sourceTree = SOURCE_ROOT; }; 720B16431C66433D006985FB /* SUTestApplicationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SUTestApplicationTest.swift; path = UITests/SUTestApplicationTest.swift; sourceTree = SOURCE_ROOT; }; 7210C7671B9A9A1500EB90AC /* SUUnarchiverTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SUUnarchiverTest.swift; sourceTree = ""; }; 7223E7611AD1AEFF008E3161 /* sais.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sais.c; sourceTree = ""; }; 7223E7621AD1AEFF008E3161 /* sais.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sais.h; sourceTree = ""; }; 722954B41D04ADAF00ECF9CA /* fileop */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fileop; sourceTree = BUILT_PRODUCTS_DIR; }; 722954B61D04ADAF00ECF9CA /* fileop.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = fileop.m; sourceTree = ""; }; 722954C21D04E66F00ECF9CA /* SUFileOperationConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUFileOperationConstants.h; sourceTree = ""; }; 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUFileOperationConstants.m; sourceTree = ""; }; 722954C81D04E89500ECF9CA /* ConfigFileop.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigFileop.xcconfig; sourceTree = ""; }; 723B252B1CEAB3A600909873 /* bscommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bscommon.c; sourceTree = ""; }; 723B252C1CEAB3A600909873 /* bscommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bscommon.h; sourceTree = ""; }; 7268AC621AD634C200C3E0C1 /* SUBinaryDeltaCreate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaCreate.m; sourceTree = ""; }; 7268AC641AD634E400C3E0C1 /* SUBinaryDeltaCreate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaCreate.h; sourceTree = ""; }; 726B2B5D1C645FC900388755 /* UI Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UI Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 726F2CE31BC9C33D001971A4 /* SUOperatingSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUOperatingSystem.h; sourceTree = ""; }; 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUOperatingSystem.m; sourceTree = ""; }; 7275F9BF1B5F1F2900B1D19E /* SUFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUFileManager.h; sourceTree = ""; }; 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUFileManager.m; sourceTree = ""; }; 729F10FD1C65A9B500DFCCC5 /* ConfigUITest.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigUITest.xcconfig; sourceTree = ""; }; 729F10FE1C65A9B500DFCCC5 /* ConfigUITestCoverage.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigUITestCoverage.xcconfig; sourceTree = ""; }; 729F10FF1C65A9B500DFCCC5 /* ConfigUITestDebug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigUITestDebug.xcconfig; sourceTree = ""; }; 729F11001C65A9B500DFCCC5 /* ConfigUITestRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ConfigUITestRelease.xcconfig; sourceTree = ""; }; 72A4A23F1BB6567D00E7820D /* SUFileManagerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SUFileManagerTest.swift; sourceTree = ""; }; 72AC6B251B9AAC8800F62325 /* SparkleTestCodeSignApp.tar.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = SparkleTestCodeSignApp.tar.gz; sourceTree = ""; }; 72AC6B271B9AAD6700F62325 /* SparkleTestCodeSignApp.tar */ = {isa = PBXFileReference; lastKnownFileType = archive.tar; path = SparkleTestCodeSignApp.tar; sourceTree = ""; }; 72AC6B291B9AAF3A00F62325 /* SparkleTestCodeSignApp.tar.bz2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = SparkleTestCodeSignApp.tar.bz2; sourceTree = ""; }; 72AC6B2B1B9AB0EE00F62325 /* SparkleTestCodeSignApp.tar.xz */ = {isa = PBXFileReference; lastKnownFileType = file; path = SparkleTestCodeSignApp.tar.xz; sourceTree = ""; }; 72AC6B2D1B9B218C00F62325 /* SparkleTestCodeSignApp.dmg */ = {isa = PBXFileReference; lastKnownFileType = file; path = SparkleTestCodeSignApp.dmg; sourceTree = ""; }; 72AFC6121B9A944200F6B565 /* Sparkle Unit Tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Sparkle Unit Tests-Bridging-Header.h"; sourceTree = ""; }; 72E45CF11B640CDD005C701A /* SUTestApplicationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUTestApplicationDelegate.h; sourceTree = ""; }; 72E45CF21B640CDD005C701A /* SUTestApplicationDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUTestApplicationDelegate.m; sourceTree = ""; }; 72E45CF41B640DAE005C701A /* SUUpdateSettingsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdateSettingsWindowController.h; sourceTree = ""; }; 72E45CF51B640DAE005C701A /* SUUpdateSettingsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdateSettingsWindowController.m; sourceTree = ""; }; 72E45CF61B640DAE005C701A /* SUUpdateSettingsWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SUUpdateSettingsWindowController.xib; sourceTree = ""; }; 72E45CFB1B641961005C701A /* sparkletestcast.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = sparkletestcast.xml; sourceTree = ""; }; 72E45CFD1B641D0D005C701A /* test_app_only_dsa_priv_dont_ever_do_this_for_real.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test_app_only_dsa_priv_dont_ever_do_this_for_real.pem; sourceTree = ""; }; 72E45DDC1B65C3BD005C701A /* sign_update */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = sign_update; path = bin/sign_update; sourceTree = SOURCE_ROOT; }; 767B61AA1972D488004E0C3C /* SUGuidedPackageInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUGuidedPackageInstaller.h; sourceTree = ""; }; 767B61AB1972D488004E0C3C /* SUGuidedPackageInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUGuidedPackageInstaller.m; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Sparkle-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Sparkle-Info.plist"; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* Sparkle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Sparkle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A5BF4F1B1BC7668B007A052A /* SUTestWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUTestWebServer.h; sourceTree = ""; }; A5BF4F1C1BC7668B007A052A /* SUTestWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUTestWebServer.m; sourceTree = ""; }; C23E88591BE7AF890050BB73 /* SparkleTestCodeSignApp.enc.dmg */ = {isa = PBXFileReference; lastKnownFileType = file; path = SparkleTestCodeSignApp.enc.dmg; sourceTree = ""; }; F8761EB01ADC5068000C9034 /* SUCodeSigningVerifierTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUCodeSigningVerifierTest.m; sourceTree = ""; }; F8761EB21ADC50EB000C9034 /* SparkleTestCodeSignApp.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = SparkleTestCodeSignApp.zip; sourceTree = ""; }; FA1941CA0D94A70100DD942E /* ConfigFrameworkDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigFrameworkDebug.xcconfig; sourceTree = ""; }; FA1941CB0D94A70100DD942E /* ConfigTestAppDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigTestAppDebug.xcconfig; sourceTree = ""; }; FA1941CC0D94A70100DD942E /* ConfigCommonRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigCommonRelease.xcconfig; sourceTree = ""; }; FA1941CD0D94A70100DD942E /* ConfigTestApp.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigTestApp.xcconfig; sourceTree = ""; }; FA1941CE0D94A70100DD942E /* ConfigRelaunch.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigRelaunch.xcconfig; sourceTree = ""; }; FA1941CF0D94A70100DD942E /* ConfigCommonDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigCommonDebug.xcconfig; sourceTree = ""; }; FA1941D00D94A70100DD942E /* ConfigCommon.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigCommon.xcconfig; sourceTree = ""; }; FA1941D10D94A70100DD942E /* ConfigFramework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigFramework.xcconfig; sourceTree = ""; }; FA1941D20D94A70100DD942E /* ConfigTestAppRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigTestAppRelease.xcconfig; sourceTree = ""; }; FA1941D30D94A70100DD942E /* ConfigRelaunchDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigRelaunchDebug.xcconfig; sourceTree = ""; }; FA1941D40D94A70100DD942E /* ConfigRelaunchRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigRelaunchRelease.xcconfig; sourceTree = ""; }; FA1941D50D94A70100DD942E /* ConfigFrameworkRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigFrameworkRelease.xcconfig; sourceTree = ""; }; FA3AAF391050B273004B3130 /* ConfigUnitTestRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigUnitTestRelease.xcconfig; sourceTree = ""; }; FA3AAF3A1050B273004B3130 /* ConfigUnitTestDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigUnitTestDebug.xcconfig; sourceTree = ""; }; FA3AAF3B1050B273004B3130 /* ConfigUnitTest.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigUnitTest.xcconfig; sourceTree = ""; }; FE5536F517A2C6A7007CB333 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Sparkle.strings; sourceTree = ""; }; FE5536F617A2C6AB007CB333 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Sparkle.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 55C14BB5136EEF1500649790 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14950071195FCE3D00BC5B5B /* AppKit.framework in Frameworks */, 14950070195FCE3A00BC5B5B /* Foundation.framework in Frameworks */, 55C14F9A136F045400649790 /* Security.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 5D06E8CE0FD68C7C005AE3F6 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 5D06E9050FD68D7D005AE3F6 /* Foundation.framework in Frameworks */, 5D06E8FF0FD68D6D005AE3F6 /* libbz2.dylib in Frameworks */, 5D1AF58B0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */, 5D1AF5900FD767AD0065DB48 /* libxml2.dylib in Frameworks */, 5D1AF59A0FD767E50065DB48 /* libz.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 612279D60DB5470200AB99EA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14732BD119610A1200593899 /* AppKit.framework in Frameworks */, 14732BD019610A0D00593899 /* Foundation.framework in Frameworks */, 721CF1AB1AD764EB00D9AC09 /* libbz2.dylib in Frameworks */, 721CF1AA1AD7647000D9AC09 /* libxar.1.dylib in Frameworks */, 14652F8019A9740F00959E44 /* Security.framework in Frameworks */, 61FA52880E2D9EA400EF58AD /* Sparkle.framework in Frameworks */, 14732BD319610A1800593899 /* XCTest.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 61B5F90009C4CEE200B25A18 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14950073195FCE4E00BC5B5B /* AppKit.framework in Frameworks */, 14950072195FCE4B00BC5B5B /* Foundation.framework in Frameworks */, 61B5F90F09C4CF3A00B25A18 /* Sparkle.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 722954B11D04ADAF00ECF9CA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 722954CB1D04EA6900ECF9CA /* Security.framework in Frameworks */, 722954CA1D04EA6000ECF9CA /* AppKit.framework in Frameworks */, 722954C91D04EA5C00ECF9CA /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 726B2B5A1C645FC900388755 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 8DC2EF560486A6940098B216 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1495006E195FCE1100BC5B5B /* AppKit.framework in Frameworks */, 1495006F195FCE1800BC5B5B /* Foundation.framework in Frameworks */, 61177A1F0D1112E900749C97 /* IOKit.framework in Frameworks */, 5D06E8FD0FD68D6B005AE3F6 /* libbz2.dylib in Frameworks */, 5D1AF58A0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */, 5D1AF82B0FD768180065DB48 /* libz.dylib in Frameworks */, 61B5F8F709C4CEB300B25A18 /* Security.framework in Frameworks */, 55C14F32136EFC2400649790 /* SystemConfiguration.framework in Frameworks */, 61B5FC4C09C4FD5E00B25A18 /* WebKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 034768DFFF38A50411DB9C8B /* Products */ = { isa = PBXGroup; children = ( 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */, 55C14BB7136EEF1500649790 /* Autoupdate.app */, 61B5F90209C4CEE200B25A18 /* Sparkle Test App.app */, 612279D90DB5470200AB99EA /* Sparkle Unit Tests.xctest */, 8DC2EF5B0486A6940098B216 /* Sparkle.framework */, 726B2B5D1C645FC900388755 /* UI Tests.xctest */, 722954B41D04ADAF00ECF9CA /* fileop */, ); name = Products; sourceTree = ""; }; 0867D691FE84028FC02AAC07 /* Sparkle */ = { isa = PBXGroup; children = ( 1495006D195FBBBD00BC5B5B /* Sparkle */, 14732BB81960EEB600593899 /* Resources */, 61227A100DB5484000AB99EA /* Tests */, 61B5F91D09C4CF7F00B25A18 /* Test Application */, 14732BB51960ECBA00593899 /* Third Party */, 1420DE391962322200203BB0 /* Documentation */, FA1941C40D94A6EA00DD942E /* Configurations */, 726B2B5E1C645FC900388755 /* UI Tests */, 0867D69AFE84028FC02AAC07 /* Frameworks */, 034768DFFF38A50411DB9C8B /* Products */, ); name = Sparkle; sourceTree = ""; usesTabs = 0; }; 0867D69AFE84028FC02AAC07 /* Frameworks */ = { isa = PBXGroup; children = ( 0867D6A5FE840307C02AAC07 /* AppKit.framework */, 525A278F133D6AE900FD8D70 /* Cocoa.framework */, 0867D69BFE84028FC02AAC07 /* Foundation.framework */, 6117796E0D1112E000749C97 /* IOKit.framework */, 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */, 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */, 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */, 5D1AF5990FD767E50065DB48 /* libz.dylib */, 61B5F8F609C4CEB300B25A18 /* Security.framework */, 55C14F31136EFC2400649790 /* SystemConfiguration.framework */, 61B5FC3F09C4FD4000B25A18 /* WebKit.framework */, 14732BD219610A1800593899 /* XCTest.framework */, ); name = Frameworks; sourceTree = ""; }; 089C1665FE841158C02AAC07 /* Framework Resources */ = { isa = PBXGroup; children = ( 8DC2EF5A0486A6940098B216 /* Sparkle-Info.plist */, 61AAE8220A321A7F00D8810D /* Sparkle.strings */, 55C14BDA136EF20D00649790 /* SUAutomaticUpdateAlert.xib */, 55C14BD8136EF00C00649790 /* SUStatus.xib */, 55C14BF0136EF26100649790 /* SUUpdateAlert.xib */, 55C14C05136EF2C700649790 /* SUUpdatePermissionPrompt.xib */, ); name = "Framework Resources"; sourceTree = ""; }; 1420DE391962322200203BB0 /* Documentation */ = { isa = PBXGroup; children = ( 1420DF4E196247B500203BB0 /* Doxyfile */, 1420DF4D196247B500203BB0 /* build-docs.sh */, ); path = Documentation; sourceTree = ""; }; 14732BB51960ECBA00593899 /* Third Party */ = { isa = PBXGroup; children = ( 14732BB61960ECE800593899 /* bsdiff */, 14732BB71960ECF000593899 /* CocoatechCore */, ); name = "Third Party"; path = Vendor; sourceTree = ""; }; 14732BB61960ECE800593899 /* bsdiff */ = { isa = PBXGroup; children = ( 723B252B1CEAB3A600909873 /* bscommon.c */, 723B252C1CEAB3A600909873 /* bscommon.h */, 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */, 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */, 611142E810FB1BE5009810AA /* bspatch.h */, 7223E7611AD1AEFF008E3161 /* sais.c */, 7223E7621AD1AEFF008E3161 /* sais.h */, ); path = bsdiff; sourceTree = ""; }; 14732BB71960ECF000593899 /* CocoatechCore */ = { isa = PBXGroup; children = ( 610EC1C00CF3914D00AE239E /* NTSynchronousTask.h */, 610EC1BF0CF3914D00AE239E /* NTSynchronousTask.m */, ); path = CocoatechCore; sourceTree = ""; }; 14732BB81960EEB600593899 /* Resources */ = { isa = PBXGroup; children = ( 14732BC21960F3FF00593899 /* bin */, 14732BBA1960EF7100593899 /* CHANGELOG */, 61C268090E2DB5D000175E6C /* LICENSE */, 14732BC11960F3B200593899 /* Makefile */, 147D6D9E1B66DC3C006607AB /* CheckLocalizations.swift */, 1420DF4F196247F900203BB0 /* Images.xcassets */, 14732BBC1960EFB500593899 /* README.markdown */, 14732BB91960EEEE00593899 /* SampleAppcast.xml */, 615AE3CF0D64DC40001CA7BD /* SUModelTranslation.plist */, ); path = Resources; sourceTree = ""; }; 14732BC21960F3FF00593899 /* bin */ = { isa = PBXGroup; children = ( 14732BC31960F3FF00593899 /* generate_keys */, 14732BC41960F3FF00593899 /* sign_update */, ); path = bin; sourceTree = SOURCE_ROOT; }; 1495006D195FBBBD00BC5B5B /* Sparkle */ = { isa = PBXGroup; children = ( 61299B3909CB055000B7442F /* Appcast Support */, 55C14BD5136EEFD000649790 /* Autoupdate */, 089C1665FE841158C02AAC07 /* Framework Resources */, 618FA6DB0DB485440026945C /* Installation */, 61B5F8F309C4CE5900B25A18 /* Other Sources */, 6101354A0DD25B7F0049ACDF /* Unarchiving */, 61F83F6E0DBFE07A006FDD30 /* Update Control */, 61299B3A09CB056100B7442F /* User Interface */, ); path = Sparkle; sourceTree = ""; }; 14958C7019AEBE350061B14F /* Resources */ = { isa = PBXGroup; children = ( 14958C6B19AEBC530061B14F /* signed-test-file.txt */, 72AC6B2D1B9B218C00F62325 /* SparkleTestCodeSignApp.dmg */, C23E88591BE7AF890050BB73 /* SparkleTestCodeSignApp.enc.dmg */, 72AC6B271B9AAD6700F62325 /* SparkleTestCodeSignApp.tar */, 72AC6B291B9AAF3A00F62325 /* SparkleTestCodeSignApp.tar.bz2 */, 72AC6B251B9AAC8800F62325 /* SparkleTestCodeSignApp.tar.gz */, 72AC6B2B1B9AB0EE00F62325 /* SparkleTestCodeSignApp.tar.xz */, F8761EB21ADC50EB000C9034 /* SparkleTestCodeSignApp.zip */, 14958C6C19AEBC610061B14F /* test-pubkey.pem */, 5AF6C74E1AEA46D10014A3AB /* test.sparkle_guided.pkg */, 5AD0FA7E1C73F2E2004BCEFF /* testappcast.xml */, 5F1510A11C96E591006E1629 /* testnamespaces.xml */, ); path = Resources; sourceTree = ""; }; 55C14BD5136EEFD000649790 /* Autoupdate */ = { isa = PBXGroup; children = ( 722954B51D04ADAF00ECF9CA /* fileop */, 55C14BB9136EEF1500649790 /* Autoupdate-Info.plist */, 55C14BD3136EEFCE00649790 /* Autoupdate.m */, ); path = Autoupdate; sourceTree = ""; }; 5D06E8D90FD68C95005AE3F6 /* Binary Delta */ = { isa = PBXGroup; children = ( 5D06E8DF0FD68CC7005AE3F6 /* SUBinaryDeltaApply.h */, 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */, 5D06E8E10FD68CC7005AE3F6 /* SUBinaryDeltaCommon.h */, 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */, 7268AC641AD634E400C3E0C1 /* SUBinaryDeltaCreate.h */, 7268AC621AD634C200C3E0C1 /* SUBinaryDeltaCreate.m */, 5D06E8E30FD68CC7005AE3F6 /* SUBinaryDeltaTool.m */, 5D06E9370FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h */, 5D06E9380FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m */, ); name = "Binary Delta"; sourceTree = ""; }; 6101354A0DD25B7F0049ACDF /* Unarchiving */ = { isa = PBXGroup; children = ( 5D06E8D90FD68C95005AE3F6 /* Binary Delta */, 6102FE480E07803800F85D09 /* SUDiskImageUnarchiver.h */, 6102FE490E07803800F85D09 /* SUDiskImageUnarchiver.m */, 6102FE440E077FCE00F85D09 /* SUPipedUnarchiver.h */, 6129C0B90E0B79810062CE76 /* SUPipedUnarchiver.m */, 61299A8B09CA790200B7442F /* SUUnarchiver.h */, 61299A8C09CA790200B7442F /* SUUnarchiver.m */, 6102FE590E08C7EC00F85D09 /* SUUnarchiver_Private.h */, ); name = Unarchiving; sourceTree = ""; }; 61227A100DB5484000AB99EA /* Tests */ = { isa = PBXGroup; children = ( 14958C7019AEBE350061B14F /* Resources */, 72AFC6121B9A944200F6B565 /* Sparkle Unit Tests-Bridging-Header.h */, 612279DA0DB5470200AB99EA /* SparkleTests-Info.plist */, 5AA4DCD01C73E5510078F128 /* SUAppcastTest.swift */, 142E0E0819A83AAC00E4312B /* SUBinaryDeltaTest.m */, F8761EB01ADC5068000C9034 /* SUCodeSigningVerifierTest.m */, 5AF9DC3B1981DBEE001EA135 /* SUDSAVerifierTest.m */, 72A4A23F1BB6567D00E7820D /* SUFileManagerTest.swift */, 5AF6C74C1AEA40760014A3AB /* SUInstallerTest.m */, 7210C7671B9A9A1500EB90AC /* SUUnarchiverTest.swift */, 14950074195FDF5900BC5B5B /* SUUpdaterTest.m */, 61227A150DB548B800AB99EA /* SUVersionComparisonTest.m */, ); path = Tests; sourceTree = ""; }; 61299B3909CB055000B7442F /* Appcast Support */ = { isa = PBXGroup; children = ( 61B5FB9409C4F04600B25A18 /* SUAppcast.h */, 61B5FB9509C4F04600B25A18 /* SUAppcast.m */, 61B5FC5309C5182000B25A18 /* SUAppcastItem.h */, 61B5FC5409C5182000B25A18 /* SUAppcastItem.m */, 61A225A20D1C4AC000430CCD /* SUStandardVersionComparator.h */, 61A225A30D1C4AC000430CCD /* SUStandardVersionComparator.m */, 61A2279A0D1CEE7600430CCD /* SUSystemProfiler.h */, 61A2279B0D1CEE7600430CCD /* SUSystemProfiler.m */, 61A2259C0D1C495D00430CCD /* SUVersionComparisonProtocol.h */, 3772FEA813DE0B6B00F79537 /* SUVersionDisplayProtocol.h */, ); name = "Appcast Support"; sourceTree = ""; }; 61299B3A09CB056100B7442F /* User Interface */ = { isa = PBXGroup; children = ( 6120721009CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h */, 6120721109CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m */, 6196CFE309C71ADE000DC222 /* SUStatusController.h */, 6196CFE409C71ADE000DC222 /* SUStatusController.m */, 61B5FCA009C5228F00B25A18 /* SUUpdateAlert.h */, 61B5FCA109C5228F00B25A18 /* SUUpdateAlert.m */, 612DCBAD0D488BC60015DBEA /* SUUpdatePermissionPrompt.h */, 612DCBAE0D488BC60015DBEA /* SUUpdatePermissionPrompt.m */, 61180BC80D64138900B4E0D1 /* SUWindowController.h */, 61180BC90D64138900B4E0D1 /* SUWindowController.m */, ); name = "User Interface"; sourceTree = ""; }; 618FA6DB0DB485440026945C /* Installation */ = { isa = PBXGroup; children = ( 7275F9BF1B5F1F2900B1D19E /* SUFileManager.h */, 7275F9C01B5F1F2900B1D19E /* SUFileManager.m */, 767B61AA1972D488004E0C3C /* SUGuidedPackageInstaller.h */, 767B61AB1972D488004E0C3C /* SUGuidedPackageInstaller.m */, 618FA4FF0DAE88B40026945C /* SUInstaller.h */, 618FA5000DAE88B40026945C /* SUInstaller.m */, 618FA5200DAE8E8A0026945C /* SUPackageInstaller.h */, 618FA5210DAE8E8A0026945C /* SUPackageInstaller.m */, 618FA5030DAE8AB80026945C /* SUPlainInstaller.h */, 618FA5040DAE8AB80026945C /* SUPlainInstaller.m */, ); name = Installation; sourceTree = ""; }; 61B5F8F309C4CE5900B25A18 /* Other Sources */ = { isa = PBXGroup; children = ( 61299B3509CB04E000B7442F /* Sparkle.h */, 61CFB3280E385186007A1735 /* Sparkle.pch */, 61299A5B09CA6D4500B7442F /* SUConstants.h */, 61299A5F09CA6EB100B7442F /* SUConstants.m */, 55E6F33219EC9F6C00005E76 /* SUErrors.h */, 14652F8319A9759F00959E44 /* SUExport.h */, 61EF67580E25C5B400F754E0 /* SUHost.h */, 61EF67550E25B58D00F754E0 /* SUHost.m */, 55C14F04136EF6DB00649790 /* SULog.h */, 55C14F05136EF6DB00649790 /* SULog.m */, 726F2CE31BC9C33D001971A4 /* SUOperatingSystem.h */, 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */, ); includeInIndex = 1; name = "Other Sources"; sourceTree = ""; }; 61B5F91D09C4CF7F00B25A18 /* Test Application */ = { isa = PBXGroup; children = ( 72E45DDC1B65C3BD005C701A /* sign_update */, 61B5F92A09C4CFD800B25A18 /* InfoPlist.strings */, 61B5F92409C4CFC900B25A18 /* main.m */, 61B5F92C09C4CFD800B25A18 /* MainMenu.xib */, 72E45CFB1B641961005C701A /* sparkletestcast.xml */, 72E45CF11B640CDD005C701A /* SUTestApplicationDelegate.h */, 72E45CF21B640CDD005C701A /* SUTestApplicationDelegate.m */, A5BF4F1B1BC7668B007A052A /* SUTestWebServer.h */, A5BF4F1C1BC7668B007A052A /* SUTestWebServer.m */, 72E45CF41B640DAE005C701A /* SUUpdateSettingsWindowController.h */, 72E45CF51B640DAE005C701A /* SUUpdateSettingsWindowController.m */, 72E45CF61B640DAE005C701A /* SUUpdateSettingsWindowController.xib */, 72E45CFD1B641D0D005C701A /* test_app_only_dsa_priv_dont_ever_do_this_for_real.pem */, 14732BBF1960F0AC00593899 /* test_app_only_dsa_pub.pem */, 61B5F90409C4CEE200B25A18 /* TestApplication-Info.plist */, ); name = "Test Application"; path = TestApplication; sourceTree = ""; }; 61CFB2C10E384958007A1735 /* Support */ = { isa = PBXGroup; children = ( 61B078CC15A5FB6100600039 /* SUCodeSigningVerifier.h */, 61B078CD15A5FB6100600039 /* SUCodeSigningVerifier.m */, 61299A2D09CA2DAB00B7442F /* SUDSAVerifier.h */, 61299A2E09CA2DAB00B7442F /* SUDSAVerifier.m */, ); name = Support; sourceTree = ""; }; 61CFB2C20E38496B007A1735 /* Drivers */ = { isa = PBXGroup; children = ( 61B93B250DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.h */, 61B93B260DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.m */, 61F83F6F0DBFE137006FDD30 /* SUBasicUpdateDriver.h */, 61F83F700DBFE137006FDD30 /* SUBasicUpdateDriver.m */, 610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */, 6101347A0DD2541A0049ACDF /* SUProbingUpdateDriver.m */, 61B93C070DD112FF00DCD2F8 /* SUScheduledUpdateDriver.h */, 61B93C080DD112FF00DCD2F8 /* SUScheduledUpdateDriver.m */, 61B93A390DD02D6400DCD2F8 /* SUUIBasedUpdateDriver.h */, 61B93A3A0DD02D6400DCD2F8 /* SUUIBasedUpdateDriver.m */, 610134710DD250470049ACDF /* SUUpdateDriver.h */, 610134720DD250470049ACDF /* SUUpdateDriver.m */, 61A354530DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h */, 61A354540DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.m */, ); name = Drivers; sourceTree = ""; }; 61F83F6E0DBFE07A006FDD30 /* Update Control */ = { isa = PBXGroup; children = ( 61CFB2C20E38496B007A1735 /* Drivers */, 61CFB2C10E384958007A1735 /* Support */, 61B5F8E309C4CE3C00B25A18 /* SUUpdater.h */, 61B5F8E409C4CE3C00B25A18 /* SUUpdater.m */, 6158A1C4137904B300487EC1 /* SUUpdater_Private.h */, ); name = "Update Control"; sourceTree = ""; }; 722954B51D04ADAF00ECF9CA /* fileop */ = { isa = PBXGroup; children = ( 722954B61D04ADAF00ECF9CA /* fileop.m */, 722954C21D04E66F00ECF9CA /* SUFileOperationConstants.h */, 722954C31D04E66F00ECF9CA /* SUFileOperationConstants.m */, ); name = fileop; path = ../../fileop; sourceTree = ""; }; 726B2B5E1C645FC900388755 /* UI Tests */ = { isa = PBXGroup; children = ( 720B16431C66433D006985FB /* SUTestApplicationTest.swift */, 720B16421C66433D006985FB /* UITests-Info.plist */, ); path = "UI Tests"; sourceTree = ""; }; FA1941C40D94A6EA00DD942E /* Configurations */ = { isa = PBXGroup; children = ( 14732BB1195FF6B700593899 /* .clang-format */, 5D06E8F10FD68D21005AE3F6 /* ConfigBinaryDelta.xcconfig */, 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */, 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */, FA1941D00D94A70100DD942E /* ConfigCommon.xcconfig */, 149B78631B7D3A0C00D7D62C /* ConfigCommonCoverage.xcconfig */, FA1941CF0D94A70100DD942E /* ConfigCommonDebug.xcconfig */, FA1941CC0D94A70100DD942E /* ConfigCommonRelease.xcconfig */, FA1941D10D94A70100DD942E /* ConfigFramework.xcconfig */, FA1941CA0D94A70100DD942E /* ConfigFrameworkDebug.xcconfig */, FA1941D50D94A70100DD942E /* ConfigFrameworkRelease.xcconfig */, FA1941CE0D94A70100DD942E /* ConfigRelaunch.xcconfig */, FA1941D30D94A70100DD942E /* ConfigRelaunchDebug.xcconfig */, FA1941D40D94A70100DD942E /* ConfigRelaunchRelease.xcconfig */, 722954C81D04E89500ECF9CA /* ConfigFileop.xcconfig */, FA1941CD0D94A70100DD942E /* ConfigTestApp.xcconfig */, FA1941CB0D94A70100DD942E /* ConfigTestAppDebug.xcconfig */, FA1941D20D94A70100DD942E /* ConfigTestAppRelease.xcconfig */, FA3AAF3B1050B273004B3130 /* ConfigUnitTest.xcconfig */, 149B78641B7D3A4800D7D62C /* ConfigUnitTestCoverage.xcconfig */, FA3AAF3A1050B273004B3130 /* ConfigUnitTestDebug.xcconfig */, FA3AAF391050B273004B3130 /* ConfigUnitTestRelease.xcconfig */, 729F10FD1C65A9B500DFCCC5 /* ConfigUITest.xcconfig */, 729F10FE1C65A9B500DFCCC5 /* ConfigUITestCoverage.xcconfig */, 729F10FF1C65A9B500DFCCC5 /* ConfigUITestDebug.xcconfig */, 729F11001C65A9B500DFCCC5 /* ConfigUITestRelease.xcconfig */, 14732BC91960F70A00593899 /* make-release-package.sh */, 14652F7919A93E5F00959E44 /* set-git-version-info.sh */, 146EC84E19A68CF8004A50C5 /* Sparkle.podspec */, ); path = Configurations; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 8DC2EF500486A6940098B216 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 611142E910FB1BE5009810AA /* bspatch.h in Headers */, 6160E7E10D3B4A8800E9CD71 /* NTSynchronousTask.h in Headers */, 61299B3609CB04E000B7442F /* Sparkle.h in Headers */, 61CFB3290E385186007A1735 /* Sparkle.pch in Headers */, 61B5FC0D09C4FC8200B25A18 /* SUAppcast.h in Headers */, 61B5FC7009C51F4A00B25A18 /* SUAppcastItem.h in Headers */, 6120721209CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.h in Headers */, 61B93B270DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.h in Headers */, 61F83F740DBFE141006FDD30 /* SUBasicUpdateDriver.h in Headers */, 5D06E9390FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h in Headers */, 61B078CE15A5FB6100600039 /* SUCodeSigningVerifier.h in Headers */, 61299A5C09CA6D4500B7442F /* SUConstants.h in Headers */, 6102FE4A0E07803800F85D09 /* SUDiskImageUnarchiver.h in Headers */, 61299A2F09CA2DAB00B7442F /* SUDSAVerifier.h in Headers */, 55E6F33319EC9F6C00005E76 /* SUErrors.h in Headers */, 14652F8419A978C200959E44 /* SUExport.h in Headers */, 7275F9C11B5F1F2900B1D19E /* SUFileManager.h in Headers */, 767B61AC1972D488004E0C3C /* SUGuidedPackageInstaller.h in Headers */, 61EF67590E25C5B400F754E0 /* SUHost.h in Headers */, 723B25301CEAB3A600909873 /* bscommon.h in Headers */, 618FA5010DAE88B40026945C /* SUInstaller.h in Headers */, 55C14F06136EF6DB00649790 /* SULog.h in Headers */, 726F2CE51BC9C33D001971A4 /* SUOperatingSystem.h in Headers */, 618FA5220DAE8E8A0026945C /* SUPackageInstaller.h in Headers */, 6102FE460E077FCE00F85D09 /* SUPipedUnarchiver.h in Headers */, 618FA5050DAE8AB80026945C /* SUPlainInstaller.h in Headers */, 6101347B0DD2541A0049ACDF /* SUProbingUpdateDriver.h in Headers */, 61B93C090DD112FF00DCD2F8 /* SUScheduledUpdateDriver.h in Headers */, 61A225A40D1C4AC000430CCD /* SUStandardVersionComparator.h in Headers */, 6196CFF909C72148000DC222 /* SUStatusController.h in Headers */, 61A2279C0D1CEE7600430CCD /* SUSystemProfiler.h in Headers */, 61B93A3C0DD02D7000DCD2F8 /* SUUIBasedUpdateDriver.h in Headers */, 61299A8D09CA790200B7442F /* SUUnarchiver.h in Headers */, 6102FE5B0E08C7EC00F85D09 /* SUUnarchiver_Private.h in Headers */, 61B5FCDF09C52A9F00B25A18 /* SUUpdateAlert.h in Headers */, 610134730DD250470049ACDF /* SUUpdateDriver.h in Headers */, 612DCBAF0D488BC60015DBEA /* SUUpdatePermissionPrompt.h in Headers */, 61B5F8ED09C4CE3C00B25A18 /* SUUpdater.h in Headers */, 6158A1C5137904B300487EC1 /* SUUpdater_Private.h in Headers */, 61A354550DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.h in Headers */, 61A2259E0D1C495D00430CCD /* SUVersionComparisonProtocol.h in Headers */, 3772FEA913DE0B6B00F79537 /* SUVersionDisplayProtocol.h in Headers */, 722954C41D04E66F00ECF9CA /* SUFileOperationConstants.h in Headers */, 61180BCA0D64138900B4E0D1 /* SUWindowController.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXLegacyTarget section */ 1420DF491962329200203BB0 /* Documentation */ = { isa = PBXLegacyTarget; buildArgumentsString = "\"$(SRCROOT)/Documentation/build-docs.sh\" $(ACTION)"; buildConfigurationList = 1420DF4A1962329200203BB0 /* Build configuration list for PBXLegacyTarget "Documentation" */; buildPhases = ( ); buildToolPath = bash; buildWorkingDirectory = ""; dependencies = ( ); name = Documentation; passBuildSettingsInEnvironment = 1; productName = Documentation; }; 14732BC51960F69300593899 /* Distribution */ = { isa = PBXLegacyTarget; buildArgumentsString = "$(SRCROOT)/Configurations/make-release-package.sh $(ACTION)"; buildConfigurationList = 14732BC61960F69300593899 /* Build configuration list for PBXLegacyTarget "Distribution" */; buildPhases = ( ); buildToolPath = bash; buildWorkingDirectory = ""; dependencies = ( 14732BCB1960F73500593899 /* PBXTargetDependency */, 1454BA1619637EDB00344E57 /* PBXTargetDependency */, 14732BCF1960F73500593899 /* PBXTargetDependency */, 1454BA1819637EE900344E57 /* PBXTargetDependency */, ); name = Distribution; passBuildSettingsInEnvironment = 1; productName = Distribution; }; /* End PBXLegacyTarget section */ /* Begin PBXNativeTarget section */ 55C14BB6136EEF1500649790 /* Autoupdate */ = { isa = PBXNativeTarget; buildConfigurationList = 55C14BBE136EEF1500649790 /* Build configuration list for PBXNativeTarget "Autoupdate" */; buildPhases = ( 55C14BB3136EEF1500649790 /* Resources */, 55C14BB4136EEF1500649790 /* Sources */, 55C14BB5136EEF1500649790 /* Frameworks */, 14652F7B19A945D600959E44 /* Run Script: Set Git Version Info */, 722954C01D04D9D600ECF9CA /* Copy fileop */, ); buildRules = ( ); dependencies = ( 722954BF1D04D9CB00ECF9CA /* PBXTargetDependency */, ); name = Autoupdate; productName = Autoupdate; productReference = 55C14BB7136EEF1500649790 /* Autoupdate.app */; productType = "com.apple.product-type.application"; }; 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */ = { isa = PBXNativeTarget; buildConfigurationList = 5D06E8DA0FD68C95005AE3F6 /* Build configuration list for PBXNativeTarget "BinaryDelta" */; buildPhases = ( 5D06E8CD0FD68C7C005AE3F6 /* Sources */, 5D06E8CE0FD68C7C005AE3F6 /* Frameworks */, ); buildRules = ( ); dependencies = ( 5D06E8D60FD68C86005AE3F6 /* PBXTargetDependency */, ); name = BinaryDelta; productName = BinaryDelta; productReference = 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */; productType = "com.apple.product-type.tool"; }; 612279D80DB5470200AB99EA /* Sparkle Unit Tests */ = { isa = PBXNativeTarget; buildConfigurationList = 612279DD0DB5470300AB99EA /* Build configuration list for PBXNativeTarget "Sparkle Unit Tests" */; buildPhases = ( 612279D50DB5470200AB99EA /* Sources */, 612279D60DB5470200AB99EA /* Frameworks */, 14958C6D19AEBC890061B14F /* Resources */, 142E0E0119A6A13300E4312B /* Copy Files */, ); buildRules = ( ); dependencies = ( 61FA528D0E2D9EB200EF58AD /* PBXTargetDependency */, ); name = "Sparkle Unit Tests"; productName = "Sparkle Unit Tests"; productReference = 612279D90DB5470200AB99EA /* Sparkle Unit Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 61B5F90109C4CEE200B25A18 /* Sparkle Test App */ = { isa = PBXNativeTarget; buildConfigurationList = 61B5F90509C4CEE300B25A18 /* Build configuration list for PBXNativeTarget "Sparkle Test App" */; buildPhases = ( 61B5F8FE09C4CEE200B25A18 /* Resources */, 61B5F8FF09C4CEE200B25A18 /* Sources */, 61B5F90009C4CEE200B25A18 /* Frameworks */, 61B5FB4D09C4E9FA00B25A18 /* Copy Frameworks */, ); buildRules = ( ); dependencies = ( 61B5F91C09C4CF7200B25A18 /* PBXTargetDependency */, ); name = "Sparkle Test App"; productName = "Test Application"; productReference = 61B5F90209C4CEE200B25A18 /* Sparkle Test App.app */; productType = "com.apple.product-type.application"; }; 722954B31D04ADAF00ECF9CA /* fileop */ = { isa = PBXNativeTarget; buildConfigurationList = 722954BB1D04ADAF00ECF9CA /* Build configuration list for PBXNativeTarget "fileop" */; buildPhases = ( 722954B01D04ADAF00ECF9CA /* Sources */, 722954B11D04ADAF00ECF9CA /* Frameworks */, 722954B21D04ADAF00ECF9CA /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = fileop; productName = fileop; productReference = 722954B41D04ADAF00ECF9CA /* fileop */; productType = "com.apple.product-type.tool"; }; 726B2B5C1C645FC900388755 /* UI Tests */ = { isa = PBXNativeTarget; buildConfigurationList = 726B2B671C645FC900388755 /* Build configuration list for PBXNativeTarget "UI Tests" */; buildPhases = ( 726B2B591C645FC900388755 /* Sources */, 726B2B5A1C645FC900388755 /* Frameworks */, 726B2B5B1C645FC900388755 /* Resources */, ); buildRules = ( ); dependencies = ( 726B2B631C645FC900388755 /* PBXTargetDependency */, ); name = "UI Tests"; productName = "UI Tests"; productReference = 726B2B5D1C645FC900388755 /* UI Tests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; 8DC2EF4F0486A6940098B216 /* Sparkle */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Sparkle" */; buildPhases = ( 8DC2EF500486A6940098B216 /* Headers */, 8DC2EF520486A6940098B216 /* Resources */, 142E0E0319A6A24100E4312B /* CopyFiles */, 8DC2EF540486A6940098B216 /* Sources */, 8DC2EF560486A6940098B216 /* Frameworks */, 6131B1910DDCDE32005215F0 /* Run Script: Set Git Version Info */, 6195D4B40E40505A00D41A50 /* Run Script: Link fr_CA to fr */, 61E31A85103299750051D188 /* Run Script: Link pt to pt_BR */, ); buildRules = ( ); dependencies = ( 55C14F97136F044100649790 /* PBXTargetDependency */, ); name = Sparkle; productInstallPath = "$(HOME)/Library/Frameworks"; productName = Sparkle; productReference = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { CLASSPREFIX = SU; LastSwiftUpdateCheck = 0720; LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Sparkle Project"; TargetAttributes = { 612279D80DB5470200AB99EA = { TestTargetID = 8DC2EF4F0486A6940098B216; }; 722954B31D04ADAF00ECF9CA = { CreatedOnToolsVersion = 7.3.1; }; 726B2B5C1C645FC900388755 = { CreatedOnToolsVersion = 7.2.1; TestTargetID = 61B5F90109C4CEE200B25A18; }; }; }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Sparkle" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, Japanese, French, German, Italian, zh_TW, en, ca, cs, cy, da, de, es, fi, fr, he, hu, id, is, it, ja, ko, nl, no, pl, ru, sk, sv, th, tr, zh_CN, fr_ca, pt_BR, pt_PT, pt, ro, sl, uk, ar, nb, el, "zh-Hans", "zh-Hant", "fr-CA", "pt-PT", "pt-BR", ); mainGroup = 0867D691FE84028FC02AAC07 /* Sparkle */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 8DC2EF4F0486A6940098B216 /* Sparkle */, 61B5F90109C4CEE200B25A18 /* Sparkle Test App */, 612279D80DB5470200AB99EA /* Sparkle Unit Tests */, 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */, 55C14BB6136EEF1500649790 /* Autoupdate */, 14732BC51960F69300593899 /* Distribution */, 1420DF491962329200203BB0 /* Documentation */, 1495005F195FB89400BC5B5B /* All */, 726B2B5C1C645FC900388755 /* UI Tests */, 722954B31D04ADAF00ECF9CA /* fileop */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 14958C6D19AEBC890061B14F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 72E641801D08D754000404CA /* fileop in Resources */, 14958C6E19AEBC950061B14F /* signed-test-file.txt in Resources */, 5F1510A21C96E591006E1629 /* testnamespaces.xml in Resources */, 72AC6B2E1B9B218C00F62325 /* SparkleTestCodeSignApp.dmg in Resources */, C23E885B1BE7B24F0050BB73 /* SparkleTestCodeSignApp.enc.dmg in Resources */, 72AC6B281B9AAD6700F62325 /* SparkleTestCodeSignApp.tar in Resources */, 72AC6B2A1B9AAF3A00F62325 /* SparkleTestCodeSignApp.tar.bz2 in Resources */, 72AC6B261B9AAC8800F62325 /* SparkleTestCodeSignApp.tar.gz in Resources */, 72AC6B2C1B9AB0EE00F62325 /* SparkleTestCodeSignApp.tar.xz in Resources */, F8761EB31ADC50EB000C9034 /* SparkleTestCodeSignApp.zip in Resources */, 14958C6F19AEBC980061B14F /* test-pubkey.pem in Resources */, 5AF6C74F1AEA46D10014A3AB /* test.sparkle_guided.pkg in Resources */, 5AD0FA7F1C73F2E2004BCEFF /* testappcast.xml in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 55C14BB3136EEF1500649790 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 1420DF51196247F900203BB0 /* Images.xcassets in Resources */, 55C14FC7136F05E100649790 /* Sparkle.strings in Resources */, 55C14BD9136EF00C00649790 /* SUStatus.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 61B5F8FE09C4CEE200B25A18 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 1420DF50196247F900203BB0 /* Images.xcassets in Resources */, 61B5F92E09C4CFD800B25A18 /* InfoPlist.strings in Resources */, 61B5F92F09C4CFD800B25A18 /* MainMenu.xib in Resources */, 72E45DDD1B65C3BD005C701A /* sign_update in Resources */, 72E45CFC1B641961005C701A /* sparkletestcast.xml in Resources */, 72E45CF81B640DAE005C701A /* SUUpdateSettingsWindowController.xib in Resources */, 72E45CFE1B641D0D005C701A /* test_app_only_dsa_priv_dont_ever_do_this_for_real.pem in Resources */, 14732BC01960F2C200593899 /* test_app_only_dsa_pub.pem in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 726B2B5B1C645FC900388755 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 720B16441C66433D006985FB /* UITests-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 8DC2EF520486A6940098B216 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 61AAE8280A321A7F00D8810D /* Sparkle.strings in Resources */, 55C14BEE136EF20D00649790 /* SUAutomaticUpdateAlert.xib in Resources */, 615AE3D00D64DC40001CA7BD /* SUModelTranslation.plist in Resources */, 55C14BEF136EF21700649790 /* SUStatus.xib in Resources */, 55C14C04136EF26100649790 /* SUUpdateAlert.xib in Resources */, 55C14C19136EF2C700649790 /* SUUpdatePermissionPrompt.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 14652F7B19A945D600959E44 /* Run Script: Set Git Version Info */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Run Script: Set Git Version Info"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"$SRCROOT/Configurations/set-git-version-info.sh\""; showEnvVarsInLog = 0; }; 6131B1910DDCDE32005215F0 /* Run Script: Set Git Version Info */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 12; files = ( ); inputPaths = ( ); name = "Run Script: Set Git Version Info"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"$SRCROOT/Configurations/set-git-version-info.sh\""; showEnvVarsInLog = 0; }; 6195D4B40E40505A00D41A50 /* Run Script: Link fr_CA to fr */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/fr.lproj", ); name = "Run Script: Link fr_CA to fr"; outputPaths = ( "$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/fr_CA.lproj", ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/usr/bin/env sh"; shellScript = "ln -sfh \"fr.lproj\" \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/fr_CA.lproj\""; showEnvVarsInLog = 0; }; 61E31A85103299750051D188 /* Run Script: Link pt to pt_BR */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/pt_BR.lproj", ); name = "Run Script: Link pt to pt_BR"; outputPaths = ( "$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/pt.lproj", ); runOnlyForDeploymentPostprocessing = 0; shellPath = "/usr/bin/env sh"; shellScript = "ln -sfh \"pt_BR.lproj\" \"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/pt.lproj\""; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 55C14BB4136EEF1500649790 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 55C14BD4136EEFCE00649790 /* Autoupdate.m in Sources */, F8761EB61ADC5E7A000C9034 /* SUCodeSigningVerifier.m in Sources */, 55C14F00136EF6B700649790 /* SUConstants.m in Sources */, 7275F9C31B5F1F2900B1D19E /* SUFileManager.m in Sources */, 767B61AE1972D488004E0C3C /* SUGuidedPackageInstaller.m in Sources */, 55C14F0C136EF6EA00649790 /* SUHost.m in Sources */, 55C14F0D136EF6F200649790 /* SUInstaller.m in Sources */, 55C14F08136EF6DB00649790 /* SULog.m in Sources */, 726F2CE71BC9C3E2001971A4 /* SUOperatingSystem.m in Sources */, 55C14F24136EF86F00649790 /* SUPackageInstaller.m in Sources */, 55C14F21136EF84D00649790 /* SUPlainInstaller.m in Sources */, 722954C61D04E66F00ECF9CA /* SUFileOperationConstants.m in Sources */, 55C14F22136EF86000649790 /* SUStandardVersionComparator.m in Sources */, 55C14F20136EF84300649790 /* SUStatusController.m in Sources */, 55C14F23136EF86700649790 /* SUSystemProfiler.m in Sources */, 55C14F2A136EF9A900649790 /* SUWindowController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 5D06E8CD0FD68C7C005AE3F6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 72544FFC1D0991A4000CFB2C /* SUFileOperationConstants.m in Sources */, 5D06E8E90FD68CDB005AE3F6 /* bsdiff.c in Sources */, 14652F7E19A9728A00959E44 /* bspatch.c in Sources */, 7223E7631AD1AEFF008E3161 /* sais.c in Sources */, 723B252F1CEAB3A600909873 /* bscommon.c in Sources */, 14652F7D19A9726700959E44 /* SUBinaryDeltaApply.m in Sources */, 14652F7C19A9725300959E44 /* SUBinaryDeltaCommon.m in Sources */, 7268AC631AD634C200C3E0C1 /* SUBinaryDeltaCreate.m in Sources */, 5D06E8EA0FD68CDB005AE3F6 /* SUBinaryDeltaTool.m in Sources */, 726F2CE81BC9C48F001971A4 /* SUConstants.m in Sources */, 726F2CDE1BC9BE5B001971A4 /* SUFileManager.m in Sources */, 726F2CE91BC9C499001971A4 /* SUOperatingSystem.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 612279D50DB5470200AB99EA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 721CF1A71AD7643600D9AC09 /* bsdiff.c in Sources */, 721CF1A81AD7644100D9AC09 /* bspatch.c in Sources */, 721CF1A91AD7644C00D9AC09 /* sais.c in Sources */, 5A4094481C74EA5200983BE0 /* SUAppcastTest.swift in Sources */, 72D4DAA11AD7632900B211E2 /* SUBinaryDeltaCreate.m in Sources */, 723B252E1CEAB3A600909873 /* bscommon.c in Sources */, 142E0E0919A83AAC00E4312B /* SUBinaryDeltaTest.m in Sources */, F8761EB11ADC5068000C9034 /* SUCodeSigningVerifierTest.m in Sources */, 5AF9DC3C1981DBEE001EA135 /* SUDSAVerifierTest.m in Sources */, 72A4A2401BB6567D00E7820D /* SUFileManagerTest.swift in Sources */, 5AF6C7541AEA49840014A3AB /* SUInstallerTest.m in Sources */, 14652F8219A9746000959E44 /* SULog.m in Sources */, 7210C7681B9A9A1500EB90AC /* SUUnarchiverTest.swift in Sources */, 5AE459001C34118500E3BB47 /* SUUpdaterTest.m in Sources */, 5AE459021C34118500E3BB47 /* SUVersionComparisonTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 61B5F8FF09C4CEE200B25A18 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 61B5F93009C4CFDC00B25A18 /* main.m in Sources */, 726F2CEB1BC9C733001971A4 /* SUConstants.m in Sources */, 5A6D31EE1BF53245009C5157 /* SUFileManager.m in Sources */, 721BC2111D1DFF05002BC71E /* SUFileOperationConstants.m in Sources */, 5A6D31EF1BF5325F009C5157 /* SUOperatingSystem.m in Sources */, 72E45CF31B640CDD005C701A /* SUTestApplicationDelegate.m in Sources */, A5BF4F1D1BC7668B007A052A /* SUTestWebServer.m in Sources */, 72E45CF71B640DAE005C701A /* SUUpdateSettingsWindowController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 722954B01D04ADAF00ECF9CA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 722954CC1D04EA7000ECF9CA /* SUOperatingSystem.m in Sources */, 722954BD1D04AE3800ECF9CA /* SUConstants.m in Sources */, 722954BC1D04AE1100ECF9CA /* SUFileManager.m in Sources */, 722954B71D04ADAF00ECF9CA /* fileop.m in Sources */, 722954C71D04E66F00ECF9CA /* SUFileOperationConstants.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 726B2B591C645FC900388755 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 720B16451C66433D006985FB /* SUTestApplicationTest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 8DC2EF540486A6940098B216 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 5D06E8EB0FD68CE4005AE3F6 /* bspatch.c in Sources */, 722954C51D04E66F00ECF9CA /* SUFileOperationConstants.m in Sources */, 610EC1E00CF3A5FE00AE239E /* NTSynchronousTask.m in Sources */, 61B5FBB709C4FAFF00B25A18 /* SUAppcast.m in Sources */, 61B5FC6F09C51F4900B25A18 /* SUAppcastItem.m in Sources */, 6120721309CC5C4B007FE0F6 /* SUAutomaticUpdateAlert.m in Sources */, 61B93B280DD0FDD300DCD2F8 /* SUAutomaticUpdateDriver.m in Sources */, 61F83F720DBFE140006FDD30 /* SUBasicUpdateDriver.m in Sources */, 5D06E8EC0FD68CE4005AE3F6 /* SUBinaryDeltaApply.m in Sources */, 5D06E8ED0FD68CE4005AE3F6 /* SUBinaryDeltaCommon.m in Sources */, 5D06E93A0FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m in Sources */, 61B078CF15A5FB6100600039 /* SUCodeSigningVerifier.m in Sources */, 61299A6009CA6EB100B7442F /* SUConstants.m in Sources */, 6102FE4B0E07803800F85D09 /* SUDiskImageUnarchiver.m in Sources */, 61299A3009CA2DAB00B7442F /* SUDSAVerifier.m in Sources */, 7275F9C21B5F1F2900B1D19E /* SUFileManager.m in Sources */, 767B61AD1972D488004E0C3C /* SUGuidedPackageInstaller.m in Sources */, 61EF67560E25B58D00F754E0 /* SUHost.m in Sources */, 618FA5020DAE88B40026945C /* SUInstaller.m in Sources */, 55C14F07136EF6DB00649790 /* SULog.m in Sources */, 723B252D1CEAB3A600909873 /* bscommon.c in Sources */, 726F2CE61BC9C33D001971A4 /* SUOperatingSystem.m in Sources */, 618FA5230DAE8E8A0026945C /* SUPackageInstaller.m in Sources */, 61D85D6D0E10B2ED00F9B4A9 /* SUPipedUnarchiver.m in Sources */, 618FA5060DAE8AB80026945C /* SUPlainInstaller.m in Sources */, 6101347C0DD2541A0049ACDF /* SUProbingUpdateDriver.m in Sources */, 61B93C0A0DD112FF00DCD2F8 /* SUScheduledUpdateDriver.m in Sources */, 61A225A50D1C4AC000430CCD /* SUStandardVersionComparator.m in Sources */, 6196CFFA09C72149000DC222 /* SUStatusController.m in Sources */, 61A2279D0D1CEE7600430CCD /* SUSystemProfiler.m in Sources */, 61B93A3D0DD02D7000DCD2F8 /* SUUIBasedUpdateDriver.m in Sources */, 61299A8E09CA790200B7442F /* SUUnarchiver.m in Sources */, 61B5FCDE09C52A9F00B25A18 /* SUUpdateAlert.m in Sources */, 610134740DD250470049ACDF /* SUUpdateDriver.m in Sources */, 612DCBB00D488BC60015DBEA /* SUUpdatePermissionPrompt.m in Sources */, 61B5F8EE09C4CE3C00B25A18 /* SUUpdater.m in Sources */, 61A354560DF113C70076ECB1 /* SUUserInitiatedUpdateDriver.m in Sources */, 61180BCB0D64138900B4E0D1 /* SUWindowController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 1454BA1619637EDB00344E57 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 61B5F90109C4CEE200B25A18 /* Sparkle Test App */; targetProxy = 1454BA1519637EDB00344E57 /* PBXContainerItemProxy */; }; 1454BA1819637EE900344E57 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1420DF491962329200203BB0 /* Documentation */; targetProxy = 1454BA1719637EE900344E57 /* PBXContainerItemProxy */; }; 14732BCB1960F73500593899 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Sparkle */; targetProxy = 14732BCA1960F73500593899 /* PBXContainerItemProxy */; }; 14732BCF1960F73500593899 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */; targetProxy = 14732BCE1960F73500593899 /* PBXContainerItemProxy */; }; 14950064195FB8A600BC5B5B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Sparkle */; targetProxy = 14950063195FB8A600BC5B5B /* PBXContainerItemProxy */; }; 14950066195FB8A600BC5B5B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 61B5F90109C4CEE200B25A18 /* Sparkle Test App */; targetProxy = 14950065195FB8A600BC5B5B /* PBXContainerItemProxy */; }; 14950068195FB8A600BC5B5B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 612279D80DB5470200AB99EA /* Sparkle Unit Tests */; targetProxy = 14950067195FB8A600BC5B5B /* PBXContainerItemProxy */; }; 1495006A195FB8A600BC5B5B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */; targetProxy = 14950069195FB8A600BC5B5B /* PBXContainerItemProxy */; }; 1495006C195FB8A600BC5B5B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 55C14BB6136EEF1500649790 /* Autoupdate */; targetProxy = 1495006B195FB8A600BC5B5B /* PBXContainerItemProxy */; }; 55C14F97136F044100649790 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 55C14BB6136EEF1500649790 /* Autoupdate */; targetProxy = 55C14F96136F044100649790 /* PBXContainerItemProxy */; }; 5D06E8D60FD68C86005AE3F6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Sparkle */; targetProxy = 5D06E8D50FD68C86005AE3F6 /* PBXContainerItemProxy */; }; 61B5F91C09C4CF7200B25A18 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Sparkle */; targetProxy = 61B5F91B09C4CF7200B25A18 /* PBXContainerItemProxy */; }; 61FA528D0E2D9EB200EF58AD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Sparkle */; targetProxy = 61FA528C0E2D9EB200EF58AD /* PBXContainerItemProxy */; }; 722954BF1D04D9CB00ECF9CA /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 722954B31D04ADAF00ECF9CA /* fileop */; targetProxy = 722954BE1D04D9CB00ECF9CA /* PBXContainerItemProxy */; }; 726B2B631C645FC900388755 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 61B5F90109C4CEE200B25A18 /* Sparkle Test App */; targetProxy = 726B2B621C645FC900388755 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 55C14BDA136EF20D00649790 /* SUAutomaticUpdateAlert.xib */ = { isa = PBXVariantGroup; children = ( 6149E6ED1601ABAC008A351E /* ar */, 55C14BDB136EF20D00649790 /* cs */, 55C14BDC136EF20D00649790 /* da */, 55C14BDD136EF20D00649790 /* de */, 555CF29B196C523E0000B31E /* el */, 55C14BDE136EF20D00649790 /* en */, 55C14BDF136EF20D00649790 /* es */, 55C14BE0136EF20D00649790 /* fr */, 55C14BE1136EF20D00649790 /* is */, 55C14BE2136EF20D00649790 /* it */, 55C14BE3136EF20D00649790 /* ja */, 55C14BE4136EF20D00649790 /* ko */, 4607BEA31948443800EF8DA4 /* nb */, 55C14BE5136EF20D00649790 /* nl */, 55C14BE6136EF20D00649790 /* pl */, 55C14BE7136EF20D00649790 /* pt_BR */, 6158A1BA1378F68100487EC1 /* pt_PT */, 004A8653192A492B00C9730D /* ro */, 55C14BE9136EF20D00649790 /* ru */, 55C14BEA136EF20D00649790 /* sk */, 61BA66DA14BDFC5500D02D86 /* sl */, 55C14BEB136EF20D00649790 /* sv */, 61F3AC1415C22D5900260CA2 /* th */, 6158A1BE1378F8BB00487EC1 /* tr */, 0263187514FEBB42005EBF43 /* uk */, 55C14BEC136EF20D00649790 /* zh_CN */, 55C14BED136EF20D00649790 /* zh_TW */, ); name = SUAutomaticUpdateAlert.xib; sourceTree = ""; }; 55C14BF0136EF26100649790 /* SUUpdateAlert.xib */ = { isa = PBXVariantGroup; children = ( 6149E6EF1601ABAC008A351E /* ar */, 55C14BF1136EF26100649790 /* cs */, 55C14BF2136EF26100649790 /* da */, 55C14BF3136EF26100649790 /* de */, 555CF29C196C52460000B31E /* el */, 55C14BF4136EF26100649790 /* en */, 55C14BF5136EF26100649790 /* es */, 55C14BF6136EF26100649790 /* fr */, 55C14BF7136EF26100649790 /* is */, 55C14BF8136EF26100649790 /* it */, 55C14BF9136EF26100649790 /* ja */, 55C14BFA136EF26100649790 /* ko */, 4607BEA41948443800EF8DA4 /* nb */, 55C14BFB136EF26100649790 /* nl */, 55C14BFC136EF26100649790 /* pl */, 55C14BFD136EF26100649790 /* pt_BR */, 6158A1BB1378F68100487EC1 /* pt_PT */, 004A8654192A492B00C9730D /* ro */, 55C14BFF136EF26100649790 /* ru */, 55C14C00136EF26100649790 /* sk */, 61BA66DB14BDFC5500D02D86 /* sl */, 55C14C01136EF26100649790 /* sv */, 61F3AC1615C22D5900260CA2 /* th */, 6158A1BF1378F8BB00487EC1 /* tr */, 0263187614FEBB42005EBF43 /* uk */, 55C14C02136EF26100649790 /* zh_CN */, 55C14C03136EF26100649790 /* zh_TW */, ); name = SUUpdateAlert.xib; sourceTree = ""; }; 55C14C05136EF2C700649790 /* SUUpdatePermissionPrompt.xib */ = { isa = PBXVariantGroup; children = ( 6149E6F01601ABAC008A351E /* ar */, 55C14C06136EF2C700649790 /* cs */, 55C14C07136EF2C700649790 /* da */, 55C14C08136EF2C700649790 /* de */, 555CF29D196C524C0000B31E /* el */, 55C14C09136EF2C700649790 /* en */, 55C14C0A136EF2C700649790 /* es */, 55C14C0B136EF2C700649790 /* fr */, 55C14C0C136EF2C700649790 /* is */, 55C14C0D136EF2C700649790 /* it */, 55C14C0E136EF2C700649790 /* ja */, 55C14C0F136EF2C700649790 /* ko */, 4607BEA51948443800EF8DA4 /* nb */, 55C14C10136EF2C700649790 /* nl */, 55C14C11136EF2C700649790 /* pl */, 55C14C12136EF2C700649790 /* pt_BR */, 6158A1B81378F64700487EC1 /* pt_PT */, 004A8655192A492B00C9730D /* ro */, 55C14C14136EF2C700649790 /* ru */, 55C14C15136EF2C700649790 /* sk */, 61BA66DC14BDFC5500D02D86 /* sl */, 55C14C16136EF2C700649790 /* sv */, 61F3AC1715C22D5900260CA2 /* th */, 6158A1C01378F8BB00487EC1 /* tr */, 0263187714FEBB42005EBF43 /* uk */, 55C14C17136EF2C700649790 /* zh_CN */, 55C14C18136EF2C700649790 /* zh_TW */, ); name = SUUpdatePermissionPrompt.xib; sourceTree = ""; }; 61AAE8220A321A7F00D8810D /* Sparkle.strings */ = { isa = PBXVariantGroup; children = ( 6149E6EA1601ABAC008A351E /* ar */, 147D6DA71B66EC1C006607AB /* ca */, 615409C4103BBC4000125AF1 /* cs */, 61131A050F846CE600E97AF6 /* da */, 619B17200E1E9D0800E72754 /* de */, 555CF29A196C52330000B31E /* el */, 61AAE8230A321A7F00D8810D /* en */, 61AAE84F0A321AF700D8810D /* es */, 147D6DA91B66EC22006607AB /* fi */, 61AAE8590A321B0400D8810D /* fr */, 147D6DAA1B66EC25006607AB /* he */, 613151B20FB4946A000DCD59 /* is */, 61F614540E24A12D009F47E7 /* it */, 611A904610240DF700CC659E /* ja */, FE5536F517A2C6A7007CB333 /* ko */, 4607BEA21948443800EF8DA4 /* nb */, 61AAE8710A321F7700D8810D /* nl */, 611A904210240DD300CC659E /* pl */, 61E31A80103299500051D188 /* pt_BR */, 6186554310D7484E00B1E074 /* pt_PT */, 004A8652192A492B00C9730D /* ro */, 6195D4920E404AD700D41A50 /* ru */, FE5536F617A2C6AB007CB333 /* sk */, 61BA66CC14BDFA0400D02D86 /* sl */, 618915730E35937600B5E981 /* sv */, 61F3AC1215C22D4A00260CA2 /* th */, 5AEF45D9189D1CC90030D7DC /* tr */, 0263187214FEBB31005EBF43 /* uk */, 61131A090F846D0A00E97AF6 /* zh_CN */, 61131A0A0F846D1100E97AF6 /* zh_TW */, ); name = Sparkle.strings; sourceTree = ""; }; 61B5F92A09C4CFD800B25A18 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 61B5F92B09C4CFD800B25A18 /* English */, 1A985A321C5C329C0001163A /* ja */, 1A985A331C5C329D0001163A /* fr */, 1A985A341C5C329E0001163A /* de */, 1A985A351C5C329E0001163A /* zh-Hant */, 1A985A361C5C32A20001163A /* it */, 1A985A371C5C32A30001163A /* ca */, 1A985A381C5C32A40001163A /* cs */, 1A985A391C5C32A40001163A /* cy */, 1A985A3A1C5C32A50001163A /* da */, 1A985A3B1C5C32A60001163A /* es */, 1A985A3C1C5C32A70001163A /* fi */, 1A985A3D1C5C32A80001163A /* he */, 1A985A3E1C5C32A90001163A /* hu */, 1A985A3F1C5C32AA0001163A /* id */, 1A985A401C5C32AA0001163A /* is */, 1A985A411C5C32AB0001163A /* ko */, 1A985A421C5C32AC0001163A /* nb */, 1A985A431C5C32AD0001163A /* nl */, 1A985A441C5C32AE0001163A /* pl */, 1A985A451C5C32AF0001163A /* ru */, 1A985A461C5C32B00001163A /* sk */, 1A985A471C5C32B10001163A /* sv */, 1A985A481C5C32B10001163A /* th */, 1A985A491C5C32B20001163A /* tr */, 1A985A4A1C5C32B50001163A /* zh-Hans */, 1A985A4B1C5C32B60001163A /* fr-CA */, 1A985A4C1C5C32B70001163A /* pt-PT */, 1A985A4D1C5C32B70001163A /* pt-BR */, 1A985A4E1C5C32B80001163A /* pt */, 1A985A4F1C5C32B90001163A /* sl */, 1A985A501C5C32BA0001163A /* ro */, 1A985A511C5C32BB0001163A /* uk */, 1A985A521C5C32BC0001163A /* ar */, 1A985A531C5C32BD0001163A /* el */, ); name = InfoPlist.strings; sourceTree = ""; }; 61B5F92C09C4CFD800B25A18 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( 61B5F92D09C4CFD800B25A18 /* English */, ); name = MainMenu.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1420DF4B1962329200203BB0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Debug; }; 1420DF4C1962329200203BB0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Release; }; 14732BC71960F69300593899 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Debug; }; 14732BC81960F69300593899 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Release; }; 14950061195FB89500BC5B5B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Debug; }; 14950062195FB89500BC5B5B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Release; }; 149B785A1B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = 149B78631B7D3A0C00D7D62C /* ConfigCommonCoverage.xcconfig */; buildSettings = { }; name = Coverage; }; 149B785B1B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941CA0D94A70100DD942E /* ConfigFrameworkDebug.xcconfig */; buildSettings = { }; name = Coverage; }; 149B785C1B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941CB0D94A70100DD942E /* ConfigTestAppDebug.xcconfig */; buildSettings = { }; name = Coverage; }; 149B785D1B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = 149B78641B7D3A4800D7D62C /* ConfigUnitTestCoverage.xcconfig */; buildSettings = { }; name = Coverage; }; 149B785E1B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */; buildSettings = { }; name = Coverage; }; 149B785F1B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941D30D94A70100DD942E /* ConfigRelaunchDebug.xcconfig */; buildSettings = { }; name = Coverage; }; 149B78601B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Coverage; }; 149B78611B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Coverage; }; 149B78621B7D398100D7D62C /* Coverage */ = { isa = XCBuildConfiguration; buildSettings = { }; name = Coverage; }; 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941CA0D94A70100DD942E /* ConfigFrameworkDebug.xcconfig */; buildSettings = { }; name = Debug; }; 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941D50D94A70100DD942E /* ConfigFrameworkRelease.xcconfig */; buildSettings = { }; name = Release; }; 1DEB91B208733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941CF0D94A70100DD942E /* ConfigCommonDebug.xcconfig */; buildSettings = { }; name = Debug; }; 1DEB91B308733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941CC0D94A70100DD942E /* ConfigCommonRelease.xcconfig */; buildSettings = { }; name = Release; }; 55C14BBB136EEF1500649790 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941D30D94A70100DD942E /* ConfigRelaunchDebug.xcconfig */; buildSettings = { }; name = Debug; }; 55C14BBC136EEF1500649790 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941D40D94A70100DD942E /* ConfigRelaunchRelease.xcconfig */; buildSettings = { }; name = Release; }; 5D06E8D20FD68C7D005AE3F6 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */; buildSettings = { }; name = Debug; }; 5D06E8D30FD68C7D005AE3F6 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */; buildSettings = { }; name = Release; }; 612279DB0DB5470300AB99EA /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA3AAF3A1050B273004B3130 /* ConfigUnitTestDebug.xcconfig */; buildSettings = { }; name = Debug; }; 612279DC0DB5470300AB99EA /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA3AAF391050B273004B3130 /* ConfigUnitTestRelease.xcconfig */; buildSettings = { }; name = Release; }; 61B5F90609C4CEE300B25A18 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941CB0D94A70100DD942E /* ConfigTestAppDebug.xcconfig */; buildSettings = { }; name = Debug; }; 61B5F90709C4CEE300B25A18 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = FA1941D20D94A70100DD942E /* ConfigTestAppRelease.xcconfig */; buildSettings = { }; name = Release; }; 722954B81D04ADAF00ECF9CA /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 722954C81D04E89500ECF9CA /* ConfigFileop.xcconfig */; buildSettings = { GCC_WARN_UNUSED_VARIABLE = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 722954B91D04ADAF00ECF9CA /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = 722954C81D04E89500ECF9CA /* ConfigFileop.xcconfig */; buildSettings = { GCC_WARN_UNUSED_VARIABLE = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Coverage; }; 722954BA1D04ADAF00ECF9CA /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 722954C81D04E89500ECF9CA /* ConfigFileop.xcconfig */; buildSettings = { GCC_WARN_UNUSED_VARIABLE = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 726B2B641C645FC900388755 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 729F10FF1C65A9B500DFCCC5 /* ConfigUITestDebug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 726B2B651C645FC900388755 /* Coverage */ = { isa = XCBuildConfiguration; baseConfigurationReference = 729F10FE1C65A9B500DFCCC5 /* ConfigUITestCoverage.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Coverage; }; 726B2B661C645FC900388755 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 729F11001C65A9B500DFCCC5 /* ConfigUITestRelease.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1420DF4A1962329200203BB0 /* Build configuration list for PBXLegacyTarget "Documentation" */ = { isa = XCConfigurationList; buildConfigurations = ( 1420DF4B1962329200203BB0 /* Debug */, 149B78611B7D398100D7D62C /* Coverage */, 1420DF4C1962329200203BB0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14732BC61960F69300593899 /* Build configuration list for PBXLegacyTarget "Distribution" */ = { isa = XCConfigurationList; buildConfigurations = ( 14732BC71960F69300593899 /* Debug */, 149B78601B7D398100D7D62C /* Coverage */, 14732BC81960F69300593899 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14950060195FB89500BC5B5B /* Build configuration list for PBXAggregateTarget "All" */ = { isa = XCConfigurationList; buildConfigurations = ( 14950061195FB89500BC5B5B /* Debug */, 149B78621B7D398100D7D62C /* Coverage */, 14950062195FB89500BC5B5B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Sparkle" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91AE08733DA50010E9CD /* Debug */, 149B785B1B7D398100D7D62C /* Coverage */, 1DEB91AF08733DA50010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Sparkle" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91B208733DA50010E9CD /* Debug */, 149B785A1B7D398100D7D62C /* Coverage */, 1DEB91B308733DA50010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 55C14BBE136EEF1500649790 /* Build configuration list for PBXNativeTarget "Autoupdate" */ = { isa = XCConfigurationList; buildConfigurations = ( 55C14BBB136EEF1500649790 /* Debug */, 149B785F1B7D398100D7D62C /* Coverage */, 55C14BBC136EEF1500649790 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 5D06E8DA0FD68C95005AE3F6 /* Build configuration list for PBXNativeTarget "BinaryDelta" */ = { isa = XCConfigurationList; buildConfigurations = ( 5D06E8D20FD68C7D005AE3F6 /* Debug */, 149B785E1B7D398100D7D62C /* Coverage */, 5D06E8D30FD68C7D005AE3F6 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 612279DD0DB5470300AB99EA /* Build configuration list for PBXNativeTarget "Sparkle Unit Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( 612279DB0DB5470300AB99EA /* Debug */, 149B785D1B7D398100D7D62C /* Coverage */, 612279DC0DB5470300AB99EA /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 61B5F90509C4CEE300B25A18 /* Build configuration list for PBXNativeTarget "Sparkle Test App" */ = { isa = XCConfigurationList; buildConfigurations = ( 61B5F90609C4CEE300B25A18 /* Debug */, 149B785C1B7D398100D7D62C /* Coverage */, 61B5F90709C4CEE300B25A18 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 722954BB1D04ADAF00ECF9CA /* Build configuration list for PBXNativeTarget "fileop" */ = { isa = XCConfigurationList; buildConfigurations = ( 722954B81D04ADAF00ECF9CA /* Debug */, 722954B91D04ADAF00ECF9CA /* Coverage */, 722954BA1D04ADAF00ECF9CA /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 726B2B671C645FC900388755 /* Build configuration list for PBXNativeTarget "UI Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( 726B2B641C645FC900388755 /* Debug */, 726B2B651C645FC900388755 /* Coverage */, 726B2B661C645FC900388755 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 0867D690FE84028FC02AAC07 /* Project object */; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle.xcodeproj/xcshareddata/xcschemes/Distribution.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle.xcodeproj/xcshareddata/xcschemes/Sparkle.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Sparkle.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/English.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/English.lproj/MainMenu.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUTestApplicationDelegate.h ================================================ // // SUTestApplicationDelegate.h // Sparkle // // Created by Mayur Pawashe on 7/25/15. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import @interface SUTestApplicationDelegate : NSObject @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUTestApplicationDelegate.m ================================================ // // SUTestApplicationDelegate.m // Sparkle // // Created by Mayur Pawashe on 7/25/15. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import "SUTestApplicationDelegate.h" #import "SUUpdateSettingsWindowController.h" #import "SUFileManager.h" #import "SUTestWebServer.h" @interface SUTestApplicationDelegate () @property (nonatomic) SUUpdateSettingsWindowController *updateSettingsWindowController; @property (nonatomic) SUTestWebServer *webServer; @end @implementation SUTestApplicationDelegate @synthesize updateSettingsWindowController = _updateSettingsWindowController; @synthesize webServer = _webServer; static NSString * const UPDATED_VERSION = @"2.0"; - (void)applicationDidFinishLaunching:(NSNotification * __unused)notification { NSBundle *mainBundle = [NSBundle mainBundle]; // Check if we are already up to date if ([[mainBundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey] isEqualToString:UPDATED_VERSION]) { NSAlert *alreadyUpdatedAlert = [[NSAlert alloc] init]; alreadyUpdatedAlert.messageText = @"Update succeeded!"; alreadyUpdatedAlert.informativeText = @"This is the updated version of Sparkle Test App.\n\nDelete and rebuild the app to test updates again."; [alreadyUpdatedAlert runModal]; [[NSApplication sharedApplication] terminate:nil]; } SUFileManager *fileManager = [SUFileManager defaultManager]; // Locate user's cache directory NSError *cacheError = nil; NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&cacheError]; if (cacheDirectoryURL == nil) { NSLog(@"Failed to locate cache directory with error: %@", cacheError); assert(NO); } NSString *bundleIdentifier = mainBundle.bundleIdentifier; assert(bundleIdentifier != nil); // Create a directory that'll be used for our web server listing NSURL *serverDirectoryURL = [[cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier] URLByAppendingPathComponent:@"ServerData"]; if ([serverDirectoryURL checkResourceIsReachableAndReturnError:nil]) { NSError *removeServerDirectoryError = nil; if (![fileManager removeItemAtURL:serverDirectoryURL error:&removeServerDirectoryError]) { assert(NO); } } NSError *createDirectoryError = nil; if (![[NSFileManager defaultManager] createDirectoryAtURL:serverDirectoryURL withIntermediateDirectories:YES attributes:nil error:&createDirectoryError]) { NSLog(@"Failed creating directory at %@ with error %@", serverDirectoryURL.path, createDirectoryError); assert(NO); } NSURL *bundleURL = mainBundle.bundleURL; assert(bundleURL != nil); // Copy main bundle into server directory NSString *bundleURLLastComponent = bundleURL.lastPathComponent; assert(bundleURLLastComponent != nil); NSURL *destinationBundleURL = [serverDirectoryURL URLByAppendingPathComponent:bundleURLLastComponent]; NSError *copyBundleError = nil; if (![fileManager copyItemAtURL:bundleURL toURL:destinationBundleURL error:©BundleError]) { NSLog(@"Failed to copy main bundle into server directory with error %@", copyBundleError); assert(NO); } // Update bundle's version keys to latest version NSURL *infoURL = [[destinationBundleURL URLByAppendingPathComponent:@"Contents"] URLByAppendingPathComponent:@"Info.plist"]; BOOL infoFileExists = [infoURL checkResourceIsReachableAndReturnError:nil]; assert(infoFileExists); NSMutableDictionary *infoDictionary = [[NSMutableDictionary alloc] initWithContentsOfURL:infoURL]; [infoDictionary setObject:UPDATED_VERSION forKey:(__bridge NSString *)kCFBundleVersionKey]; [infoDictionary setObject:UPDATED_VERSION forKey:@"CFBundleShortVersionString"]; BOOL wroteInfoFile = [infoDictionary writeToURL:infoURL atomically:NO]; assert(wroteInfoFile); // Change current working directory so web server knows where to list files NSString *serverDirectoryPath = serverDirectoryURL.path; assert(serverDirectoryPath != nil); // Create the archive for our update NSString *zipName = @"Sparkle_Test_App.zip"; NSTask *dittoTask = [[NSTask alloc] init]; dittoTask.launchPath = @"/usr/bin/ditto"; dittoTask.arguments = @[@"-c", @"-k", @"--sequesterRsrc", @"--keepParent", destinationBundleURL.lastPathComponent, zipName]; dittoTask.currentDirectoryPath = serverDirectoryPath; [dittoTask launch]; [dittoTask waitUntilExit]; assert(dittoTask.terminationStatus == 0); [fileManager removeItemAtURL:destinationBundleURL error:NULL]; // Don't ever do this at home, kids (seriously) // (that is, including the private key inside of your application) NSString *privateKeyPath = [mainBundle pathForResource:@"test_app_only_dsa_priv_dont_ever_do_this_for_real" ofType:@"pem"]; assert(privateKeyPath != nil); // Sign our update NSTask *signUpdateTask = [[NSTask alloc] init]; NSString *signUpdatePath = [mainBundle pathForResource:@"sign_update" ofType:@""]; assert(signUpdatePath != nil); signUpdateTask.launchPath = signUpdatePath; NSURL *archiveURL = [serverDirectoryURL URLByAppendingPathComponent:zipName]; signUpdateTask.arguments = @[archiveURL.path, privateKeyPath]; NSPipe *outputPipe = [NSPipe pipe]; signUpdateTask.standardOutput = outputPipe; [signUpdateTask launch]; [signUpdateTask waitUntilExit]; assert(signUpdateTask.terminationStatus == 0); NSData *signatureData = [outputPipe.fileHandleForReading readDataToEndOfFile]; NSString *signature = [[[NSString alloc] initWithData:signatureData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; assert(signature != nil); // Obtain the file attributes to get the file size of our update later NSError *fileAttributesError = nil; NSString *archivePath = archiveURL.path; assert(archivePath != nil); NSDictionary *archiveFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:archivePath error:&fileAttributesError]; if (archiveFileAttributes == nil) { NSLog(@"Failed to retrieve file attributes from archive with error %@", fileAttributesError); assert(NO); } NSString * const appcastName = @"sparkletestcast"; NSString * const appcastExtension = @"xml"; // Copy our appcast over to the server directory NSURL *appcastDestinationURL = [[serverDirectoryURL URLByAppendingPathComponent:appcastName] URLByAppendingPathExtension:appcastExtension]; NSError *copyAppcastError = nil; if (![fileManager copyItemAtURL:[mainBundle URLForResource:appcastName withExtension:appcastExtension] toURL:appcastDestinationURL error:©AppcastError]) { NSLog(@"Failed to copy appcast into cache directory with error %@", copyAppcastError); assert(NO); } // Update the appcast with the file size and signature of the update archive // We could be using some sort of XML parser instead of doing string substitutions, but for now, this is easier NSError *appcastError = nil; NSMutableString *appcastContents = [[NSMutableString alloc] initWithContentsOfURL:appcastDestinationURL encoding:NSUTF8StringEncoding error:&appcastError]; if (appcastContents == nil) { NSLog(@"Failed to load appcast contents with error %@", appcastError); assert(NO); } NSUInteger numberOfLengthReplacements = [appcastContents replaceOccurrencesOfString:@"$INSERT_ARCHIVE_LENGTH" withString:[NSString stringWithFormat:@"%llu", archiveFileAttributes.fileSize] options:NSLiteralSearch range:NSMakeRange(0, appcastContents.length)]; assert(numberOfLengthReplacements == 1); NSUInteger numberOfSignatureReplacements = [appcastContents replaceOccurrencesOfString:@"$INSERT_DSA_SIGNATURE" withString:signature options:NSLiteralSearch range:NSMakeRange(0, appcastContents.length)]; assert(numberOfSignatureReplacements == 1); NSError *writeAppcastError = nil; if (![appcastContents writeToURL:appcastDestinationURL atomically:NO encoding:NSUTF8StringEncoding error:&writeAppcastError]) { NSLog(@"Failed to write updated appcast with error %@", writeAppcastError); assert(NO); } // Finally start the server SUTestWebServer *webServer = [[SUTestWebServer alloc] initWithPort:1337 workingDirectory:serverDirectoryPath]; if (!webServer) { NSLog(@"Failed to create the web server"); assert(NO); } self.webServer = webServer; // Show the Settings window self.updateSettingsWindowController = [[SUUpdateSettingsWindowController alloc] init]; [self.updateSettingsWindowController showWindow:nil]; } - (void)applicationWillTerminate:(NSNotification * __unused)notification { [self.webServer close]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUTestWebServer.h ================================================ // // SUTestWebServer.h // Sparkle // // Created by Kevin Wojniak on 10/8/15. // Copyright © 2015 Sparkle Project. All rights reserved. // #import @interface SUTestWebServer : NSObject - (instancetype)initWithPort:(int)port workingDirectory:(NSString*)workingDirectory; - (void)close; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUTestWebServer.m ================================================ // // SUTestWebServer.m // Sparkle // // Created by Kevin Wojniak on 10/8/15. // Copyright © 2015 Sparkle Project. All rights reserved. // #import "SUTestWebServer.h" #import #import @class SUTestWebServerConnection; @protocol SUTestWebServerConnectionDelegate @required - (void)connectionDidClose:(SUTestWebServerConnection*)sender; @end @interface SUTestWebServerConnection : NSObject @property (nonatomic) NSString* workingDirectory; @property (nonatomic, weak) id delegate; @property (nonatomic) NSInputStream *inputStream; @property (nonatomic) NSOutputStream *outputStream; @property (nonatomic) NSData *dataToWrite; @property (nonatomic) NSInteger numBytesToWrite; @end @implementation SUTestWebServerConnection @synthesize workingDirectory = _workingDirectory; @synthesize delegate = _delegate; @synthesize inputStream = _inputStream; @synthesize outputStream = _outputStream; @synthesize dataToWrite = _dataToWrite; @synthesize numBytesToWrite = _numBytesToWrite; - (instancetype)initWithNativeHandle:(CFSocketNativeHandle)handle workingDirectory:(NSString*)workingDirectory delegate:(id)delegate { self = [super init]; assert(self != nil); _workingDirectory = workingDirectory; _delegate = delegate; CFReadStreamRef readStream = NULL; CFWriteStreamRef writeStream = NULL; CFStreamCreatePairWithSocket(NULL, handle, &readStream, &writeStream); assert(readStream != NULL); assert(writeStream != NULL); _inputStream = (__bridge NSInputStream*)readStream; assert(_inputStream != nil); _inputStream.delegate = self; _outputStream = (__bridge NSOutputStream*)writeStream; assert(_outputStream != nil); _outputStream.delegate = self; [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_inputStream open]; [_outputStream open]; return self; } - (void)close { NSInputStream *inputStream = self.inputStream; NSOutputStream *outputStream = self.outputStream; if (inputStream == nil) { assert(outputStream == nil); return; } [inputStream close]; [outputStream close]; [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; self.inputStream = nil; self.outputStream = nil; [self.delegate connectionDidClose:self]; } - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { if (eventCode == NSStreamEventEndEncountered) { [self close]; return; } if (aStream == self.inputStream && eventCode == NSStreamEventHasBytesAvailable) { uint8_t buffer[8096]; const NSInteger numBytes = [self.inputStream read:buffer maxLength:sizeof(buffer)]; if (numBytes > 0) { NSString *request = [[NSString alloc] initWithBytes:buffer length:(NSUInteger)numBytes encoding:NSUTF8StringEncoding]; NSArray *lines = [request componentsSeparatedByString:@"\r\n"]; NSString *requestLine = lines.count >= 3 ? [lines objectAtIndex:0] : nil; NSArray *parts = requestLine ? [requestLine componentsSeparatedByString:@" "] : nil; // Only process GET requests for existing files if ([[parts objectAtIndex:0] isEqualToString:@"GET"]) { // Use NSURL to strip out query parameters NSString *path = [NSURL URLWithString:[parts objectAtIndex:1] relativeToURL:nil].path; NSString *filePath = [self.workingDirectory stringByAppendingString:path]; BOOL isDir = NO; if (![[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDir] || isDir) { NSLog(@"%@ - 404", requestLine); [self write404]; } else { NSLog(@"%@ - 200", requestLine); [self write:[NSData dataWithContentsOfFile:filePath] status:YES]; } } else { NSLog(@"%@ - 404", requestLine); [self write404]; } } } else if (aStream == self.outputStream && eventCode == NSStreamEventHasSpaceAvailable && self.dataToWrite != nil) { [self checkIfCanWriteNow]; } } - (void)write404 { NSString *body = @"404 Not Found

Not Found

"; [self write:[body dataUsingEncoding:NSUTF8StringEncoding] status:NO]; } - (void)write:(NSData*)body status:(BOOL)status { NSString *state = status ? @"200 OK" : @"404 Not Found"; NSString *header = [NSString stringWithFormat:@"HTTP/1.0 %@\r\nContent-Length: %lu\r\n\r\n", state, body.length]; NSMutableData *response = [[header dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; [response appendData:body]; [self queueWrite:response]; } - (void)queueWrite:(NSData*)data { assert(self.dataToWrite == nil); assert(data != nil); assert(data.length > 0); self.dataToWrite = data; self.numBytesToWrite = (NSInteger)data.length; [self checkIfCanWriteNow]; } - (void)checkIfCanWriteNow { assert(self.dataToWrite != nil); if (self.numBytesToWrite == 0) { // nothing more to write, we're done. self.dataToWrite = nil; self.numBytesToWrite = -1; } else if (self.outputStream.hasSpaceAvailable) { [self writeNow]; } // otherwise wait for space available event } - (void)writeNow { assert(self.outputStream != nil); assert(self.outputStream.hasSpaceAvailable); assert(self.dataToWrite != nil); NSData *dataToWrite = self.dataToWrite; const uint8_t *bytesOffset = (const uint8_t*)dataToWrite.bytes + ((NSInteger)dataToWrite.length - self.numBytesToWrite); const NSInteger bytesWritten = [self.outputStream write:bytesOffset maxLength:(NSUInteger)self.numBytesToWrite]; if (bytesWritten > 0) { self.numBytesToWrite = self.numBytesToWrite - bytesWritten; assert(self.numBytesToWrite >= 0); // wait for next space available event to write more } else { NSLog(@"Error: bytes written = %ld (%@)", bytesWritten, [NSString stringWithUTF8String:strerror(errno)]); } } @end @interface SUTestWebServer () { CFSocketRef _socket; } @property (nonatomic) NSMutableArray *connections; @property (nonatomic) NSString *workingDirectory; - (void)accept:(CFSocketNativeHandle)address; @end static void connectCallback(CFSocketRef __unused s, CFSocketCallBackType type, CFDataRef __unused address, const void *data, void *info) { if (type == kCFSocketAcceptCallBack) { assert(data != NULL); assert(info != NULL); SUTestWebServer *server = (__bridge SUTestWebServer*)info; assert(server != nil); [server accept:*(const CFSocketNativeHandle*)data]; } } @implementation SUTestWebServer @synthesize connections = _connections; @synthesize workingDirectory = _workingDirectory; - (instancetype)initWithPort:(int)port workingDirectory:(NSString*)workingDirectory { self = [super init]; assert(self != nil); CFSocketContext ctx; memset(&ctx, 0, sizeof(ctx)); ctx.info = (__bridge void*)self; _socket = CFSocketCreate(NULL, 0, 0, 0, kCFSocketAcceptCallBack, connectCallback, &ctx); assert(_socket != NULL); struct sockaddr_in address; memset(&address, 0, sizeof(address)); address.sin_len = sizeof(address); address.sin_family = AF_INET; address.sin_port = htons(port); address.sin_addr.s_addr = INADDR_ANY; // will fail if port is in use. CFSocketError socketErr = CFSocketSetAddress(_socket, (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)]); if (socketErr != kCFSocketSuccess) { NSLog(@"Socket error: %@", [NSString stringWithUTF8String:strerror(errno)]); return nil; } _connections = [[NSMutableArray alloc] init]; _workingDirectory = workingDirectory; CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(NULL, _socket, 0); assert(source != NULL); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source); return self; } - (void)connectionDidClose:(SUTestWebServerConnection *)sender { assert(self.connections != nil); assert([self.connections containsObject:sender]); [self.connections removeObject:sender]; } - (void)accept:(CFSocketNativeHandle)address { SUTestWebServerConnection *conn = [[SUTestWebServerConnection alloc] initWithNativeHandle:address workingDirectory:self.workingDirectory delegate:self]; assert(conn != nil); if (conn) { assert(self.connections != nil); [self.connections addObject:conn]; } } - (void)close { for (SUTestWebServerConnection *conn in self.connections) { [conn close]; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdirect-ivar-access" if (_socket) { CFSocketInvalidate(_socket); CFRelease(_socket); _socket = NULL; } #pragma clang diagnostic pop } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUUpdateSettingsWindowController.h ================================================ // // SUUpdateSettingsWindowController.h // Sparkle // // Created by Mayur Pawashe on 7/25/15. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import @interface SUUpdateSettingsWindowController : NSWindowController @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUUpdateSettingsWindowController.m ================================================ // // SUUpdateSettingsWindowController.m // Sparkle // // Created by Mayur Pawashe on 7/25/15. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import "SUUpdateSettingsWindowController.h" #import @interface SUUpdateSettingsWindowController () @property (nonatomic) IBOutlet SUUpdater *updater; @end @implementation SUUpdateSettingsWindowController @synthesize updater = _updater; - (NSString *)windowNibName { return NSStringFromClass([self class]); } - (IBAction)checkForUpdates:(id __unused)sender { [self.updater checkForUpdates:nil]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/SUUpdateSettingsWindowController.xib ================================================ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/TestApplication-Info.plist ================================================ CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName Sparkle Test App CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 1.5 CFBundleShortVersionString 1.5 NSMainNibFile MainMenu NSPrincipalClass NSApplication SUEnableSystemProfiling SUFeedURL http://0.0.0.0:1337/sparkletestcast.xml SUPublicDSAKeyFile test_app_only_dsa_pub.pem NSAppTransportSecurity NSAllowsArbitraryLoads ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/ar.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/ca.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/cs.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/cy.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/da.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/de.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/el.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/es.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/fi.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/fr-CA.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/fr.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/he.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/hu.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/id.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/is.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/it.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/ja.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/ko.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/main.m ================================================ // // main.m // Sparkle // // Created by Andy Matuschak on 3/12/06. // Copyright Andy Matuschak 2006. All rights reserved. // #import int main(int argc, const char *argv[]) { return NSApplicationMain(argc, argv); } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/nb.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/nl.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/pl.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/pt-BR.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/pt-PT.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/pt.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/ro.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/ru.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/sk.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/sl.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/sparkletestcast.xml ================================================ Sparkle Test App Changelog Most recent changes with links to updates. en Version 2.0
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • Suspendisse sed felis ac ante ultrices rhoncus. Etiam quis elit vel nibh placerat facilisis in id leo.
  • Vestibulum nec tortor odio, nec malesuada libero. Cras vel convallis nunc.
  • Suspendisse tristique massa eget velit consequat tincidunt. Praesent sodales hendrerit pretium.
  • ]]>
    Sat, 26 Jul 2014 15:20:11 +0000
    ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/sv.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/test_app_only_dsa_priv_dont_ever_do_this_for_real.pem ================================================ -----BEGIN DSA PRIVATE KEY----- MIIGPgIBAAKCAgEAvvs5v/Vbc6gYls5WnHUmWy9rhUsuAvf+HAuz9Tzp63fb8Cni 8aqe+qkiL3RKuabl92BAK7Vwv4EMOXozDlNPWZS5EZUBZuiQ7vtQC9Eg3C68KWwb lrhSwzyVnXz8twD75CiR+oPSZEIiOQ81Ok///i1+TV17ierbLxYJ2Sd1wTRQdLeS 7rmnL6aLka0PNtsFvFlvW9GbrlNP9PZaGmrJYadsTV5+F9XZRFnwZc6crmrIo+DA VNucgqBdZUNN0CWz9OJfKr/GKMw+blTF2WixDnGH2FKtNh7QSuRwlvioXBjYJbX4 UV6jJqxZdLUMSm9iZteC8Q34cFVwrPizwRBO/8Yx/htFPrvyIkD0uvfMO55dbkld 0nWfbHcUJRHjSEOPahOlHHGejmLWEsBtp9vD453czFXi19GhR98gEaXmcbwOr4gH A1WHTqOwUx6Ll+AFWG5nyZIKB9SxMUBWu0znVg9d9wqYAL0/aBWhXEFe86xYdReK z0RvnTRyVDbIGhMrRMviyaiTGHBbJVC62Appp/fl8o0HFSKJfTwhRSfmLupqt0XE C1GTbSZ9l04fsryY/dyyuO+E3e/+Q1jxyqrdJI9mhkUY2w4FWmaovScWNbekwhXb qy6+bsY2ZyuNGjR/S0xFyHw95l5VGiHeyXD+bWg9lYQzIULjX3zTAU6FFYMCFQCu tNIHrUBezHQxUxsQDgGubSZ90wKCAgAKHL/AbnSnGcoDNw2tDTrfYvQQH1CN8/yx 0X1ryUwzn9BJYCzl6NTaHZx/LSwhSasNRmiSlp95YqhLa31xJDPaHVZH8SDLyCSn rBWCrKaKT8U3PKl71WUnxttYIyMTyNYh6ef+OP1uxDnOn0asWYinbDEM5E6CrRmW AoKx0yDgchiyOMGiwCuTN1Z7fx0AL8Y+JZ1ulOJjwd+qBaPIT/tduz5fKzEvfpHy 6seOaRAuj/CNx1svSiCi16nlHZjlFlbzE5+IlX5iOvSEroacGbj7FHmHlRTpdgBo Ez6uzSV3drPDmsH9jb0nvhy/MAkBrHVOm4jj77613nD/QlO3Ilrox5+O1LuYrU6c gy5ZEek093QUsazHonjZc+btYIshs4FTWG9gBbZRQbJy0cOlNei0XcwIHOWqJXOJ LWpV1ZXu2IxLhfbg4Nt02z1CNHTm+VKD8UPyUCk8Mf9SuPUzRXeWMiLE57+8hsUs BcMvvURRgHZqVaNdO5ktb+i/g0zhEIFaOyfCAwZKNzLyz/JuwosbOruLMW0YCnaO mj9YvO0MQ+zHcd806n/9t8sig69zU/4wg9lxJrgWvf3LbjwBii143nDcSu3DGaZj Ti9ZAj19AbUX3PB44m9TDxFwTDTFI+xp8HSVtlWx1Pt2KXbsxD5mP1LEpHpq1GOB 7zLcRL1F1wKCAgEAlikrAItC1I6mHKst1GQLDr9c07OuIJA+GJIJB6Iq/K/CjdUA Rjv/3L6l4pP33VAZySh6jqMe3Qm9W0ZZ687YSuVWRCsl2yKWAWuqXm8zaEijaHZR Sa6tygj9EUMZ2zbJEOPihIdWB+fHcwd01KAqUzYZEWZvw7fLZNE2QM0emQai3U1V 3SXYney1qXW9lJ38EjB4LrYzcDFZaNKXidvXo3nI66M1MgI9lAltmRLGAOJmV6bL KvJEFVpSux5cubXISPQBlAw9Zb15UPc3IAe82jX7ccsI8RHe5xeIepZ0c7xnk+J8 sFrgBrlN1swki2WfGavQOsV9kZg79rYCB3fOtxRsCBcQOasL295YWtQh5g/dCQWA JMx+6SVMWwr//0YSu361zJakdFz/vssVVGkaun0bM2YTOYP1ZZp6LyHwIrWEqh9m d2v2NAX1h4LktrJ8OHgBV/z6/YzFOlsa424Dvl1jnUozK1RDX5/z2FnX1J22hNSY fx2M8wTD09+xwJyFOzT8g/pjkKvYMgvnWVjvONwvM5nc2OFzuHl9JVvdF5LbSYPO M/KkRrtg+denjT4/fwp16gGcCsIf+RbEDN1F9rWg5hNPKteVkrMl6suuZZOu7H7E uZIiEcW3bxXYiN8LLXwS4G6VH+9fsUmSusJZKxhBuRThzehy0n6lfgSRRGICFBi3 z3tWjm2F6noctsjrVH5d0jRZ -----END DSA PRIVATE KEY----- ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/test_app_only_dsa_pub.pem ================================================ -----BEGIN PUBLIC KEY----- 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 QbkU4c3octJ+pX4EkURi -----END PUBLIC KEY----- ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/th.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/tr.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/uk.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/zh-Hans.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/TestApplication/zh-Hant.lproj/InfoPlist.strings ================================================ /* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© Andy Matuschak, 2006"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/Resources/signed-test-file.txt ================================================ Hello World! ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/Resources/test-pubkey.pem ================================================ -----BEGIN PUBLIC KEY----- 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 bKZWFUcty/GYmXtxTTk= -----END PUBLIC KEY----- ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/Resources/testappcast.xml ================================================ For unit test only Version 2.0 desc Sat, 26 Jul 2014 15:20:11 +0000 Version 3.0 Version 4.0 Sat, 26 Jul 2014 15:20:13 +0000 17.0.0 Version 5.0 2.0.0 ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/Resources/testnamespaces.xml ================================================ For unit test only Version 3.0 Sat, 26 Jul 2014 15:20:12 +0000 https://sparkle-project.org/#works Version 2.0 desc Sat, 26 Jul 2014 15:20:11 +0000 ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUAppcastTest.swift ================================================ // // SUAppcastTest.swift // Sparkle // // Created by Kornel on 17/02/2016. // Copyright © 2016 Sparkle Project. All rights reserved. // import XCTest import Sparkle; class SUAppcastTest: XCTestCase { func testParseAppcast() { let appcast = SUAppcast(); let testFile = NSBundle(forClass: SUAppcastTest.self).pathForResource("testappcast", ofType: "xml")!; let testFileUrl = NSURL(fileURLWithPath: testFile); XCTAssertNotNil(testFileUrl); do { let items = try appcast.parseAppcastItemsFromXMLFile(testFileUrl) as! [SUAppcastItem]; XCTAssertEqual(4, items.count); XCTAssertEqual("Version 2.0", items[0].title); XCTAssertEqual("desc", items[0].itemDescription); XCTAssertEqual("Sat, 26 Jul 2014 15:20:11 +0000", items[0].dateString) // This is the best release matching our system version XCTAssertEqual("Version 3.0", items[1].title); XCTAssertNil(items[1].itemDescription); XCTAssertNil(items[1].dateString) XCTAssertEqual("Version 4.0", items[2].title); XCTAssertNil(items[2].itemDescription); XCTAssertEqual("Sat, 26 Jul 2014 15:20:13 +0000", items[2].dateString) XCTAssertEqual("Version 5.0", items[3].title); XCTAssertNil(items[3].itemDescription); XCTAssertNil(items[3].dateString) // Test best appcast item & a delta update item var deltaItem: SUAppcastItem? = nil let bestAppcastItem = SUBasicUpdateDriver.bestItemFromAppcastItems(items, getDeltaItem: &deltaItem, withHostVersion: "1.0", comparator: SUStandardVersionComparator.defaultComparator()) XCTAssertEqual(bestAppcastItem, items[1]) XCTAssertEqual(deltaItem!.fileURL.lastPathComponent, "3.0_from_1.0.patch") // Test latest delta update item available var latestDeltaItem: SUAppcastItem? = nil SUBasicUpdateDriver.bestItemFromAppcastItems(items, getDeltaItem: &latestDeltaItem, withHostVersion: "2.0", comparator: SUStandardVersionComparator.defaultComparator()) XCTAssertEqual(latestDeltaItem!.fileURL.lastPathComponent, "3.0_from_2.0.patch") // Test a delta item that does not exist var nonexistantDeltaItem: SUAppcastItem? = nil SUBasicUpdateDriver.bestItemFromAppcastItems(items, getDeltaItem: &nonexistantDeltaItem, withHostVersion: "2.1", comparator: SUStandardVersionComparator.defaultComparator()) XCTAssertNil(nonexistantDeltaItem) } catch let err as NSError { NSLog("%@", err); XCTFail(err.localizedDescription); } } func testNamespaces() { let appcast = SUAppcast(); let testFile = NSBundle(forClass: SUAppcastTest.self).pathForResource("testnamespaces", ofType: "xml")!; let testFileUrl = NSURL(fileURLWithPath: testFile); XCTAssertNotNil(testFileUrl); do { let items = try appcast.parseAppcastItemsFromXMLFile(testFileUrl) as! [SUAppcastItem]; XCTAssertEqual(2, items.count); XCTAssertEqual("Version 2.0", items[1].title); XCTAssertEqual("desc", items[1].itemDescription); XCTAssertNotNil(items[0].releaseNotesURL); XCTAssertEqual("https://sparkle-project.org/#works", items[0].releaseNotesURL!.absoluteString); } catch let err as NSError { NSLog("%@", err); XCTFail(err.localizedDescription); } } } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUBinaryDeltaTest.m ================================================ // // SUBinaryDeltaTest.m // Sparkle // // Created by Jake Petroules on 2014-08-22. // Copyright (c) 2014 Sparkle Project. All rights reserved. // #import #import #import "SUBinaryDeltaCommon.h" #import "SUBinaryDeltaCreate.h" #import "SUBinaryDeltaApply.h" #import #include @interface SUBinaryDeltaTest : XCTestCase @end typedef void (^SUDeltaHandler)(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory); @implementation SUBinaryDeltaTest - (void)testTemporaryDirectory { NSString *tmp1 = temporaryDirectory(@"Sparkle"); NSString *tmp2 = temporaryDirectory(@"Sparkle"); NSLog(@"Temporary directories: %@, %@", tmp1, tmp2); XCTAssertNotEqualObjects(tmp1, tmp2); XCTAssert(YES, @"Pass"); } - (void)testTemporaryFile { NSString *tmp1 = temporaryFilename(@"Sparkle"); NSString *tmp2 = temporaryFilename(@"Sparkle"); NSLog(@"Temporary files: %@, %@", tmp1, tmp2); XCTAssertNotEqualObjects(tmp1, tmp2); XCTAssert(YES, @"Pass"); } - (BOOL)createAndApplyPatchUsingVersion:(SUBinaryDeltaMajorVersion)majorVersion beforeDiffHandler:(SUDeltaHandler)beforeDiffHandler afterDiffHandler:(SUDeltaHandler)afterDiffHandler { NSString *sourceDirectory = temporaryDirectory(@"Sparkle_temp1"); NSString *destinationDirectory = temporaryDirectory(@"Sparkle_temp2"); NSString *diffFile = temporaryFilename(@"Sparkle_diff"); NSString *patchDirectory = temporaryDirectory(@"Sparkle_patch"); XCTAssertNotNil(sourceDirectory); XCTAssertNotNil(destinationDirectory); XCTAssertNotNil(diffFile); NSFileManager *fileManager = [[NSFileManager alloc] init]; if (beforeDiffHandler != nil) { beforeDiffHandler(fileManager, sourceDirectory, destinationDirectory); } NSError *createDiffError = nil; BOOL createdDiff = createBinaryDelta(sourceDirectory, destinationDirectory, diffFile, majorVersion, NO, &createDiffError); if (!createdDiff) { NSLog(@"Creating binary diff failed with error: %@", createDiffError); } else if (afterDiffHandler != nil) { afterDiffHandler(fileManager, sourceDirectory, destinationDirectory); } NSError *applyDiffError = nil; BOOL appliedDiff = NO; if (createdDiff) { if (applyBinaryDelta(sourceDirectory, patchDirectory, diffFile, NO, &applyDiffError)) { appliedDiff = YES; } else { NSLog(@"Applying binary diff failed with error: %@", applyDiffError); } } XCTAssertTrue([fileManager removeItemAtPath:sourceDirectory error:nil]); XCTAssertTrue([fileManager removeItemAtPath:destinationDirectory error:nil]); XCTAssertTrue([fileManager removeItemAtPath:patchDirectory error:nil]); XCTAssertTrue([fileManager removeItemAtPath:diffFile error:nil]); return appliedDiff; } - (BOOL)createAndApplyPatchWithBeforeDiffHandler:(SUDeltaHandler)beforeDiffHandler afterDiffHandler:(SUDeltaHandler)afterDiffHandler { return [self createAndApplyPatchUsingVersion:LATEST_DELTA_DIFF_MAJOR_VERSION beforeDiffHandler:beforeDiffHandler afterDiffHandler:afterDiffHandler]; } - (void)createAndApplyPatchWithHandler:(SUDeltaHandler)handler { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:handler afterDiffHandler:nil]; XCTAssertTrue(success); } - (BOOL)testDirectoryHashEqualityWithSource:(NSString *)source destination:(NSString *)destination { XCTAssertNotNil(source); XCTAssertNotNil(destination); NSString *beforeHash = hashOfTree(source); NSString *afterHash = hashOfTree(destination); XCTAssertNotNil(beforeHash); XCTAssertNotNil(afterHash); return [beforeHash isEqualToString:afterHash]; } - (void)testNoFilesDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { XCTAssertTrue([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testEmptyDataDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSData *emptyData = [NSData data]; NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([emptyData writeToFile:sourceFile atomically:YES]); XCTAssertTrue([emptyData writeToFile:destinationFile atomically:YES]); XCTAssertTrue([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testDifferentlyNamedEmptyDataDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSData *emptyData = [NSData data]; NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"B"]; XCTAssertTrue([emptyData writeToFile:sourceFile atomically:YES]); XCTAssertTrue([emptyData writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testEmptyDirectoryDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } if (![fileManager createDirectoryAtPath:destinationFile withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } XCTAssertTrue([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testDifferentlyNamedEmptyDirectoryDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } if (![fileManager createDirectoryAtPath:destinationFile withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testSameNonexistantSymlinkDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:sourceFile withDestinationPath:@"B" error:&error]) { NSLog(@"Failed creating empty symlink with error: %@", error); XCTFail("Failed to create empty symlink"); } if (![fileManager createSymbolicLinkAtPath:destinationFile withDestinationPath:@"B" error:&error]) { NSLog(@"Failed creating empty symlink with error: %@", error); XCTFail("Failed to create empty symlink"); } XCTAssertTrue([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testDifferentNonexistantSymlinkDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:sourceFile withDestinationPath:@"B" error:&error]) { NSLog(@"Failed creating empty symlink with error: %@", error); XCTFail("Failed to create empty symlink"); } if (![fileManager createSymbolicLinkAtPath:destinationFile withDestinationPath:@"C" error:&error]) { NSLog(@"Failed creating empty symlink with error: %@", error); XCTFail("Failed to create empty symlink"); } XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testNonexistantSymlinkPermissionDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:sourceFile withDestinationPath:@"B" error:&error]) { NSLog(@"Failed creating empty symlink to source with error: %@", error); XCTFail("Failed to create empty symlink"); } if (![fileManager createSymbolicLinkAtPath:destinationFile withDestinationPath:@"B" error:&error]) { NSLog(@"Failed creating empty symlink to destination with error: %@", error); XCTFail("Failed to create empty symlink"); } if (lchmod([sourceFile fileSystemRepresentation], 0777) != 0) { NSLog(@"Change Permission Error.."); XCTFail(@"Failed setting file permissions"); } XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testSmallDataDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testInvalidSource { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); } afterDiffHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *__unused destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData dataWithBytes:"testt" length:5] writeToFile:sourceFile atomically:YES]); }]; XCTAssertFalse(success); } - (NSData *)bigData1 { const size_t bufferSize = 4096*10; uint8_t *buffer = calloc(1, bufferSize); XCTAssertTrue(buffer != NULL); return [NSData dataWithBytesNoCopy:buffer length:bufferSize]; } - (NSData *)bigData2 { const size_t bufferSize = 4096*10; uint8_t *buffer = calloc(1, bufferSize); XCTAssertTrue(buffer != NULL); for (size_t bufferIndex = 0; bufferIndex < bufferSize; ++bufferIndex) { buffer[bufferIndex] = 1; } return [NSData dataWithBytesNoCopy:buffer length:bufferSize]; } - (void)testBigDataSameDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[self bigData1] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[self bigData1] writeToFile:destinationFile atomically:YES]); XCTAssertTrue([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testBigDataDifferentDiff { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[self bigData1] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[self bigData2] writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testRegularFileAdded { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"lol" length:3] writeToFile:destinationFile2 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } // Make sure old version patches still work for simple cases - (void)testRegularFileAddedWithAzureVersion { XCTAssertTrue([self createAndApplyPatchUsingVersion:SUAzureMajorVersion beforeDiffHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"lol" length:3] writeToFile:destinationFile2 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); } afterDiffHandler:nil]); } - (void)testDirectoryAdded { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSString *destinationFile3 = [destinationFile2 stringByAppendingPathComponent:@"C"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile1 atomically:YES]); NSError *error = nil; if (![fileManager createDirectoryAtPath:destinationFile2 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } XCTAssertTrue([[self bigData2] writeToFile:destinationFile3 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testRegularFileRemoved { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"lol" length:3] writeToFile:sourceFile2 atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testDirectoryRemoved { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceDirectory stringByAppendingPathComponent:@"B"]; NSString *sourceFile3 = [sourceFile2 stringByAppendingPathComponent:@"C"]; XCTAssertTrue([[NSData data] writeToFile:destinationFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:sourceFile1 atomically:YES]); NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile2 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } XCTAssertTrue([[self bigData2] writeToFile:sourceFile3 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testRegularFileMove { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testCaseSensitiveRegularFileMove { [self createAndApplyPatchWithHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceDirectory stringByAppendingPathComponent:@"b"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"a"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile1 atomically:YES]); XCTAssertTrue([data writeToFile:sourceFile2 atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile1 atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile2 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testRemovingSymlink { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:sourceFile withDestinationPath:@"B" error:&error]) { NSLog(@"Error in creating symlink: %@", error); XCTFail(@"Failed to create symlink"); } XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testAddingSymlink { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:destinationFile withDestinationPath:@"B" error:&error]) { NSLog(@"Error in creating symlink: %@", error); XCTFail(@"Failed to create symlink"); } XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testSmallFilePermissionChangeWithNoContentChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile atomically:YES]); NSError *error = nil; if (![fileManager setAttributes:@{NSFilePosixPermissions : @0755} ofItemAtPath:destinationFile error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testBigFilePermissionChangeWithNoContentChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSData *data = [self bigData1]; XCTAssertTrue([data writeToFile:sourceFile atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile atomically:YES]); NSError *error = nil; if (![fileManager setAttributes:@{NSFilePosixPermissions : @0755} ofItemAtPath:destinationFile error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testSmallFilePermissionChangeWithContentChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:@"lawl" length:4] writeToFile:destinationFile atomically:YES]); NSError *error = nil; if (![fileManager setAttributes:@{NSFilePosixPermissions : @0755} ofItemAtPath:destinationFile error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testBigFilePermissionChangeWithContentChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[self bigData1] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[self bigData2] writeToFile:destinationFile atomically:YES]); NSError *error = nil; if (![fileManager setAttributes:@{NSFilePosixPermissions : @0755} ofItemAtPath:destinationFile error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testDirectoryPermissionChangeWithContentChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceFile1 stringByAppendingPathComponent:@"B"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationFile1 stringByAppendingPathComponent:@"B"]; NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile1 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } if (![fileManager createDirectoryAtPath:destinationFile1 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } XCTAssertTrue([[self bigData1] writeToFile:sourceFile2 atomically:YES]); XCTAssertTrue([[self bigData1] writeToFile:destinationFile2 atomically:YES]); if (![fileManager setAttributes:@{NSFilePosixPermissions : @0766} ofItemAtPath:sourceFile1 error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } if (![fileManager setAttributes:@{NSFilePosixPermissions : @0755} ofItemAtPath:destinationFile1 error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testInvalidPermissionsInAfterTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile atomically:YES]); NSError *error = nil; if (![fileManager setAttributes:@{NSFilePosixPermissions : @0777} ofItemAtPath:destinationFile error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); } afterDiffHandler:nil]; XCTAssertFalse(success); } - (void)testBadPermissionsInBeforeTree { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile atomically:YES]); NSError *error = nil; if (![fileManager setAttributes:@{NSFilePosixPermissions : @0777} ofItemAtPath:sourceFile error:&error]) { NSLog(@"Change Permission Error: %@", error); XCTFail(@"Failed setting file permissions"); } // This would fail for version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testInvalidRegularFileWithACLInBeforeTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString * __unused destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:sourceFile atomically:YES]); acl_t acl = acl_init(1); acl_entry_t entry; XCTAssertEqual(0, acl_create_entry(&acl, &entry)); acl_permset_t permset; XCTAssertEqual(0, acl_get_permset(entry, &permset)); XCTAssertEqual(0, acl_add_perm(permset, ACL_SEARCH)); XCTAssertEqual(0, acl_set_link_np([sourceFile fileSystemRepresentation], ACL_TYPE_EXTENDED, acl)); XCTAssertEqual(0, acl_free(acl)); } afterDiffHandler:nil]; XCTAssertFalse(success); } - (void)testInvalidRegularFileWithACLInAfterTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *__unused fileManager, NSString *__unused sourceDirectory, NSString *destinationDirectory) { NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); acl_t acl = acl_init(1); acl_entry_t entry; XCTAssertEqual(0, acl_create_entry(&acl, &entry)); acl_permset_t permset; XCTAssertEqual(0, acl_get_permset(entry, &permset)); XCTAssertEqual(0, acl_add_perm(permset, ACL_SEARCH)); XCTAssertEqual(0, acl_set_link_np([destinationFile fileSystemRepresentation], ACL_TYPE_EXTENDED, acl)); XCTAssertEqual(0, acl_free(acl)); } afterDiffHandler:nil]; XCTAssertFalse(success); } - (void)testInvalidDirectoryWithACLInBeforeTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString * __unused destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([fileManager createDirectoryAtPath:sourceFile withIntermediateDirectories:NO attributes:nil error:nil]); acl_t acl = acl_init(1); acl_entry_t entry; XCTAssertEqual(0, acl_create_entry(&acl, &entry)); acl_permset_t permset; XCTAssertEqual(0, acl_get_permset(entry, &permset)); XCTAssertEqual(0, acl_add_perm(permset, ACL_SEARCH)); XCTAssertEqual(0, acl_set_link_np([sourceFile fileSystemRepresentation], ACL_TYPE_EXTENDED, acl)); XCTAssertEqual(0, acl_free(acl)); } afterDiffHandler:nil]; XCTAssertFalse(success); } - (void)testInvalidDirectoryWithACLInAfterTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *fileManager, NSString * __unused sourceDirectory, NSString *destinationDirectory) { NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([fileManager createDirectoryAtPath:destinationFile withIntermediateDirectories:NO attributes:nil error:nil]); acl_t acl = acl_init(1); acl_entry_t entry; XCTAssertEqual(0, acl_create_entry(&acl, &entry)); acl_permset_t permset; XCTAssertEqual(0, acl_get_permset(entry, &permset)); XCTAssertEqual(0, acl_add_perm(permset, ACL_SEARCH)); XCTAssertEqual(0, acl_set_link_np([destinationFile fileSystemRepresentation], ACL_TYPE_EXTENDED, acl)); XCTAssertEqual(0, acl_free(acl)); } afterDiffHandler:nil]; XCTAssertFalse(success); } - (void)testRegularFileToSymlinkChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceDirectory stringByAppendingPathComponent:@"B"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSData *data = [NSData dataWithBytes:"A" length:1]; XCTAssertTrue([data writeToFile:sourceFile1 atomically:YES]); XCTAssertTrue([data writeToFile:sourceFile2 atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile1 atomically:YES]); NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:destinationFile2 withDestinationPath:@"A" error:&error]) { NSLog(@"Error in creating symlink: %@", error); XCTFail(@"Failed to create symlink"); } // This would fail with version 1.0 XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testSymlinkToRegularFileChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceDirectory stringByAppendingPathComponent:@"B"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"B"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile1 atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile1 atomically:YES]); XCTAssertTrue([data writeToFile:destinationFile2 atomically:YES]); NSError *error = nil; if (![fileManager createSymbolicLinkAtPath:sourceFile2 withDestinationPath:@"A" error:&error]) { NSLog(@"Error in creating symlink: %@", error); XCTFail(@"Failed to create symlink"); } XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testRegularFileToDirectoryChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:sourceFile atomically:YES]); NSError *error = nil; if (![fileManager createDirectoryAtPath:destinationFile withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testDirectoryToRegularFileChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory"); } NSData *data = [NSData dataWithBytes:"loltest" length:7]; XCTAssertTrue([data writeToFile:destinationFile atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } // See issue #514 for more info - (void)testDirectoryToSymlinkChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceDirectory stringByAppendingPathComponent:@"Current"]; NSString *sourceFile3 = [sourceFile2 stringByAppendingPathComponent:@"B"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationFile1 stringByAppendingPathComponent:@"B"]; NSString *destinationFile3 = [destinationDirectory stringByAppendingPathComponent:@"Current"]; NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile1 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory A in source"); } if (![fileManager createDirectoryAtPath:sourceFile2 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory Current in source"); } if (![fileManager createDirectoryAtPath:destinationFile1 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory A in destination"); } if (![fileManager createSymbolicLinkAtPath:destinationFile3 withDestinationPath:@"A/" error:&error]) { NSLog(@"Error in creating symlink: %@", error); XCTFail(@"Failed to create symlink"); } XCTAssertTrue([[self bigData1] writeToFile:sourceFile3 atomically:YES]); XCTAssertTrue([[self bigData1] writeToFile:destinationFile2 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } // Opposite of the test method testDirectoryToSymlinkChange - (void)testSymlinkToDirectoryChange { [self createAndApplyPatchWithHandler:^(NSFileManager *fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile1 = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *sourceFile2 = [sourceFile1 stringByAppendingPathComponent:@"B"]; NSString *sourceFile3 = [sourceDirectory stringByAppendingPathComponent:@"Current"]; NSString *destinationFile1 = [destinationDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile2 = [destinationDirectory stringByAppendingPathComponent:@"Current"]; NSString *destinationFile3 = [destinationFile2 stringByAppendingPathComponent:@"B"]; NSError *error = nil; if (![fileManager createDirectoryAtPath:sourceFile1 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory A in source"); } if (![fileManager createDirectoryAtPath:destinationFile1 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory A in destination"); } if (![fileManager createDirectoryAtPath:destinationFile2 withIntermediateDirectories:NO attributes:nil error:&error]) { NSLog(@"Failed creating directory with error: %@", error); XCTFail("Failed to create directory Current in destination"); } if (![fileManager createSymbolicLinkAtPath:sourceFile3 withDestinationPath:@"A/" error:&error]) { NSLog(@"Error in creating symlink: %@", error); XCTFail(@"Failed to create symlink"); } XCTAssertTrue([[self bigData1] writeToFile:sourceFile2 atomically:YES]); XCTAssertTrue([[self bigData1] writeToFile:destinationFile3 atomically:YES]); XCTAssertFalse([self testDirectoryHashEqualityWithSource:sourceDirectory destination:destinationDirectory]); }]; } - (void)testInvalidCodeSignatureExtendedAttributeInBeforeTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); // the actual data doesn't matter for testing purposes const char xattrValue[] = "hello"; XCTAssertEqual(0, setxattr([sourceFile fileSystemRepresentation], APPLE_CODE_SIGN_XATTR_CODE_DIRECTORY_KEY, xattrValue, sizeof(xattrValue), 0, XATTR_CREATE)); XCTAssertEqual(0, setxattr([sourceFile fileSystemRepresentation], APPLE_CODE_SIGN_XATTR_CODE_REQUIREMENTS_KEY, xattrValue, sizeof(xattrValue), 0, XATTR_CREATE)); XCTAssertEqual(0, setxattr([sourceFile fileSystemRepresentation], APPLE_CODE_SIGN_XATTR_CODE_SIGNATURE_KEY, xattrValue, sizeof(xattrValue), 0, XATTR_CREATE)); } afterDiffHandler:nil]; XCTAssertFalse(success); } - (void)testInvalidCodeSignatureExtendedAttributeInAfterTree { BOOL success = [self createAndApplyPatchWithBeforeDiffHandler:^(NSFileManager *__unused fileManager, NSString *sourceDirectory, NSString *destinationDirectory) { NSString *sourceFile = [sourceDirectory stringByAppendingPathComponent:@"A"]; NSString *destinationFile = [destinationDirectory stringByAppendingPathComponent:@"A"]; XCTAssertTrue([[NSData data] writeToFile:sourceFile atomically:YES]); XCTAssertTrue([[NSData dataWithBytes:"loltest" length:7] writeToFile:destinationFile atomically:YES]); // the actual data doesn't matter for testing purposes const char xattrValue[] = "hello"; XCTAssertEqual(0, setxattr([destinationFile fileSystemRepresentation], APPLE_CODE_SIGN_XATTR_CODE_DIRECTORY_KEY, xattrValue, sizeof(xattrValue), 0, XATTR_CREATE)); XCTAssertEqual(0, setxattr([destinationFile fileSystemRepresentation], APPLE_CODE_SIGN_XATTR_CODE_REQUIREMENTS_KEY, xattrValue, sizeof(xattrValue), 0, XATTR_CREATE)); XCTAssertEqual(0, setxattr([destinationFile fileSystemRepresentation], APPLE_CODE_SIGN_XATTR_CODE_SIGNATURE_KEY, xattrValue, sizeof(xattrValue), 0, XATTR_CREATE)); } afterDiffHandler:nil]; XCTAssertFalse(success); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUCodeSigningVerifierTest.m ================================================ // // SUCodeSigningVerifierTest.m // Sparkle // // Created by Isaac Wankerl on 04/13/2015. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import #import #import "NTSynchronousTask.h" #import "SUCodeSigningVerifier.h" #import "SUFileManager.h" @interface SUCodeSigningVerifierTest : XCTestCase @property (copy) NSString *notSignedAppPath; @property (copy) NSString *validSignedAppPath; @property (copy) NSString *invalidSignedAppPath; @end @implementation SUCodeSigningVerifierTest @synthesize notSignedAppPath = _notSignedAppPath; @synthesize validSignedAppPath = _validSignedAppPath; @synthesize invalidSignedAppPath = _invalidSignedAppPath; - (void)setUp { [super setUp]; NSBundle *unitTestBundle = [NSBundle bundleForClass:[self class]]; NSString *unitTestBundleIdentifier = unitTestBundle.bundleIdentifier; NSString *zippedAppPath = [unitTestBundle pathForResource:@"SparkleTestCodeSignApp" ofType:@"zip"]; SUFileManager *fileManager = [SUFileManager defaultManager]; NSError *tempError = nil; NSURL *tempDir = [fileManager makeTemporaryDirectoryWithPreferredName:unitTestBundleIdentifier appropriateForDirectoryURL:[NSURL fileURLWithPath:zippedAppPath] error:&tempError]; if (tempDir == nil) { XCTFail(@"Failed to create temporary directory with error: %@", tempError); return; } NSString *tempDirPath = tempDir.path; XCTAssertNotNil(tempDirPath); NSError *error = nil; if ([[NSFileManager defaultManager] createDirectoryAtPath:tempDirPath withIntermediateDirectories:YES attributes:nil error:&error]) { if ([self unzip:zippedAppPath toPath:tempDirPath]) { self.notSignedAppPath = [tempDirPath stringByAppendingPathComponent:@"SparkleTestCodeSignApp.app"]; [self setupValidSignedApp]; [self setupInvalidSignedApp]; } else { NSLog(@"Failed to unzip %@", zippedAppPath); } } else { NSLog(@"Failed to created dir %@ with error %@", tempDir, error); } } - (void)tearDown { [super tearDown]; if (self.notSignedAppPath) { NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; [[NSFileManager defaultManager] removeItemAtPath:tempDir error:nil]; } } - (void)setupValidSignedApp { NSError *error = nil; NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; NSString *signedAndValid = [tempDir stringByAppendingPathComponent:@"valid-signed.app"]; if ([[NSFileManager defaultManager] fileExistsAtPath:signedAndValid]) { [[NSFileManager defaultManager] removeItemAtPath:signedAndValid error:NULL]; } if ([[NSFileManager defaultManager] copyItemAtPath:self.notSignedAppPath toPath:signedAndValid error:&error]) { self.validSignedAppPath = signedAndValid; if (![self codesignAppPath:self.validSignedAppPath]) { NSLog(@"Failed to codesign %@", self.validSignedAppPath); } } else { NSLog(@"Failed to copy %@ to %@ with error %@", self.notSignedAppPath, signedAndValid, error); } } - (void)setupInvalidSignedApp { NSError *error = nil; NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; NSString *signedAndInvalid = [tempDir stringByAppendingPathComponent:@"invalid-signed.app"]; if ([[NSFileManager defaultManager] fileExistsAtPath:signedAndInvalid]) { [[NSFileManager defaultManager] removeItemAtPath:signedAndInvalid error:NULL]; } if ([[NSFileManager defaultManager] copyItemAtPath:self.notSignedAppPath toPath:signedAndInvalid error:&error]) { self.invalidSignedAppPath = signedAndInvalid; if ([self codesignAppPath:self.invalidSignedAppPath]) { NSString *fileInAppBundleToRemove = [self.invalidSignedAppPath stringByAppendingPathComponent:@"Contents/Resources/test_app_only_dsa_pub.pem"]; if (![[NSFileManager defaultManager] removeItemAtPath:fileInAppBundleToRemove error:&error]) { NSLog(@"Failed to remove %@ with error %@", fileInAppBundleToRemove, error); } } else { NSLog(@"Failed to codesign %@", self.invalidSignedAppPath); } } else { NSLog(@"Failed to copy %@ to %@ with error %@", self.notSignedAppPath, signedAndInvalid, error); } } - (BOOL)unzip:(NSString *)zipPath toPath:(NSString *)destPath { BOOL success = NO; @try { NTSynchronousTask *task = [[NTSynchronousTask alloc] init]; NSArray *arguments = @[ zipPath ]; [task run:@"/usr/bin/unzip" directory:destPath withArgs:arguments input:nil]; success = ([task result] == 0); } @catch (NSException *exception) { NSLog(@"exception: %@", exception); } return success; } - (BOOL)codesignAppPath:(NSString *)appPath { BOOL success = NO; @try { // ad-hoc signing with the dash NSArray *arguments = @[ @"--force", @"--deep", @"--sign", @"-", appPath ]; NTSynchronousTask *task = [[NTSynchronousTask alloc] init]; [task run:@"/usr/bin/codesign" directory:nil withArgs:arguments input:nil]; success = ([task result] == 0); } @catch (NSException *exception) { NSLog(@"exception: %@", exception); } return success; } - (void)testUnsignedApp { XCTAssertFalse([SUCodeSigningVerifier applicationAtPathIsCodeSigned:self.notSignedAppPath], @"App not expected to be code signed"); NSError *error = nil; XCTAssertFalse([SUCodeSigningVerifier codeSignatureIsValidAtPath:self.notSignedAppPath error:&error], @"signature should not be valid as it's not code signed"); XCTAssertNotNil(error, @"error should not be nil"); } - (void)testValidSignedApp { XCTAssertTrue([SUCodeSigningVerifier applicationAtPathIsCodeSigned:self.validSignedAppPath], @"App expected to be code signed"); NSError *error = nil; XCTAssertTrue([SUCodeSigningVerifier codeSignatureIsValidAtPath:self.validSignedAppPath error:&error], @"signature should be valid"); XCTAssertNil(error, @"error should be nil"); } - (void)testValidSignedCalculatorApp { NSString *appPath = @"/Applications/Calculator.app"; XCTAssertTrue([SUCodeSigningVerifier applicationAtPathIsCodeSigned:appPath], @"App expected to be code signed"); NSError *error = nil; XCTAssertTrue([SUCodeSigningVerifier codeSignatureIsValidAtPath:appPath error:&error], @"signature should be valid"); XCTAssertNil(error, @"error should be nil"); } - (void)testInvalidSignedApp { XCTAssertTrue([SUCodeSigningVerifier applicationAtPathIsCodeSigned:self.invalidSignedAppPath], @"App expected to be code signed, but signature is invalid"); NSError *error = nil; XCTAssertFalse([SUCodeSigningVerifier codeSignatureIsValidAtPath:self.invalidSignedAppPath error:&error], @"signature should not be valid"); XCTAssertNotNil(error, @"error should not be nil"); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUDSAVerifierTest.m ================================================ // // SUDSAVerifierTest.m // Sparkle // // Created by Kornel on 25/07/2014. // Copyright (c) 2014 Sparkle Project. All rights reserved. // #import #import #import "SUDSAVerifier.h" @interface SUDSAVerifierTest : XCTestCase @property NSString *testFile, *pubKeyFile; @end @implementation SUDSAVerifierTest @synthesize testFile, pubKeyFile; - (void)setUp { [super setUp]; self.testFile = [[NSBundle bundleForClass:[self class]] pathForResource:@"signed-test-file" ofType:@"txt"]; self.pubKeyFile = [[NSBundle bundleForClass:[self class]] pathForResource:@"test-pubkey" ofType:@"pem"]; } - (void)testVerifyFileAtPath { NSData *pubKey = [NSData dataWithContentsOfFile:self.pubKeyFile]; XCTAssertNotNil(pubKey, @"Public key must be readable"); NSString *validSig = @"MCwCFCIHCIYYkfZavNzTitTW5tlRp/k5AhQ40poFytqcVhIYdCxQznaXeJPJDQ=="; XCTAssertTrue([self checkFile:self.testFile withPubKey:pubKey signature:validSig], @"Expected valid signature"); XCTAssertFalse([self checkFile:self.testFile withPubKey:[NSData dataWithBytes:"lol" length:3] signature:validSig], @"Invalid pubkey"); XCTAssertFalse([self checkFile:self.pubKeyFile withPubKey:pubKey signature:validSig], @"Wrong file checked"); XCTAssertFalse([self checkFile:self.testFile withPubKey:pubKey signature:@"MCwCFCIHCiYYkfZavNzTitTW5tlRp/k5AhQ40poFytqcVhIYdCxQznaXeJPJDQ=="], @"Expected invalid signature"); XCTAssertTrue([self checkFile:self.testFile withPubKey:pubKey signature:@"MC0CFAsKO7cq2q7L5/FWe6ybVIQkwAwSAhUA2Q8GKsE309eugi/v3Kh1W3w3N8c="], @"Expected valid signature"); XCTAssertFalse([self checkFile:self.testFile withPubKey:pubKey signature:@"MC0CFAsKO7cq2q7L5/FWe6ybVIQkwAwSAhUA2Q8GKsE309eugi/v3Kh1W3w3N8"], @"Expected invalid signature"); } - (BOOL)checkFile:(NSString *)aFile withPubKey:(NSData *)pubKey signature:(NSString *)sigString { SUDSAVerifier *v = [[SUDSAVerifier alloc] initWithPublicKeyData:pubKey]; NSData *sig = [[NSData alloc] initWithBase64EncodedString:sigString options:(NSDataBase64DecodingOptions)0]; return [v verifyFileAtPath:aFile signature:sig]; } - (void)testValidatePath { NSString *pubkey = [NSString stringWithContentsOfFile:self.pubKeyFile encoding:NSASCIIStringEncoding error:nil]; XCTAssertTrue([SUDSAVerifier validatePath:self.testFile withEncodedDSASignature:@"MC0CFFMF3ha5kjvrJ9JTpTR8BenPN9QUAhUAzY06JRdtP17MJewxhK0twhvbKIE=" withPublicDSAKey:pubkey], @"Expected valid signature"); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUFileManagerTest.swift ================================================ // // SUFileManagerTest.swift // Sparkle // // Created by Mayur Pawashe on 9/26/15. // Copyright © 2015 Sparkle Project. All rights reserved. // import XCTest class SUFileManagerTest: XCTestCase { func makeTempFiles(testBlock: (SUFileManager, NSURL, NSURL, NSURL, NSURL, NSURL, NSURL) -> Void) { let fileManager = SUFileManager.defaultManager() let tempDirectoryURL = try! fileManager.makeTemporaryDirectoryWithPreferredName("Sparkle Unit Test Data", appropriateForDirectoryURL: NSURL(fileURLWithPath: NSHomeDirectory())) defer { try! fileManager.removeItemAtURL(tempDirectoryURL) } let ordinaryFileURL = tempDirectoryURL.URLByAppendingPathComponent("a file written by sparkles unit tests") try! "foo".dataUsingEncoding(NSUTF8StringEncoding)!.writeToURL(ordinaryFileURL, options: .DataWritingAtomic) let directoryURL = tempDirectoryURL.URLByAppendingPathComponent("a directory written by sparkles unit tests") try! NSFileManager.defaultManager().createDirectoryAtURL(directoryURL, withIntermediateDirectories: false, attributes: nil) let fileInDirectoryURL = directoryURL.URLByAppendingPathComponent("a file inside a directory written by sparkles unit tests") try! "bar baz".dataUsingEncoding(NSUTF8StringEncoding)!.writeToURL(fileInDirectoryURL, options: .DataWritingAtomic) let validSymlinkURL = tempDirectoryURL.URLByAppendingPathComponent("symlink test") try! NSFileManager.defaultManager().createSymbolicLinkAtURL(validSymlinkURL, withDestinationURL: directoryURL) let invalidSymlinkURL = tempDirectoryURL.URLByAppendingPathComponent("symlink test 2") try! NSFileManager.defaultManager().createSymbolicLinkAtURL(invalidSymlinkURL, withDestinationURL: tempDirectoryURL.URLByAppendingPathComponent("does not exist")) testBlock(fileManager, tempDirectoryURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL) } func testMoveFiles() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.moveItemAtURL(ordinaryFileURL, toURL: directoryURL)) XCTAssertNil(try? fileManager.moveItemAtURL(ordinaryFileURL, toURL: directoryURL.URLByAppendingPathComponent("foo").URLByAppendingPathComponent("bar"))) XCTAssertNil(try? fileManager.moveItemAtURL(rootURL.URLByAppendingPathComponent("does not exist"), toURL: directoryURL)) let newFileURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new file"))! try! fileManager.moveItemAtURL(ordinaryFileURL, toURL: newFileURL) XCTAssertFalse(fileManager._itemExistsAtURL(ordinaryFileURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newFileURL)) let newValidSymlinkURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new symlink"))! try! fileManager.moveItemAtURL(validSymlinkURL, toURL: newValidSymlinkURL) XCTAssertFalse(fileManager._itemExistsAtURL(validSymlinkURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newValidSymlinkURL)) XCTAssertTrue(fileManager._itemExistsAtURL(directoryURL)) let newInvalidSymlinkURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new invalid symlink"))! try! fileManager.moveItemAtURL(invalidSymlinkURL, toURL: newInvalidSymlinkURL) XCTAssertFalse(fileManager._itemExistsAtURL(invalidSymlinkURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newValidSymlinkURL)) let newDirectoryURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new directory"))! try! fileManager.moveItemAtURL(directoryURL, toURL: newDirectoryURL) XCTAssertFalse(fileManager._itemExistsAtURL(directoryURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newDirectoryURL)) XCTAssertFalse(fileManager._itemExistsAtURL(fileInDirectoryURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newDirectoryURL.URLByAppendingPathComponent(fileInDirectoryURL.lastPathComponent!))) } } func testMoveFilesToTrash() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.moveItemAtURLToTrash(rootURL.URLByAppendingPathComponent("does not exist"))) let trashURL = try! NSFileManager.defaultManager().URLForDirectory(.TrashDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false) try! fileManager.moveItemAtURLToTrash(ordinaryFileURL) XCTAssertFalse(fileManager._itemExistsAtURL(ordinaryFileURL)) let ordinaryFileTrashURL = trashURL.URLByAppendingPathComponent(ordinaryFileURL.lastPathComponent!) XCTAssertTrue(fileManager._itemExistsAtURL(ordinaryFileTrashURL)) try! fileManager.removeItemAtURL(ordinaryFileTrashURL) let validSymlinkTrashURL = trashURL.URLByAppendingPathComponent(validSymlinkURL.lastPathComponent!) try! fileManager.moveItemAtURLToTrash(validSymlinkURL) XCTAssertTrue(fileManager._itemExistsAtURL(validSymlinkTrashURL)) XCTAssertTrue(fileManager._itemExistsAtURL(directoryURL)) try! fileManager.removeItemAtURL(validSymlinkTrashURL) try! fileManager.moveItemAtURLToTrash(directoryURL) XCTAssertFalse(fileManager._itemExistsAtURL(directoryURL)) XCTAssertFalse(fileManager._itemExistsAtURL(fileInDirectoryURL)) let directoryTrashURL = trashURL.URLByAppendingPathComponent(directoryURL.lastPathComponent!) XCTAssertTrue(fileManager._itemExistsAtURL(directoryTrashURL)) XCTAssertTrue(fileManager._itemExistsAtURL(directoryTrashURL.URLByAppendingPathComponent(fileInDirectoryURL.lastPathComponent!))) try! fileManager.removeItemAtURL(directoryTrashURL) } } func testCopyFiles() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.copyItemAtURL(ordinaryFileURL, toURL: directoryURL)) XCTAssertNil(try? fileManager.copyItemAtURL(ordinaryFileURL, toURL: directoryURL.URLByAppendingPathComponent("foo").URLByAppendingPathComponent("bar"))) XCTAssertNil(try? fileManager.copyItemAtURL(rootURL.URLByAppendingPathComponent("does not exist"), toURL: directoryURL)) let newFileURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new file"))! try! fileManager.copyItemAtURL(ordinaryFileURL, toURL: newFileURL) XCTAssertTrue(fileManager._itemExistsAtURL(ordinaryFileURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newFileURL)) let newSymlinkURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new symlink file"))! try! fileManager.copyItemAtURL(invalidSymlinkURL, toURL: newSymlinkURL) XCTAssertTrue(fileManager._itemExistsAtURL(newSymlinkURL)) let newDirectoryURL = (ordinaryFileURL.URLByDeletingLastPathComponent?.URLByAppendingPathComponent("new directory"))! try! fileManager.copyItemAtURL(directoryURL, toURL: newDirectoryURL) XCTAssertTrue(fileManager._itemExistsAtURL(directoryURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newDirectoryURL)) XCTAssertTrue(fileManager._itemExistsAtURL(fileInDirectoryURL)) XCTAssertTrue(fileManager._itemExistsAtURL(newDirectoryURL.URLByAppendingPathComponent(fileInDirectoryURL.lastPathComponent!))) } } func testRemoveFiles() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.removeItemAtURL(rootURL.URLByAppendingPathComponent("does not exist"))) try! fileManager.removeItemAtURL(ordinaryFileURL) XCTAssertFalse(fileManager._itemExistsAtURL(ordinaryFileURL)) try! fileManager.removeItemAtURL(validSymlinkURL) XCTAssertFalse(fileManager._itemExistsAtURL(validSymlinkURL)) XCTAssertTrue(fileManager._itemExistsAtURL(directoryURL)) try! fileManager.removeItemAtURL(directoryURL) XCTAssertFalse(fileManager._itemExistsAtURL(directoryURL)) XCTAssertFalse(fileManager._itemExistsAtURL(fileInDirectoryURL)) } } func testReleaseFilesFromQuarantine() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in try! fileManager.releaseItemFromQuarantineAtRootURL(ordinaryFileURL) try! fileManager.releaseItemFromQuarantineAtRootURL(directoryURL) try! fileManager.releaseItemFromQuarantineAtRootURL(validSymlinkURL) let quarantineData = "does not really matter what is here".cStringUsingEncoding(NSUTF8StringEncoding)! let quarantineDataLength = Int(strlen(quarantineData)) XCTAssertEqual(0, setxattr(ordinaryFileURL.path!, SUAppleQuarantineIdentifier, quarantineData, quarantineDataLength, 0, XATTR_CREATE)) XCTAssertGreaterThan(getxattr(ordinaryFileURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW), 0) try! fileManager.releaseItemFromQuarantineAtRootURL(ordinaryFileURL) XCTAssertEqual(-1, getxattr(ordinaryFileURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW)) XCTAssertEqual(0, setxattr(directoryURL.path!, SUAppleQuarantineIdentifier, quarantineData, quarantineDataLength, 0, XATTR_CREATE)) XCTAssertGreaterThan(getxattr(directoryURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW), 0) XCTAssertEqual(0, setxattr(fileInDirectoryURL.path!, SUAppleQuarantineIdentifier, quarantineData, quarantineDataLength, 0, XATTR_CREATE)) XCTAssertGreaterThan(getxattr(fileInDirectoryURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW), 0) // Extended attributes can't be set on symbolic links currently try! fileManager.releaseItemFromQuarantineAtRootURL(validSymlinkURL) XCTAssertGreaterThan(getxattr(directoryURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW), 0) XCTAssertEqual(-1, getxattr(validSymlinkURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW)) try! fileManager.releaseItemFromQuarantineAtRootURL(directoryURL) XCTAssertEqual(-1, getxattr(directoryURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW)) XCTAssertEqual(-1, getxattr(fileInDirectoryURL.path!, SUAppleQuarantineIdentifier, nil, 0, 0, XATTR_NOFOLLOW)) } } func groupIDAtPath(path: String) -> gid_t { let attributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(path) let groupID = attributes[NSFileGroupOwnerAccountID] as! NSNumber return groupID.unsignedIntValue } // Only the super user can alter user IDs, so changing user IDs is not tested here // Instead we try to change the group ID - we just have to be a member of that group func testAlterFilesGroupID() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.changeOwnerAndGroupOfItemAtRootURL(ordinaryFileURL, toMatchURL: rootURL.URLByAppendingPathComponent("does not exist"))) XCTAssertNil(try? fileManager.changeOwnerAndGroupOfItemAtRootURL(rootURL.URLByAppendingPathComponent("does not exist"), toMatchURL: ordinaryFileURL)) let everyoneGroup = getgrnam("everyone") let everyoneGroupID = everyoneGroup.memory.gr_gid let staffGroup = getgrnam("staff") let staffGroupID = staffGroup.memory.gr_gid XCTAssertNotEqual(staffGroupID, everyoneGroupID) XCTAssertEqual(staffGroupID, self.groupIDAtPath(ordinaryFileURL.path!)) XCTAssertEqual(staffGroupID, self.groupIDAtPath(directoryURL.path!)) XCTAssertEqual(staffGroupID, self.groupIDAtPath(fileInDirectoryURL.path!)) XCTAssertEqual(staffGroupID, self.groupIDAtPath(validSymlinkURL.path!)) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(fileInDirectoryURL, toMatchURL: ordinaryFileURL) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(ordinaryFileURL, toMatchURL: ordinaryFileURL) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(validSymlinkURL, toMatchURL: ordinaryFileURL) XCTAssertEqual(staffGroupID, self.groupIDAtPath(ordinaryFileURL.path!)) XCTAssertEqual(staffGroupID, self.groupIDAtPath(directoryURL.path!)) XCTAssertEqual(staffGroupID, self.groupIDAtPath(validSymlinkURL.path!)) XCTAssertEqual(0, chown(ordinaryFileURL.path!, getuid(), everyoneGroupID)) XCTAssertEqual(everyoneGroupID, self.groupIDAtPath(ordinaryFileURL.path!)) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(fileInDirectoryURL, toMatchURL: ordinaryFileURL) XCTAssertEqual(everyoneGroupID, self.groupIDAtPath(fileInDirectoryURL.path!)) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(fileInDirectoryURL, toMatchURL: directoryURL) XCTAssertEqual(staffGroupID, self.groupIDAtPath(fileInDirectoryURL.path!)) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(validSymlinkURL, toMatchURL: ordinaryFileURL) XCTAssertEqual(everyoneGroupID, self.groupIDAtPath(validSymlinkURL.path!)) try! fileManager.changeOwnerAndGroupOfItemAtRootURL(directoryURL, toMatchURL: ordinaryFileURL) XCTAssertEqual(everyoneGroupID, self.groupIDAtPath(directoryURL.path!)) XCTAssertEqual(everyoneGroupID, self.groupIDAtPath(fileInDirectoryURL.path!)) } } func testUpdateFileModificationTime() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.updateModificationAndAccessTimeOfItemAtURL(rootURL.URLByAppendingPathComponent("does not exist"))) let oldOrdinaryFileAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(ordinaryFileURL.path!) let oldDirectoryAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(directoryURL.path!) let oldValidSymlinkAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(validSymlinkURL.path!) sleep(1); // wait for clock to advance try! fileManager.updateModificationAndAccessTimeOfItemAtURL(ordinaryFileURL) try! fileManager.updateModificationAndAccessTimeOfItemAtURL(directoryURL) try! fileManager.updateModificationAndAccessTimeOfItemAtURL(validSymlinkURL) let newOrdinaryFileAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(ordinaryFileURL.path!) XCTAssertGreaterThan((newOrdinaryFileAttributes[NSFileModificationDate] as! NSDate).timeIntervalSinceDate(oldOrdinaryFileAttributes[NSFileModificationDate] as! NSDate), 0) let newDirectoryAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(directoryURL.path!) XCTAssertGreaterThan((newDirectoryAttributes[NSFileModificationDate] as! NSDate).timeIntervalSinceDate(oldDirectoryAttributes[NSFileModificationDate] as! NSDate), 0) let newSymlinkAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(validSymlinkURL.path!) XCTAssertGreaterThan((newSymlinkAttributes[NSFileModificationDate] as! NSDate).timeIntervalSinceDate(oldValidSymlinkAttributes[NSFileModificationDate] as! NSDate), 0) } } func testFileExists() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertTrue(fileManager._itemExistsAtURL(ordinaryFileURL)) XCTAssertTrue(fileManager._itemExistsAtURL(directoryURL)) XCTAssertFalse(fileManager._itemExistsAtURL(rootURL.URLByAppendingPathComponent("does not exist"))) var isOrdinaryFileDirectory: ObjCBool = false XCTAssertTrue(fileManager._itemExistsAtURL(ordinaryFileURL, isDirectory: &isOrdinaryFileDirectory) && !isOrdinaryFileDirectory) var isDirectoryADirectory: ObjCBool = false XCTAssertTrue(fileManager._itemExistsAtURL(directoryURL, isDirectory: &isDirectoryADirectory) && isDirectoryADirectory) XCTAssertFalse(fileManager._itemExistsAtURL(rootURL.URLByAppendingPathComponent("does not exist"), isDirectory: nil)) XCTAssertTrue(fileManager._itemExistsAtURL(validSymlinkURL)) var validSymlinkIsADirectory: ObjCBool = false XCTAssertTrue(fileManager._itemExistsAtURL(validSymlinkURL, isDirectory: &validSymlinkIsADirectory) && !validSymlinkIsADirectory) // Symlink should still exist even if it doesn't point to a file that exists XCTAssertTrue(fileManager._itemExistsAtURL(invalidSymlinkURL)) var invalidSymlinkIsADirectory: ObjCBool = false XCTAssertTrue(fileManager._itemExistsAtURL(invalidSymlinkURL, isDirectory: &invalidSymlinkIsADirectory) && !invalidSymlinkIsADirectory) } } func testMakeDirectory() { makeTempFiles() { fileManager, rootURL, ordinaryFileURL, directoryURL, fileInDirectoryURL, validSymlinkURL, invalidSymlinkURL in XCTAssertNil(try? fileManager.makeDirectoryAtURL(ordinaryFileURL)) XCTAssertNil(try? fileManager.makeDirectoryAtURL(directoryURL)) XCTAssertNil(try? fileManager.makeDirectoryAtURL(rootURL.URLByAppendingPathComponent("this should").URLByAppendingPathComponent("be a failure"))) let newDirectoryURL = rootURL.URLByAppendingPathComponent("new test directory") XCTAssertFalse(fileManager._itemExistsAtURL(newDirectoryURL)) try! fileManager.makeDirectoryAtURL(newDirectoryURL) var isDirectory: ObjCBool = false XCTAssertTrue(fileManager._itemExistsAtURL(newDirectoryURL, isDirectory: &isDirectory)) try! fileManager.removeItemAtURL(directoryURL) XCTAssertNil(try? fileManager.makeDirectoryAtURL(validSymlinkURL)) } } func testAcquireBadAuthorization() { let fileManager = SUFileManager.defaultManager() XCTAssertNil(try? fileManager._acquireAuthorization()) } } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUInstallerTest.m ================================================ // // SUInstallerTest.m // Sparkle // // Created by Kornel on 24/04/2015. // Copyright (c) 2015 Sparkle Project. All rights reserved. // #import #import #import "SUHost.h" #import "SUInstaller.h" #import @interface SUInstallerTest : XCTestCase @end @implementation SUInstallerTest - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } #if __clang_major__ >= 6 - (void)testInstallIfRoot { uid_t uid = getuid(); if (uid) { NSLog(@"Test must be run as root: sudo xctest -XCTest SUInstallerTest 'Sparkle Unit Tests.xctest'"); return; } NSString *expectedDestination = @"/tmp/sparklepkgtest.app"; NSFileManager *fm = [NSFileManager defaultManager]; [fm removeItemAtPath:expectedDestination error:nil]; XCTAssertFalse([fm fileExistsAtPath:expectedDestination isDirectory:nil]); XCTestExpectation *done = [self expectationWithDescription:@"install finished"]; NSBundle *bundle = [NSBundle bundleForClass:[self class]]; NSString *path = [bundle pathForResource:@"test.sparkle_guided" ofType:@"pkg"]; XCTAssertNotNil(path); SUHost *host = [[SUHost alloc] initWithBundle:bundle]; NSString *fileOperationToolPath = [bundle pathForResource:@""SPARKLE_FILEOP_TOOL_NAME ofType:@""]; XCTAssertNotNil(fileOperationToolPath); [SUInstaller installFromUpdateFolder:[path stringByDeletingLastPathComponent] overHost:host installationPath:@"/tmp" fileOperationToolPath:fileOperationToolPath versionComparator:nil completionHandler:^(NSError *error) { if (error != nil) { NSLog(@"Underlying error: %@", [error.userInfo objectForKey:NSUnderlyingErrorKey]); } XCTAssertNil(error); XCTAssertTrue([fm fileExistsAtPath:expectedDestination isDirectory:nil]); [done fulfill]; }]; [self waitForExpectationsWithTimeout:40 handler:nil]; [fm removeItemAtPath:expectedDestination error:nil]; } #endif @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUUnarchiverTest.swift ================================================ // // SUUnarchiverTest.swift // Sparkle // // Created by Mayur Pawashe on 9/4/15. // Copyright © 2015 Sparkle Project. All rights reserved. // import XCTest class SUUnarchiverTest: XCTestCase, SUUnarchiverDelegate { var password: String? = nil var unarchivedExpectation: XCTestExpectation? = nil var unarchivedResult: Bool = false func unarchiver(unarchiver: SUUnarchiver!, extractedProgress progress: Double) { } func unarchiverDidFail(unarchiver: SUUnarchiver!) { self.unarchivedResult = false self.unarchivedExpectation!.fulfill() } func unarchiverDidFinish(unarchiver: SUUnarchiver!) { self.unarchivedResult = true self.unarchivedExpectation!.fulfill() } func unarchiveTestAppWithExtension(archiveExtension: String) { let appName = "SparkleTestCodeSignApp" let archiveResourceURL = NSBundle(forClass: self.dynamicType).URLForResource(appName, withExtension: archiveExtension)! let fileManager = NSFileManager.defaultManager() let tempDirectoryURL = try! fileManager.URLForDirectory(.ItemReplacementDirectory, inDomain: .UserDomainMask, appropriateForURL: NSURL(fileURLWithPath: NSHomeDirectory()), create: true) defer { try! fileManager.removeItemAtURL(tempDirectoryURL) } let tempArchiveURL = tempDirectoryURL.URLByAppendingPathComponent(archiveResourceURL.lastPathComponent!) let extractedAppURL = tempDirectoryURL.URLByAppendingPathComponent(appName).URLByAppendingPathExtension("app") try! fileManager.copyItemAtURL(archiveResourceURL, toURL: tempArchiveURL) self.unarchivedExpectation = super.expectationWithDescription("Unarchived Application (format: \(archiveExtension))") let unarchiver = SUUnarchiver(forPath: tempArchiveURL.path!, updatingHostBundlePath: nil, withPassword: self.password) unarchiver.delegate = self unarchiver.start() super.waitForExpectationsWithTimeout(7.0, handler: nil) XCTAssertTrue(self.unarchivedResult) XCTAssertTrue(fileManager.fileExistsAtPath(extractedAppURL.path!)) XCTAssertEqual("6a60ab31430cfca8fb499a884f4a29f73e59b472", hashOfTree(extractedAppURL.path!)) } func testUnarchivingZip() { self.unarchiveTestAppWithExtension("zip") } func testUnarchivingTarDotGz() { self.unarchiveTestAppWithExtension("tar.gz") } func testUnarchivingTar() { self.unarchiveTestAppWithExtension("tar") } func testUnarchivingTarDotBz2() { self.unarchiveTestAppWithExtension("tar.bz2") } func testUnarchivingTarDotXz() { self.unarchiveTestAppWithExtension("tar.xz") } func testUnarchivingDmg() { self.unarchiveTestAppWithExtension("dmg") } func testUnarchivingEncryptedDmg() { self.password = "testpass"; self.unarchiveTestAppWithExtension("enc.dmg") } } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUUpdaterTest.m ================================================ // // SUUpdaterTest.m // Sparkle // // Created by Jake Petroules on 2014-06-29. // Copyright (c) 2014 Sparkle Project. All rights reserved. // #import #import "SUConstants.h" #import "SUUpdater.h" @interface SUUpdaterTest : XCTestCase @property (strong) NSOperationQueue *queue; @property (strong) SUUpdater *updater; @end @implementation SUUpdaterTest @synthesize queue; @synthesize updater; - (void)setUp { [super setUp]; self.queue = [[NSOperationQueue alloc] init]; self.updater = [[SUUpdater alloc] initForBundle:[NSBundle bundleForClass:[self class]]]; self.updater.delegate = self; } - (void)tearDown { self.updater = nil; self.queue = nil; [super tearDown]; } - (NSString *)feedURLStringForUpdater:(SUUpdater *) __unused updater { return @"https://test.example.com"; } - (void)testFeedURL { [self.updater feedURL]; // this WON'T throw [self.queue addOperationWithBlock:^{ XCTAssertTrue(![NSThread isMainThread]); @try { [self.updater feedURL]; XCTFail(@"feedURL did not throw an exception when called on a secondary thread"); } @catch (NSException *exception) { NSLog(@"%@", exception); } }]; [self.queue waitUntilAllOperationsAreFinished]; } - (void)testSetTestFeedURL { [self.updater setFeedURL:[NSURL URLWithString:@""]]; // this WON'T throw [self.queue addOperationWithBlock:^{ XCTAssertTrue(![NSThread isMainThread]); @try { [self.updater setFeedURL:[NSURL URLWithString:@""]]; XCTFail(@"setFeedURL: did not throw an exception when called on a secondary thread"); } @catch (NSException *exception) { NSLog(@"%@", exception); } }]; [self.queue waitUntilAllOperationsAreFinished]; } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SUVersionComparisonTest.m ================================================ // // SUVersionComparisonTest.m // Sparkle // // Created by Andy Matuschak on 4/15/08. // Copyright 2008 Andy Matuschak. All rights reserved. // #import "SUStandardVersionComparator.h" #import @interface SUVersionComparisonTestCase : XCTestCase { } @end @implementation SUVersionComparisonTestCase #define SUAssertOrder(a, b, c) XCTAssertTrue([[SUStandardVersionComparator defaultComparator] compareVersion:a toVersion:b] == c, @"b should be newer than a!") #define SUAssertAscending(a, b) SUAssertOrder(a, b, NSOrderedAscending) #define SUAssertDescending(a, b) SUAssertOrder(a, b, NSOrderedDescending) #define SUAssertEqual(a, b) SUAssertOrder(a, b, NSOrderedSame) - (void)testNumbers { SUAssertAscending(@"1.0", @"1.1"); SUAssertEqual(@"1.0", @"1.0"); SUAssertDescending(@"2.0", @"1.1"); SUAssertDescending(@"0.1", @"0.0.1"); //SUAssertDescending(@".1", @"0.0.1"); Known bug, but I'm not sure I care. SUAssertAscending(@"0.1", @"0.1.2"); } - (void)testPrereleases { SUAssertAscending(@"1.5.5", @"1.5.6a1"); SUAssertAscending(@"1.1.0b1", @"1.1.0b2"); SUAssertAscending(@"1.1.1b2", @"1.1.2b1"); SUAssertAscending(@"1.1.1b2", @"1.1.2a1"); SUAssertAscending(@"1.0a1", @"1.0b1"); SUAssertAscending(@"1.0b1", @"1.0"); SUAssertAscending(@"0.9", @"1.0a1"); SUAssertAscending(@"1.0b", @"1.0b2"); SUAssertAscending(@"1.0b10", @"1.0b11"); SUAssertAscending(@"1.0b9", @"1.0b10"); SUAssertAscending(@"1.0rc", @"1.0"); SUAssertAscending(@"1.0b", @"1.0"); SUAssertAscending(@"1.0pre1", @"1.0"); } - (void)testVersionsWithBuildNumbers { SUAssertAscending(@"1.0 (1234)", @"1.0 (1235)"); SUAssertAscending(@"1.0b1 (1234)", @"1.0 (1234)"); SUAssertAscending(@"1.0b5 (1234)", @"1.0b5 (1235)"); SUAssertAscending(@"1.0b5 (1234)", @"1.0.1b5 (1234)"); SUAssertAscending(@"1.0.1b5 (1234)", @"1.0.1b6 (1234)"); SUAssertAscending(@"2.0.0.2429", @"2.0.0.2430"); SUAssertAscending(@"1.1.1.1818", @"2.0.0.2430"); SUAssertAscending(@"3.3 (5847)", @"3.3.1b1 (5902)"); } - (void)testWordsWithSpaceInFront { // SUAssertAscending(@"1.0 beta", @"1.0"); // SUAssertAscending(@"1.0 - beta", @"1.0"); // SUAssertAscending(@"1.0 alpha", @"1.0 beta"); // SUAssertEqual(@"1.0 - beta", @"1.0beta"); // SUAssertEqual(@"1.0 - beta", @"1.0 beta"); } - (void)testVersionsWithReverseDateBasedNumbers { SUAssertAscending(@"201210251627", @"201211051041"); } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/Sparkle Unit Tests-Bridging-Header.h ================================================ // // Use this file to import your target's public headers that you would like to expose to Swift. // #import "SUUnarchiver.h" #import "SUPipedUnarchiver.h" #import "SUBinaryDeltaCommon.h" #import "SUFileManager.h" #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUBasicUpdateDriver.h" #import "SUVersionComparisonProtocol.h" #import "SUStandardVersionComparator.h" // Duplicated to avoid exporting a private symbol from SUFileManager static const char *SUAppleQuarantineIdentifier = "com.apple.quarantine"; @interface SUFileManager (Private) - (BOOL)_acquireAuthorizationWithError:(NSError *__autoreleasing *)error; - (BOOL)_itemExistsAtURL:(NSURL *)fileURL; - (BOOL)_itemExistsAtURL:(NSURL *)fileURL isDirectory:(BOOL *)isDirectory; @end @interface SUBasicUpdateDriver (Private) + (SUAppcastItem *)bestItemFromAppcastItems:(NSArray *)appcastItems getDeltaItem:(SUAppcastItem * __autoreleasing *)deltaItem withHostVersion:(NSString *)hostVersion comparator:(id)comparator; @end @interface SUAppcast (Private) - (NSArray *)parseAppcastItemsFromXMLFile:(NSURL *)appcastFile error:(NSError *__autoreleasing*)errorp; @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Tests/SparkleTests-Info.plist ================================================ CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleSignature ???? CFBundleVersion 1.0 SUEnableAutomaticChecks SUEnableSystemProfiling SUPublicDSAKeyFile test-pubkey.pem ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/UITests/SUTestApplicationTest.swift ================================================ // // SUTestApplicationTest.swift // Sparkle // // Created by Mayur Pawashe on 8/27/15. // Copyright © 2015 Sparkle Project. All rights reserved. // import XCTest class SUTestApplicationTest: XCTestCase { var testApplicationURL: NSURL? = nil var testApplicationBackupURL: NSURL? = nil var tempDirectoryURL: NSURL? = nil func runningTestApplication() -> NSRunningApplication { // TODO: don't hardcode bundle ID? let runningApplications = NSRunningApplication.runningApplicationsWithBundleIdentifier("org.sparkle-project.SparkleTestApp") XCTAssertEqual(runningApplications.count, 1, "More than one or zero running instances of the Test Application are found") return runningApplications[0] } override func setUp() { super.setUp() let app = XCUIApplication() app.launch() XCTAssertFalse(app.dialogs["alert"].staticTexts["Update succeeded!"].exists, "Update is already installed; please do a clean build") // We need to grab the app URL so we can back up the app // When a successful test is over we'll revert the update let testApplicationURL = self.runningTestApplication().bundleURL! let tempDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.ItemReplacementDirectory, inDomain: NSSearchPathDomainMask.UserDomainMask, appropriateForURL: testApplicationURL, create: true) let testApplicationBackupURL = tempDirectoryURL.URLByAppendingPathComponent(testApplicationURL.lastPathComponent!) try! NSFileManager.defaultManager().copyItemAtURL(testApplicationURL, toURL: testApplicationBackupURL) self.testApplicationURL = testApplicationURL self.testApplicationBackupURL = testApplicationBackupURL self.tempDirectoryURL = tempDirectoryURL // when we add more tests we don't want to continue after a failure // since a test can fail after the new app has been placed in self.continueAfterFailure = false } override func tearDown() { // Terminate just in case the app hasn't already quit XCUIApplication().terminate() // Revert our update with the original test application var resultingURL: NSURL? = nil try! NSFileManager.defaultManager().replaceItemAtURL(self.testApplicationURL!, withItemAtURL: self.testApplicationBackupURL!, backupItemName: nil, options: .UsingNewMetadataOnly, resultingItemURL: &resultingURL) do { try NSFileManager.defaultManager().removeItemAtURL(self.tempDirectoryURL!) } catch let error as NSError { NSLog("Failed to remove temporary directory \(self.tempDirectoryURL) with error: \(error)") } // The URL we get back from NSFileManager should be the same.. XCTAssertEqual(self.testApplicationURL, resultingURL, "Test application was replaced, but at a different location") super.tearDown() } func testRegularUpdate() { let app = XCUIApplication() let menuBarsQuery = app.menuBars menuBarsQuery.menuBarItems["Sparkle Test App"].click() // If the update window already showed up automatically, this option will be disabled. // Even if it's disabled, attempting to click it will do no harm and continue on menuBarsQuery.menuItems["Check for Updates…"].click() app.dialogs["SUUpdateAlert"].buttons["Install Update"].click() app.buttons["Install and Relaunch"].click() // Wait for the new updated app to finish launching so we can test if it's the frontmost app sleep(3) // Our new updated app should be launched now. Test if it's the active app // We used to run into timing issues where the updated app sometimes may not show up as the frontmost one XCTAssertTrue(self.runningTestApplication().active) // Grab another XCUIApplication instance rather than using the old one, just in case let updatedApp = XCUIApplication() // Check if we can click the "Update succeeded!" label rather than using the exists property // Checking the exists property doesn't reload the "cache" and as such the UI tests still would think we are // referring to the older app. Clicking does force the UI tests to find the new app however updatedApp.dialogs["alert"].staticTexts["Update succeeded!"].click() updatedApp.dialogs["alert"].buttons["OK"].click() } } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/UITests/UITests-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/CocoatechCore/NTSynchronousTask.h ================================================ // // NTSynchronousTask.h // CocoatechCore // // Created by Steve Gehrman on 9/29/05. // Copyright 2005 Steve Gehrman. All rights reserved. // #ifndef NTSYNCHRONOUSTASK_H #define NTSYNCHRONOUSTASK_H @interface NTSynchronousTask : NSObject @property (readonly, strong) NSData *output; @property (readonly) int result; // pass nil for directory if not needed // returns the result +(int) task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input output: (NSData**)outData; +(NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input; - (void)run:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input; @end #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/CocoatechCore/NTSynchronousTask.m ================================================ // // NTSynchronousTask.m // CocoatechCore // // Created by Steve Gehrman on 9/29/05. // Copyright 2005 Steve Gehrman. All rights reserved. // #import "NTSynchronousTask.h" @interface NTSynchronousTask () @property (strong) NSTask *task; @property (strong) NSPipe *outputPipe; @property (strong) NSPipe *inputPipe; @property (readwrite, strong) NSData *output; @property (getter = isDone) BOOL done; @property (readwrite) int result; @end @implementation NTSynchronousTask @synthesize output = mv_output; @synthesize result = mv_result; @synthesize task = mv_task; @synthesize outputPipe = mv_outputPipe; @synthesize inputPipe = mv_inputPipe; @synthesize done = mv_done; - (void)taskOutputAvailable:(NSNotification*)note { self.output = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem]; self.done = YES; } - (void)taskDidTerminate:(NSNotification *) __unused note { self.result = [self.task terminationStatus]; } - (instancetype)init { self = [super init]; if (self) { self.task = [[NSTask alloc] init]; self.outputPipe = [[NSPipe alloc] init]; self.inputPipe = [[NSPipe alloc] init]; self.task.standardInput = self.inputPipe; self.task.standardOutput = self.outputPipe; self.task.standardError = self.outputPipe; } return self; } //---------------------------------------------------------- // dealloc //---------------------------------------------------------- - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)run:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input { BOOL success = NO; if (currentDirectory) { self.task.currentDirectoryPath = currentDirectory; } self.task.launchPath = toolPath; self.task.arguments = args; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskOutputAvailable:) name:NSFileHandleReadToEndOfFileCompletionNotification object:[[self outputPipe] fileHandleForReading]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidTerminate:) name:NSTaskDidTerminateNotification object:[self task]]; [[[self outputPipe] fileHandleForReading] readToEndOfFileInBackgroundAndNotifyForModes:@[NSDefaultRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode]]; @try { [self.task launch]; success = YES; } @catch (NSException *) { } if (success) { if (input) { // feed the running task our input [[self.inputPipe fileHandleForWriting] writeData:input]; [[self.inputPipe fileHandleForWriting] closeFile]; } // loop until we are done receiving the data if (!self.done) { double resolution = 1; BOOL isRunning; NSDate* next; do { next = [NSDate dateWithTimeIntervalSinceNow:resolution]; isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:next]; } while (isRunning && !self.done); } } } + (NSData*)task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input { @autoreleasepool { NSData* result = nil; // we need this wacky pool here, otherwise we run out of pipes, the pipes are internally autoreleased @try { NTSynchronousTask* task = [[NTSynchronousTask alloc] init]; [task run:toolPath directory:currentDirectory withArgs:args input:input]; if ([task result] == 0) { result = [task output]; } } @catch (NSException *) { } return result; } } +(int) task:(NSString*)toolPath directory:(NSString*)currentDirectory withArgs:(NSArray*)args input:(NSData*)input output: (NSData*__autoreleasing *)outData { // we need this wacky pool here, otherwise we run out of pipes, the pipes are internally autoreleased @autoreleasepool { int taskResult = 0; if (outData) { *outData = nil; } @try { NTSynchronousTask* task = [[NTSynchronousTask alloc] init]; [task run:toolPath directory:currentDirectory withArgs:args input:input]; taskResult = [task result]; if (outData) { *outData = [task output]; } } @catch (NSException *) { taskResult = errCppGeneral; } return taskResult; } } @end ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/bscommon.c ================================================ /* * bscommon.c * Sparkle * * Created by Mayur Pawashe on 5/16/16. */ #include "bscommon.h" #include u_char *readfile(const char *filename, off_t *outSize) { int success = -1; u_char *buffer = NULL; FILE *file = fopen(filename, "r"); if (file == NULL) { goto cleanup; } if (fseek(file, 0L, SEEK_END) != 0) { goto cleanup; } long offset = ftell(file); if (offset == -1) { goto cleanup; } size_t size = (size_t)offset; if (outSize != NULL) { *outSize = (off_t)size; } /* Allocate size + 1 bytes instead of newsize bytes to ensure that we never try to malloc(0) and get a NULL pointer */ buffer = malloc(size + 1); if (buffer == NULL) { goto cleanup; } if (fseek(file, 0L, SEEK_SET) != 0) { goto cleanup; } if (fread(buffer, 1, size, file) < size) { goto cleanup; } success = 0; cleanup: if (success == -1) { free(buffer); buffer = NULL; } if (file != NULL) { fclose(file); } return buffer; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/bscommon.h ================================================ /* * bscommon.h * Sparkle * * Created by Mayur Pawashe on 5/16/16. */ #ifndef BS_COMMON_H #define BS_COMMON_H #include #include u_char *readfile(const char *filename, off_t *outSize); #endif ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/bsdiff.c ================================================ /*- * Copyright 2003 - 2005 Colin Percival * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if 0 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c, v 1.1 2005/08/06 01:59:05 cperciva Exp $"); #endif #include #include "sais.h" #include #include #include #include #include #include #include "bscommon.h" #define MIN(x, y) (((x)<(y)) ? (x) : (y)) /* matchlen(old, oldsize, new, newsize) * * Returns the length of the longest common prefix between 'old' and 'new'. */ static off_t matchlen(u_char *old, off_t oldsize, u_char *new, off_t newsize) { off_t i; for (i = 0; (i < oldsize) && (i < newsize); i++) { if (old[i] != new[i]) break; } return i; } /* search(I, old, oldsize, new, newsize, st, en, pos) * * Searches for the longest prefix of 'new' that occurs in 'old', stores its * offset in '*pos', and returns its length. 'I' should be the suffix sort of * 'old', and 'st' and 'en' are the lowest and highest indices in the suffix * sort to consider. If you're searching all suffixes, 'st = 0' and 'en = * oldsize - 1'. */ static off_t search(off_t *I, u_char *old, off_t oldsize, u_char *new, off_t newsize, off_t st, off_t en, off_t *pos) { off_t x, y; if (en - st < 2) { x = matchlen(old + I[st], oldsize - I[st], new, newsize); y = matchlen(old + I[en], oldsize - I[en], new, newsize); if (x > y) { *pos = I[st]; return x; } else { *pos = I[en]; return y; } } x = st + (en - st)/2; if (memcmp(old + I[x], new, (size_t)(MIN(oldsize - I[x], newsize))) < 0) { return search(I, old, oldsize, new, newsize, x, en, pos); } else { return search(I, old, oldsize, new, newsize, st, x, pos); }; } /* offtout(x, buf) * * Writes the off_t 'x' portably to the array 'buf'. */ static void offtout(off_t x, u_char *buf) { off_t y; if (x < 0) y = -x; else y = x; buf[0] = (u_char)(y % 256); y -= buf[0]; y = y/256; buf[1] = (u_char)(y%256); y -= buf[1]; y = y/256; buf[2] = (u_char)(y%256); y -= buf[2]; y = y/256; buf[3] = (u_char)(y%256); y -= buf[3]; y = y/256; buf[4] = (u_char)(y%256); y -= buf[4]; y = y/256; buf[5] = (u_char)(y%256); y -= buf[5]; y = y/256; buf[6] = (u_char)(y%256); y -= buf[6]; y = y/256; buf[7] = (u_char)(y%256); if (x < 0) buf[7] |= 0x80; } int bsdiff(int argc, char *argv[]); // Added by AMM: suppresses a warning about the following not having a prototype. int bsdiff(int argc, char *argv[]) { u_char *old = NULL,*new = NULL; /* contents of old, new files */ off_t oldsize = 0, newsize = 0; /* length of old, new files */ off_t *I = NULL,*V = NULL; /* arrays used for suffix sort; I is ordering */ off_t scan = 0; /* position of current match in old file */ off_t pos = 0; /* position of current match in new file */ off_t len = 0; /* length of current match */ off_t lastscan = 0; /* position of previous match in old file */ off_t lastpos = 0; /* position of previous match in new file */ off_t lastoffset = 0; /* lastpos - lastscan */ off_t oldscore = 0, scsc = 0; /* temp variables in match search */ off_t s = 0, Sf = 0, lenf = 0, Sb = 0, lenb = 0; /* temp vars in match extension */ off_t overlap = 0, Ss = 0, lens = 0; off_t i = 0; off_t dblen = 0, eblen = 0; /* length of diff, extra sections */ u_char *db = NULL,*eb = NULL; /* contents of diff, extra sections */ u_char buf[8] = {0}; u_char header[32] = {0}; FILE * pf = NULL; int exitstatus = -1; if (argc != 4) { warnx("usage: %s oldfile newfile patchfile\n", argv[0]); goto cleanup; } old = readfile(argv[1], &oldsize); if (old == NULL) { warn("old file error: %s", argv[1]); goto cleanup; } if (((I = malloc(((size_t)oldsize + 1) * sizeof(off_t))) == NULL) || ((V = malloc(((size_t)oldsize + 1) * sizeof(off_t))) == NULL)) { warn("Failed to allocate memory for I or V"); goto cleanup; } /* Do a suffix sort on the old file. */ I[0] = oldsize; sais(old, I+1, (int)oldsize); free(V); V = NULL; new = readfile(argv[2], &newsize); if (new == NULL) { warn("new file error: %s", argv[2]); goto cleanup; } if (((db = malloc((size_t)newsize + 1)) == NULL) || ((eb = malloc((size_t)newsize + 1)) == NULL)) { warn("Failed to allocate memory for db or eb"); goto cleanup; } dblen = 0; eblen = 0; /* Create the patch file */ if ((pf = fopen(argv[3], "w")) == NULL) { warn("%s", argv[3]); goto cleanup; } /* Header is 0 8 "BSDIFN40" 8 8 length of ctrl block 16 8 length of diff block 24 8 length of new file */ /* File is 0 32 Header 32 ?? ctrl block ?? ?? diff block ?? ?? extra block */ memcpy(header, "BSDIFN40", 8); offtout(0, header + 8); offtout(0, header + 16); offtout(newsize, header + 24); if (fwrite(header, 32, 1, pf) != 1) { warn("fwrite(%s)", argv[3]); goto cleanup; } /* Compute the differences, writing ctrl as we go */ scan = 0; len = 0; lastscan = 0; lastpos = 0; lastoffset = 0; while (scan < newsize) { oldscore = 0; for (scsc = scan += len; scan < newsize; scan++) { /* 'oldscore' is the number of characters that match between the * substrings 'old[lastoffset + scan:lastoffset + scsc]' and * 'new[scan:scsc]'. */ len = search(I, old, oldsize, new + scan, newsize - scan, 0, oldsize, &pos); /* If this match extends further than the last one, add any new * matching characters to 'oldscore'. */ for (; scsc < scan + len; scsc++) { if ((scsc + lastoffset < oldsize) && (old[scsc + lastoffset] == new[scsc])) oldscore++; } /* Choose this as our match if it contains more than eight * characters that would be wrong if matched with a forward * extension of the previous match instead. */ if (((len == oldscore) && (len != 0)) || (len > oldscore + 8)) break; /* Since we're advancing 'scan' by 1, remove the character under it * from 'oldscore' if it matches. */ if ((scan + lastoffset < oldsize) && (old[scan + lastoffset] == new[scan])) oldscore--; } /* Skip this section if we found an exact match that would be * better serviced by a forward extension of the previous match. */ if ((len != oldscore) || (scan == newsize)) { /* Figure out how far forward the previous match should be * extended... */ s = 0; Sf = 0; lenf = 0; for (i = 0; (lastscan + i < scan) && (lastpos + i < oldsize);) { if (old[lastpos + i] == new[lastscan + i]) s++; i++; if (s * 2 - i > Sf * 2 - lenf) { Sf = s; lenf = i; } } /* ... and how far backwards the next match should be extended. */ lenb = 0; if (scan < newsize) { s = 0; Sb = 0; for (i = 1; (scan >= lastscan + i) && (pos >= i); i++) { if (old[pos - i] == new[scan - i]) s++; if (s * 2 - i > Sb * 2 - lenb) { Sb = s; lenb = i; } } } /* If there is an overlap between the extensions, find the best * dividing point in the middle and reset 'lenf' and 'lenb' * accordingly. */ if (lastscan + lenf > scan - lenb) { overlap = (lastscan + lenf) - (scan - lenb); s = 0; Ss = 0; lens = 0; for (i = 0; i < overlap; i++) { if (new[lastscan + lenf - overlap + i] == old[lastpos + lenf - overlap + i]) s++; if (new[scan - lenb + i] == old[pos - lenb + i]) s--; if (s > Ss) { Ss = s; lens = i + 1; } } lenf += lens - overlap; lenb -= lens; } /* Write the diff data for the last match to the diff section... */ for (i = 0; i < lenf; i++) db[dblen + i] = new[lastscan + i] - old[lastpos + i]; /* ... and, if there's a gap between the extensions just * calculated, write the data in that gap to the extra section. */ for (i = 0; i< (scan - lenb) - (lastscan + lenf); i++) eb[eblen + i] = new[lastscan + lenf + i]; /* Update the diff and extra section lengths accordingly. */ dblen += lenf; eblen += (scan - lenb) - (lastscan + lenf); /* Write the following triple of integers to the control section: * - length of the diff * - length of the extra section * - offset between the end of the diff and the start of the next * diff, in the old file */ offtout(lenf, buf); if (fwrite(buf, 8, 1, pf) != 1) { warnx("fwrite"); goto cleanup; } offtout((scan - lenb) - (lastscan + lenf), buf); if (fwrite(buf, 8, 1, pf) != 1) { warn("fwrite"); goto cleanup; } offtout((pos - lenb) - (lastpos + lenf), buf); if (fwrite(buf, 8, 1, pf) != 1) { warn("fwrite"); goto cleanup; } /* Update the variables describing the last match. Note that * 'lastscan' is set to the start of the current match _after_ the * backwards extension; the data in that extension will be written * in the next pass. */ lastscan = scan - lenb; lastpos = pos - lenb; lastoffset = pos - scan; } } /* Compute size of compressed ctrl data */ if ((len = ftello(pf)) == -1) { warn("ftello"); goto cleanup; } offtout(len - 32, header + 8); /* Write diff data */ if (dblen && fwrite(db, (size_t)dblen, 1, pf) != 1) { warn("fwrite"); goto cleanup; } /* Compute size of compressed diff data */ if ((newsize = ftello(pf)) == -1) { warn("ftello"); goto cleanup; } offtout(newsize - len, header + 16); /* Write extra data */ if (eblen && fwrite(eb, (size_t)eblen, 1, pf) != 1) { warn("fwrite"); goto cleanup; } /* Seek to the beginning, write the header, and close the file */ if (fseeko(pf, 0, SEEK_SET)) { warn("fseeko"); goto cleanup; } if (fwrite(header, 32, 1, pf) != 1) { warn("fwrite(%s)", argv[3]); goto cleanup; } if (fclose(pf)) { warn("fclose"); pf = NULL; goto cleanup; } pf = NULL; exitstatus = 0; cleanup: if (pf != NULL) { fclose(pf); } /* Free the memory we used */ free(db); free(eb); free(I); free(V); free(old); free(new); return exitstatus; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/bspatch.c ================================================ /*- * Copyright 2003-2005 Colin Percival * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if 0 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $"); #endif #include "bspatch.h" #include #include #include #include #include #include #include #include "bscommon.h" /* Compatibility layer for reading either the old BSDIFF40 or the new BSDIFN40 patch formats: */ typedef void* stream_t; typedef struct { stream_t (*open)(FILE*); void (*close)(stream_t); off_t (*read)(stream_t, void*, off_t); } io_funcs_t; static stream_t BSDIFF40_open(FILE *f) { int bzerr = 0; BZFILE *s = NULL; if ((s = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) { warnx("BZ2_bzReadOpen, bz2err = %d", bzerr); } return s; } static void BSDIFF40_close(stream_t s) { int bzerr; BZ2_bzReadClose(&bzerr, (BZFILE*)s); } static off_t BSDIFF40_read(stream_t s, void *buf, off_t len) { int bzerr = 0, lenread = 0; lenread = BZ2_bzRead(&bzerr, (BZFILE*)s, buf, (int)len); if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) { warnx("Corrupt patch\n"); lenread = -1; } return lenread; } static io_funcs_t BSDIFF40_funcs = { BSDIFF40_open, BSDIFF40_close, BSDIFF40_read }; static stream_t BSDIFN40_open(FILE *f) { return f; } static void BSDIFN40_close(stream_t __unused s) { } static off_t BSDIFN40_read(stream_t s, void *buf, off_t len) { return (off_t)fread(buf, 1, (size_t)len, (FILE*)s); } static io_funcs_t BSDIFN40_funcs = { BSDIFN40_open, BSDIFN40_close, BSDIFN40_read }; #ifndef u_char typedef unsigned char u_char; #endif static off_t offtin(u_char *buf) { off_t y; y=buf[7]&0x7F; y=y*256;y+=buf[6]; y=y*256;y+=buf[5]; y=y*256;y+=buf[4]; y=y*256;y+=buf[3]; y=y*256;y+=buf[2]; y=y*256;y+=buf[1]; y=y*256;y+=buf[0]; if(buf[7]&0x80) y=-y; return y; } int bspatch(int argc,const char * const argv[]) { FILE * f = NULL, * cpf = NULL, * dpf = NULL, * epf = NULL; stream_t cstream = NULL, dstream = NULL, estream = NULL; ssize_t oldsize = 0,newsize = 0; ssize_t bzctrllen = 0,bzdatalen = 0; u_char header[32] = {0},buf[8] = {0}; u_char *old = NULL, *new = NULL; off_t oldpos = 0,newpos = 0; off_t ctrl[3] = {0}; off_t lenread = 0; off_t i = 0; io_funcs_t * io = NULL; int exitstatus = -1; if(argc!=4) { warnx("usage: %s oldfile newfile patchfile\n",argv[0]); goto cleanup; } /* Open patch file */ if ((f = fopen(argv[3], "r")) == NULL) { warn("fopen(%s)", argv[3]); goto cleanup; } /* File format: 0 8 "BSDIFF40" (bzip2) or "BSDIFN40" (raw) 8 8 X 16 8 Y 24 8 sizeof(newfile) 32 X bzip2(control block) 32+X Y bzip2(diff block) 32+X+Y ??? bzip2(extra block) with control block a set of triples (x,y,z) meaning "add x bytes from oldfile to x bytes from the diff block; copy y bytes from the extra block; seek forwards in oldfile by z bytes". */ /* Read header */ if (fread(header, 1, 32, f) < 32) { if (feof(f)) { warnx("Corrupt patch\n"); } else { warn("fread(%s)", argv[3]); } goto cleanup; } /* Check for appropriate magic */ if (memcmp(header, "BSDIFF40", 8) == 0) io = &BSDIFF40_funcs; else if (memcmp(header, "BSDIFN40", 8) == 0) io = &BSDIFN40_funcs; else { warnx("Corrupt patch\n"); goto cleanup; } /* Read lengths from header */ bzctrllen=offtin(header+8); bzdatalen=offtin(header+16); newsize=offtin(header+24); if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) { warnx("Corrupt patch\n"); goto cleanup; } /* Close patch file and re-open it via libbzip2 at the right places */ if (fclose(f)) { warn("fclose(%s)", argv[3]); f = NULL; goto cleanup; } f = NULL; if ((cpf = fopen(argv[3], "r")) == NULL) { warn("fopen(%s)", argv[3]); goto cleanup; } if (fseeko(cpf, 32, SEEK_SET)) { warn("fseeko(%s, %lld)", argv[3], (long long)32); goto cleanup; } cstream = io->open(cpf); if (cstream == NULL) { warn("cstream open"); goto cleanup; } if ((dpf = fopen(argv[3], "r")) == NULL) { warn("fopen(%s)", argv[3]); goto cleanup; } if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) { warn("fseeko(%s, %lld)", argv[3], (long long)(32 + bzctrllen)); goto cleanup; } dstream = io->open(dpf); if (dstream == NULL) { warn("dstream open"); goto cleanup; } if ((epf = fopen(argv[3], "r")) == NULL) { warn("fopen(%s)", argv[3]); goto cleanup; } if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) { warn("fseeko(%s, %lld)", argv[3], (long long)(32 + bzctrllen + bzdatalen)); goto cleanup; } estream = io->open(epf); if (estream == NULL) { warn("estream open"); goto cleanup; } off_t size = 0; old = readfile(argv[1], &size); if (old == NULL) { warn("old file: %s", argv[1]); goto cleanup; } oldsize = size; if((new=malloc((size_t)newsize+1))==NULL) { warn("Failed to allocate memory for new"); goto cleanup; } oldpos=0;newpos=0; while(newposread(cstream, buf, 8); if (lenread < 8) { warnx("Corrupt patch\n"); goto cleanup; } ctrl[i]=offtin(buf); }; /* Sanity-check */ if(newpos+ctrl[0]>newsize) { warnx("Corrupt patch\n"); goto cleanup; } /* Read diff string */ lenread = io->read(dstream, new + newpos, ctrl[0]); if (lenread < 0 || lenread < ctrl[0]) { warnx("Corrupt patch\n"); goto cleanup; } /* Add old data to diff string */ for(i=0;i=0) && (oldpos+inewsize) { warnx("Corrupt patch\n"); goto cleanup; } /* Read extra string */ lenread = io->read(estream, new + newpos, ctrl[1]); if (lenread < 0 || lenread < ctrl[1]) { warnx("Corrupt patch\n"); goto cleanup; } /* Adjust pointers */ newpos+=ctrl[1]; oldpos+=ctrl[2]; }; /* Clean up the bzip2 reads */ io->close(cstream); cstream = NULL; io->close(dstream); dstream = NULL; io->close(estream); estream = NULL; if (fclose(cpf) != 0) { warn("fclose cpf(%s)", argv[3]); cpf = NULL; goto cleanup; } cpf = NULL; if (fclose(dpf) != 0) { warn("fclose dpf(%s)", argv[3]); dpf = NULL; goto cleanup; } dpf = NULL; if (fclose(epf) != 0) { warn("fclose epf(%s)", argv[3]); epf = NULL; goto cleanup; } epf = NULL; /* Write the new file */ f = fopen(argv[2], "w"); if (f == NULL) { warn("failed to write new file: %s", argv[2]); goto cleanup; } if (fwrite(new, 1, (size_t)newsize, f) < (size_t)newsize) { warn("failed to write to new file: %s", argv[2]); goto cleanup; } if (fclose(f) != 0) { warn("failed to close new file: %s", argv[2]); f = NULL; goto cleanup; } f = NULL; exitstatus = 0; cleanup: free(new); free(old); if (f != NULL) { fclose(f); } if (estream != NULL) { io->close(estream); } if (epf != NULL) { fclose(epf); } if (dstream != NULL) { io->close(dstream); } if (dpf != NULL) { fclose(dpf); } if (cstream != NULL) { io->close(cstream); } if (cpf != NULL) { fclose(cpf); } return exitstatus; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/bspatch.h ================================================ /* * bspatch.h * Sparkle * * Created by Andy Matuschak on 1/11/10. * Copyright 2010 Andy Matuschak. All rights reserved. * */ // So that we can use this method in SUBinaryDeltaApply.m. // Silences the GCC warning that the prototype doesn't exist. int bspatch(int argc, const char * const argv[]); ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/sais.c ================================================ /* * sais.c for sais-lite * Copyright (c) 2008-2010 Yuta Mori All Rights Reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include "sais.h" #ifndef UCHAR_SIZE # define UCHAR_SIZE 256 #endif #ifndef MINBUCKETSIZE # define MINBUCKETSIZE 256 #endif #define sais_bool_type int #define SAIS_LMSSORT2_LIMIT 0x3fffffff #define SAIS_MYMALLOC(_num, _type) ((_type *)malloc((_num) * sizeof(_type))) #define SAIS_MYFREE(_ptr, _num, _type) free((_ptr)) #define chr(_a) (cs == sizeof(sais_index_type) ? ((const sais_index_type *)T)[(_a)] : ((const unsigned char *)T)[(_a)]) /* find the start or end of each bucket */ static void getCounts(const void *T, sais_index_type *C, sais_index_type n, sais_index_type k, int cs) { sais_index_type i; for(i = 0; i < k; ++i) { C[i] = 0; } for(i = 0; i < n; ++i) { ++C[chr(i)]; } } static void getBuckets(const sais_index_type *C, sais_index_type *B, sais_index_type k, sais_bool_type end) { sais_index_type i, sum = 0; if(end) { for(i = 0; i < k; ++i) { sum += C[i]; B[i] = sum; } } else { for(i = 0; i < k; ++i) { sum += C[i]; B[i] = sum - C[i]; } } } /* sort all type LMS suffixes */ static void LMSsort1(const void *T, sais_index_type *SA, sais_index_type *C, sais_index_type *B, sais_index_type n, sais_index_type k, int cs) { sais_index_type *b, i, j; sais_index_type c0, c1; /* compute SAl */ if(C == B) { getCounts(T, C, n, k, cs); } getBuckets(C, B, k, 0); /* find starts of buckets */ j = n - 1; b = SA + B[c1 = chr(j)]; --j; *b++ = (chr(j) < c1) ? ~j : j; for(i = 0; i < n; ++i) { if(0 < (j = SA[i])) { assert(chr(j) >= chr(j + 1)); if((c0 = chr(j)) != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert(i < (b - SA)); --j; *b++ = (chr(j) < c1) ? ~j : j; SA[i] = 0; } else if(j < 0) { SA[i] = ~j; } } /* compute SAs */ if(C == B) { getCounts(T, C, n, k, cs); } getBuckets(C, B, k, 1); /* find ends of buckets */ for(i = n - 1, b = SA + B[c1 = 0]; 0 <= i; --i) { if(0 < (j = SA[i])) { assert(chr(j) <= chr(j + 1)); if((c0 = chr(j)) != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert((b - SA) <= i); --j; *--b = (chr(j) > c1) ? ~(j + 1) : j; SA[i] = 0; } } } static sais_index_type LMSpostproc1(const void *T, sais_index_type *SA, sais_index_type n, sais_index_type m, int cs) { sais_index_type i, j, p, q, plen, qlen, name; sais_index_type c0, c1; sais_bool_type diff; /* compact all the sorted substrings into the first m items of SA 2*m must be not larger than n (proveable) */ assert(0 < n); for(i = 0; (p = SA[i]) < 0; ++i) { SA[i] = ~p; assert((i + 1) < n); } if(i < m) { for(j = i, ++i;; ++i) { assert(i < n); if((p = SA[i]) < 0) { SA[j++] = ~p; SA[i] = 0; if(j == m) { break; } } } } /* store the length of all substrings */ i = n - 1; j = n - 1; c0 = chr(n - 1); do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) >= c1)); for(; 0 <= i;) { do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) <= c1)); if(0 <= i) { SA[m + ((i + 1) >> 1)] = j - i; j = i + 1; do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) >= c1)); } } /* find the lexicographic names of all substrings */ for(i = 0, name = 0, q = n, qlen = 0; i < m; ++i) { p = SA[i], plen = SA[m + (p >> 1)], diff = 1; if((plen == qlen) && ((q + plen) < n)) { for(j = 0; (j < plen) && (chr(p + j) == chr(q + j)); ++j) { } if(j == plen) { diff = 0; } } if(diff != 0) { ++name, q = p, qlen = plen; } SA[m + (p >> 1)] = name; } return name; } static void LMSsort2(const void *T, sais_index_type *SA, sais_index_type *C, sais_index_type *B, sais_index_type *D, sais_index_type n, sais_index_type k, int cs) { sais_index_type *b, i, j, t, d; sais_index_type c0, c1; assert(C != B); /* compute SAl */ getBuckets(C, B, k, 0); /* find starts of buckets */ j = n - 1; b = SA + B[c1 = chr(j)]; --j; t = (chr(j) < c1); j += n; *b++ = (t & 1) ? ~j : j; for(i = 0, d = 0; i < n; ++i) { if(0 < (j = SA[i])) { if(n <= j) { d += 1; j -= n; } assert(chr(j) >= chr(j + 1)); if((c0 = chr(j)) != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert(i < (b - SA)); --j; t = c0; t = (t << 1) | (chr(j) < c1); if(D[t] != d) { j += n; D[t] = d; } *b++ = (t & 1) ? ~j : j; SA[i] = 0; } else if(j < 0) { SA[i] = ~j; } } for(i = n - 1; 0 <= i; --i) { if(0 < SA[i]) { if(SA[i] < n) { SA[i] += n; for(j = i - 1; SA[j] < n; --j) { } SA[j] -= n; i = j; } } } /* compute SAs */ getBuckets(C, B, k, 1); /* find ends of buckets */ for(i = n - 1, d += 1, b = SA + B[c1 = 0]; 0 <= i; --i) { if(0 < (j = SA[i])) { if(n <= j) { d += 1; j -= n; } assert(chr(j) <= chr(j + 1)); if((c0 = chr(j)) != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert((b - SA) <= i); --j; t = c0; t = (t << 1) | (chr(j) > c1); if(D[t] != d) { j += n; D[t] = d; } *--b = (t & 1) ? ~(j + 1) : j; SA[i] = 0; } } } static sais_index_type LMSpostproc2(sais_index_type *SA, sais_index_type n, sais_index_type m) { sais_index_type i, j, d, name; /* compact all the sorted LMS substrings into the first m items of SA */ assert(0 < n); for(i = 0, name = 0; (j = SA[i]) < 0; ++i) { j = ~j; if(n <= j) { name += 1; } SA[i] = j; assert((i + 1) < n); } if(i < m) { for(d = i, ++i;; ++i) { assert(i < n); if((j = SA[i]) < 0) { j = ~j; if(n <= j) { name += 1; } SA[d++] = j; SA[i] = 0; if(d == m) { break; } } } } if(name < m) { /* store the lexicographic names */ for(i = m - 1, d = name + 1; 0 <= i; --i) { if(n <= (j = SA[i])) { j -= n; --d; } SA[m + (j >> 1)] = d; } } else { /* unset flags */ for(i = 0; i < m; ++i) { if(n <= (j = SA[i])) { j -= n; SA[i] = j; } } } return name; } /* compute SA and BWT */ static void induceSA(const void *T, sais_index_type *SA, sais_index_type *C, sais_index_type *B, sais_index_type n, sais_index_type k, int cs) { sais_index_type *b, i, j; sais_index_type c0, c1; /* compute SAl */ if(C == B) { getCounts(T, C, n, k, cs); } getBuckets(C, B, k, 0); /* find starts of buckets */ j = n - 1; b = SA + B[c1 = chr(j)]; *b++ = ((0 < j) && (chr(j - 1) < c1)) ? ~j : j; for(i = 0; i < n; ++i) { j = SA[i], SA[i] = ~j; if(0 < j) { --j; assert(chr(j) >= chr(j + 1)); if((c0 = chr(j)) != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert(i < (b - SA)); *b++ = ((0 < j) && (chr(j - 1) < c1)) ? ~j : j; } } /* compute SAs */ if(C == B) { getCounts(T, C, n, k, cs); } getBuckets(C, B, k, 1); /* find ends of buckets */ for(i = n - 1, b = SA + B[c1 = 0]; 0 <= i; --i) { if(0 < (j = SA[i])) { --j; assert(chr(j) <= chr(j + 1)); if((c0 = chr(j)) != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert((b - SA) <= i); *--b = ((j == 0) || (chr(j - 1) > c1)) ? ~j : j; } else { SA[i] = ~j; } } } static sais_index_type computeBWT(const void *T, sais_index_type *SA, sais_index_type *C, sais_index_type *B, sais_index_type n, sais_index_type k, int cs) { sais_index_type *b, i, j, pidx = -1; sais_index_type c0, c1; /* compute SAl */ if(C == B) { getCounts(T, C, n, k, cs); } getBuckets(C, B, k, 0); /* find starts of buckets */ j = n - 1; b = SA + B[c1 = chr(j)]; *b++ = ((0 < j) && (chr(j - 1) < c1)) ? ~j : j; for(i = 0; i < n; ++i) { if(0 < (j = SA[i])) { --j; assert(chr(j) >= chr(j + 1)); SA[i] = ~((sais_index_type)(c0 = chr(j))); if(c0 != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert(i < (b - SA)); *b++ = ((0 < j) && (chr(j - 1) < c1)) ? ~j : j; } else if(j != 0) { SA[i] = ~j; } } /* compute SAs */ if(C == B) { getCounts(T, C, n, k, cs); } getBuckets(C, B, k, 1); /* find ends of buckets */ for(i = n - 1, b = SA + B[c1 = 0]; 0 <= i; --i) { if(0 < (j = SA[i])) { --j; assert(chr(j) <= chr(j + 1)); SA[i] = (c0 = chr(j)); if(c0 != c1) { B[c1] = b - SA; b = SA + B[c1 = c0]; } assert((b - SA) <= i); *--b = ((0 < j) && (chr(j - 1) > c1)) ? ~((sais_index_type)chr(j - 1)) : j; } else if(j != 0) { SA[i] = ~j; } else { pidx = i; } } return pidx; } /* find the suffix array SA of T[0..n-1] in {0..255}^n */ static sais_index_type sais_main(const void *T, sais_index_type *SA, sais_index_type fs, sais_index_type n, sais_index_type k, int cs, sais_bool_type isbwt) { sais_index_type *C, *B, *D, *RA, *b; sais_index_type i, j, m, p, q, t, name, pidx = 0, newfs; sais_index_type c0, c1; unsigned int flags; assert((T != NULL) && (SA != NULL)); assert((0 <= fs) && (0 < n) && (1 <= k)); if(k <= MINBUCKETSIZE) { if((C = SAIS_MYMALLOC((size_t)k, sais_index_type)) == NULL) { return -2; } if(k <= fs) { B = SA + (n + fs - k); flags = 1; } else { if((B = SAIS_MYMALLOC((size_t)k, sais_index_type)) == NULL) { SAIS_MYFREE(C, k, sais_index_type); return -2; } flags = 3; } } else if(k <= fs) { C = SA + (n + fs - k); if(k <= (fs - k)) { B = C - k; flags = 0; } else if(k <= (MINBUCKETSIZE * 4)) { if((B = SAIS_MYMALLOC((size_t)k, sais_index_type)) == NULL) { return -2; } flags = 2; } else { B = C; flags = 8; } } else { if((C = B = SAIS_MYMALLOC((size_t)k, sais_index_type)) == NULL) { return -2; } flags = 4 | 8; } if((n <= SAIS_LMSSORT2_LIMIT) && (2 <= (n / k))) { if(flags & 1) { flags |= ((k * 2) <= (fs - k)) ? 32 : 16; } else if((flags == 0) && ((k * 2) <= (fs - k * 2))) { flags |= 32; } } /* stage 1: reduce the problem by at least 1/2 sort all the LMS-substrings */ getCounts(T, C, n, k, cs); getBuckets(C, B, k, 1); /* find ends of buckets */ for(i = 0; i < n; ++i) { SA[i] = 0; } b = &t; i = n - 1; j = n; m = 0; c0 = chr(n - 1); do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) >= c1)); for(; 0 <= i;) { do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) <= c1)); if(0 <= i) { *b = j; b = SA + --B[c1]; j = i; ++m; do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) >= c1)); } } if(1 < m) { if(flags & (16 | 32)) { if(flags & 16) { if((D = SAIS_MYMALLOC((size_t)k * 2, sais_index_type)) == NULL) { if(flags & (1 | 4)) { SAIS_MYFREE(C, k, sais_index_type); } if(flags & 2) { SAIS_MYFREE(B, k, sais_index_type); } return -2; } } else { D = B - k * 2; } assert((j + 1) < n); ++B[chr(j + 1)]; for(i = 0, j = 0; i < k; ++i) { j += C[i]; if(B[i] != j) { assert(SA[B[i]] != 0); SA[B[i]] += n; } D[i] = D[i + k] = 0; } LMSsort2(T, SA, C, B, D, n, k, cs); name = LMSpostproc2(SA, n, m); if(flags & 16) { SAIS_MYFREE(D, k * 2, sais_index_type); } } else { LMSsort1(T, SA, C, B, n, k, cs); name = LMSpostproc1(T, SA, n, m, cs); } } else if(m == 1) { *b = j + 1; name = 1; } else { name = 0; } /* stage 2: solve the reduced problem recurse if names are not yet unique */ if(name < m) { if(flags & 4) { SAIS_MYFREE(C, k, sais_index_type); } if(flags & 2) { SAIS_MYFREE(B, k, sais_index_type); } newfs = (n + fs) - (m * 2); if((flags & (1 | 4 | 8)) == 0) { if((k + name) <= newfs) { newfs -= k; } else { flags |= 8; } } assert((n >> 1) <= (newfs + m)); RA = SA + m + newfs; for(i = m + (n >> 1) - 1, j = m - 1; m <= i; --i) { if(SA[i] != 0) { RA[j--] = SA[i] - 1; } } if(sais_main(RA, SA, newfs, m, name, sizeof(sais_index_type), 0) != 0) { if(flags & 1) { SAIS_MYFREE(C, k, sais_index_type); } return -2; } i = n - 1; j = m - 1; c0 = chr(n - 1); do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) >= c1)); for(; 0 <= i;) { do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) <= c1)); if(0 <= i) { RA[j--] = i + 1; do { c1 = c0; } while((0 <= --i) && ((c0 = chr(i)) >= c1)); } } for(i = 0; i < m; ++i) { SA[i] = RA[SA[i]]; } if(flags & 4) { if((C = B = SAIS_MYMALLOC((size_t)k, sais_index_type)) == NULL) { return -2; } } if(flags & 2) { if((B = SAIS_MYMALLOC((size_t)k, sais_index_type)) == NULL) { if(flags & 1) { SAIS_MYFREE(C, k, sais_index_type); } return -2; } } } /* stage 3: induce the result for the original problem */ if(flags & 8) { getCounts(T, C, n, k, cs); } /* put all left-most S characters into their buckets */ if(1 < m) { getBuckets(C, B, k, 1); /* find ends of buckets */ i = m - 1, j = n, p = SA[m - 1], c1 = chr(p); do { q = B[c0 = c1]; while(q < j) { SA[--j] = 0; } do { SA[--j] = p; if(--i < 0) { break; } p = SA[i]; } while((c1 = chr(p)) == c0); } while(0 <= i); while(0 < j) { SA[--j] = 0; } } if(isbwt == 0) { induceSA(T, SA, C, B, n, k, cs); } else { pidx = computeBWT(T, SA, C, B, n, k, cs); } if(flags & (1 | 4)) { SAIS_MYFREE(C, k, sais_index_type); } if(flags & 2) { SAIS_MYFREE(B, k, sais_index_type); } return pidx; } /*---------------------------------------------------------------------------*/ sais_index_type sais(const unsigned char *T, sais_index_type *SA, int n) { if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } if(n <= 1) { if(n == 1) { SA[0] = 0; } return 0; } return sais_main(T, SA, 0, n, UCHAR_SIZE, sizeof(unsigned char), 0); } sais_index_type sais_int(const int *T, sais_index_type *SA, int n, int k) { if((T == NULL) || (SA == NULL) || (n < 0) || (k <= 0)) { return -1; } if(n <= 1) { if(n == 1) { SA[0] = 0; } return 0; } return sais_main(T, SA, 0, n, k, sizeof(int), 0); } sais_index_type sais_bwt(const unsigned char *T, unsigned char *U, sais_index_type *A, int n) { int i; sais_index_type pidx; if((T == NULL) || (U == NULL) || (A == NULL) || (n < 0)) { return -1; } if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } pidx = sais_main(T, A, 0, n, UCHAR_SIZE, sizeof(unsigned char), 1); if(pidx < 0) { return pidx; } U[0] = T[n - 1]; for(i = 0; i < pidx; ++i) { U[i + 1] = (unsigned char)A[i]; } for(i += 1; i < n; ++i) { U[i] = (unsigned char)A[i]; } pidx += 1; return pidx; } sais_index_type sais_int_bwt(const sais_index_type *T, sais_index_type *U, sais_index_type *A, int n, int k) { int i; sais_index_type pidx; if((T == NULL) || (U == NULL) || (A == NULL) || (n < 0) || (k <= 0)) { return -1; } if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } pidx = sais_main(T, A, 0, n, k, sizeof(int), 1); if(pidx < 0) { return pidx; } U[0] = T[n - 1]; for(i = 0; i < pidx; ++i) { U[i + 1] = A[i]; } for(i += 1; i < n; ++i) { U[i] = A[i]; } pidx += 1; return pidx; } ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/Vendor/bsdiff/sais.h ================================================ /* * sais.h for sais-lite * Copyright (c) 2008-2010 Yuta Mori All Rights Reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SAIS_H #define SAIS_H 1 #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include #define sais_index_type off_t /* find the suffix array SA of T[0..n-1] use a working space (excluding T and SA) of at most 2n+O(lg n) */ sais_index_type sais(const unsigned char *T, sais_index_type *SA, int n); /* find the suffix array SA of T[0..n-1] in {0..k-1}^n use a working space (excluding T and SA) of at most MAX(4k,2n) */ sais_index_type sais_int(const int *T, sais_index_type *SA, int n, int k); /* burrows-wheeler transform */ sais_index_type sais_bwt(const unsigned char *T, unsigned char *U, sais_index_type *A, int n); sais_index_type sais_int_bwt(const sais_index_type *T, sais_index_type *U, sais_index_type *A, int n, int k); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* SAIS_H */ ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/bin/generate_keys ================================================ #!/bin/bash set -e for file in "dsaparam.pem" "dsa_priv.pem" "dsa_pub.pem"; do if [ -e "$file" ]; then echo "There's already a $file here! Move it aside or be more careful!" exit 1 fi done openssl="/usr/bin/openssl" $openssl gendsa <($openssl dsaparam 4096) -out dsa_priv.pem chmod 0400 dsa_priv.pem $openssl dsa -in dsa_priv.pem -pubout -out dsa_pub.pem echo " Generated two files: dsa_priv.pem: your private key. Keep it secret and don't share it! dsa_pub.pem: public counterpart to include in the app bundle. BACK UP YOUR PRIVATE KEY AND KEEP IT SAFE! If you lose it, your users will be unable to upgrade! " open -R dsa_priv.pem ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/bin/sign_update ================================================ #!/bin/sh set -e set -o pipefail if [ "$#" -ne 2 ]; then echo "Usage: $0 update_archive private_key" exit 1 fi openssl=/usr/bin/openssl $openssl dgst -sha1 -binary < "$1" | $openssl dgst -dss1 -sign "$2" | $openssl enc -base64 ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/fileop/SUFileOperationConstants.h ================================================ // // SUFileOperationConstants.h // Sparkle // // Created by Mayur Pawashe on 6/5/16. // Copyright © 2016 Sparkle Project. All rights reserved. // extern char * const SUFileOpRemoveQuarantineCommand; extern char * const SUFileOpCopyCommand; extern char * const SUFileOpMoveCommand; extern char * const SUFileOpChangeOwnerAndGroupCommand; extern char * const SUFileOpUpdateModificationAndAccessTimeCommand; extern char * const SUFileOpMakeDirectoryCommand; extern char * const SUFileOpRemoveCommand; extern char * const SUFileOpInstallCommand; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/fileop/SUFileOperationConstants.m ================================================ // // SUFileOperationConstants.m // Sparkle // // Created by Mayur Pawashe on 6/5/16. // Copyright © 2016 Sparkle Project. All rights reserved. // #import "SUFileOperationConstants.h" char * const SUFileOpRemoveQuarantineCommand = "xattr-d-apple-quarantine"; char * const SUFileOpCopyCommand = "cp"; char * const SUFileOpMoveCommand = "mv"; char * const SUFileOpChangeOwnerAndGroupCommand = "chown"; char * const SUFileOpUpdateModificationAndAccessTimeCommand = "touch"; char * const SUFileOpMakeDirectoryCommand = "mkdir"; char * const SUFileOpRemoveCommand = "rm"; char * const SUFileOpInstallCommand = "installer"; ================================================ FILE: archive/bitbar/App/Vendor/Sparkle/fileop/fileop.m ================================================ // // fileop.m // fileop // // Created by Mayur Pawashe on 6/5/16. // Copyright © 2016 Sparkle Project. All rights reserved. // #import #import "SUFileManager.h" #import "SUFileOperationConstants.h" // If we fail, we exit with a unique status code // We don't try to NSLog because the logs can't be seen anywhere, // and we don't want to log to a file irresponsibly (especially as root), // nor want to communicate to the parent forcing the parent to read from the stdout pipe // (allowing us to avoid any sort of potential blocking issues?) typedef NS_ENUM(uint8_t, SUFileOpError) { SUWritePIDFailure = 0x02, SUFlushFailure = 0x03, SUInsufficientNumberOfArguments = 0x04, SUCommandNameUTF8ParseFailure = 0x05, SUPathUTF8ParseFailure = 0x06, SUQuarantineRemovalFailure = 0x07, SUCopyFailure = 0x08, SUMoveFailure = 0x09, SUChangeOwnerAndGroupFailure = 0x0A, SUTouchFailure = 0x0B, SUMakeDirectoryFailure = 0x0C, SURemoveFailure = 0x0D, SUPackageFailureStatusCode = 0x0E, SUPackageRaisedExceptionFailure = 0x0F, SUInvalidCommandFailure = 0x10, // This code can be OR'd with another one to produce a unique combination SUInvalidOrNoDestination = 0x80 }; int main(int argc, const char *argv[]) { @autoreleasepool { // Before we do anything, we should let the parent know our pid so they can wait() on us // We do this because AuthorizationExecuteWithPrivileges() has no way of reporting back the child pid, // but it does give us a communication pipe uint32_t pid = CFSwapInt32HostToLittle((uint32_t)getpid()); if (fwrite(&pid, sizeof(pid), 1, stdout) < 1) { exit(SUWritePIDFailure); } if (fflush(stdout) != 0) { exit(SUFlushFailure); } // At least we need the command name and file path arguments if (argc < 3) { exit(SUInsufficientNumberOfArguments); } NSString *command = [[NSString alloc] initWithUTF8String:argv[1]]; if (command == nil) { exit(SUCommandNameUTF8ParseFailure); } NSString *filepath = [[NSString alloc] initWithUTF8String:argv[2]]; if (filepath == nil) { exit(SUPathUTF8ParseFailure); } NSURL *fileURL = [NSURL fileURLWithPath:filepath]; if (fileURL == nil) { exit(SUPathUTF8ParseFailure); } NSURL *destinationURL = nil; if (argc >= 4) { NSString *destinationPath = [[NSString alloc] initWithUTF8String:argv[3]]; if (destinationPath != nil) { destinationURL = [NSURL fileURLWithPath:destinationPath]; } } // This tool should be executed as root, so we should not try to authorize SUFileManager *fileManager = [SUFileManager defaultManager]; if ([command isEqualToString:@(SUFileOpRemoveQuarantineCommand)]) { if (![fileManager releaseItemFromQuarantineAtRootURL:fileURL error:NULL]) { exit(SUQuarantineRemovalFailure); } } else if ([command isEqualToString:@(SUFileOpCopyCommand)]) { if (destinationURL != nil) { if (![fileManager copyItemAtURL:fileURL toURL:destinationURL error:NULL]) { exit(SUCopyFailure); } } else { exit(SUInvalidOrNoDestination | SUCopyFailure); } } else if ([command isEqualToString:@(SUFileOpMoveCommand)]) { if (destinationURL != nil) { if (![fileManager moveItemAtURL:fileURL toURL:destinationURL error:NULL]) { exit(SUMoveFailure); } } else { exit(SUInvalidOrNoDestination | SUMoveFailure); } } else if ([command isEqualToString:@(SUFileOpChangeOwnerAndGroupCommand)]) { if (destinationURL != nil) { if (![fileManager changeOwnerAndGroupOfItemAtRootURL:fileURL toMatchURL:destinationURL error:NULL]) { exit(SUChangeOwnerAndGroupFailure); } } else { exit(SUInvalidOrNoDestination | SUChangeOwnerAndGroupFailure); } } else if ([command isEqualToString:@(SUFileOpUpdateModificationAndAccessTimeCommand)]) { if (![fileManager updateModificationAndAccessTimeOfItemAtURL:fileURL error:NULL]) { exit(SUTouchFailure); } } else if ([command isEqualToString:@(SUFileOpMakeDirectoryCommand)]) { if (![fileManager makeDirectoryAtURL:fileURL error:NULL]) { exit(SUMakeDirectoryFailure); } } else if ([command isEqualToString:@(SUFileOpRemoveCommand)]) { if (![fileManager removeItemAtURL:fileURL error:NULL]) { exit(SURemoveFailure); } } else if ([command isEqualToString:@(SUFileOpInstallCommand)]) { // The one command that can *only* be run as the root user NSString *installerPath = @"/usr/sbin/installer"; NSTask *task = [[NSTask alloc] init]; task.launchPath = installerPath; task.arguments = @[@"-pkg", filepath, @"-target", @"/"]; // Output won't show up anyway, so we may as well ignore it task.standardError = [NSPipe pipe]; task.standardOutput = [NSPipe pipe]; @try { [task launch]; [task waitUntilExit]; if (task.terminationStatus != EXIT_SUCCESS) { exit(SUPackageFailureStatusCode); } } @catch (NSException *) { exit(SUPackageRaisedExceptionFailure); } } else { exit(SUInvalidCommandFailure); } return EXIT_SUCCESS; } } ================================================ FILE: archive/bitbar/Docs/DistributingBitBar.md ================================================ # Distribution You can currently choose between two versions of the BitBar app: **BitBar** and **BitBarDistro**. **Both** can be [bundled with plugins](#bundling-plugins). **BitBarDistro** disables user configuration by default. We provide builds of each version with the [latest release](https://github.com/matryer/bitbar/releases/latest). ## Bundling plugins We recommend using our [bundler script](https://github.com/matryer/bitbar/blob/master/Scripts/bitbar-bundler) for convenience. It takes a version of the BitBar app and the plugins to bundle, removes the code signature from the app, copies the plugins into the app bundle and ensures they are executable. If needed, please re-sign the app with your own certificate via the [Apple Developer Program](https://developer.apple.com/programs/). Usage: ``` bitbar-bundler /path/to/BitBar.app /path/to/first-plugin /path/to/second-plugin ... ``` If user configuration is enabled, symbolic links to the bundled plugins are created in the plugin folder selected by the user. ## Settings You can use `defaults` to set the plugins directory and whether user configuration should be enabled programmatically. Examples: ``` defaults write com.matryer.BitBar pluginsDirectory "path/to/plugins" defaults write com.matryer.BitBar userConfigDisabled -bool true ``` For global setting use `/Library/Preferences/com.matryer.BitBar` as domain. ================================================ FILE: archive/bitbar/Docs/URLScheme.md ================================================ # URL Scheme The BitBar app registers the custom URL scheme `bitbar://`. The following paths are currently implemented: - [`openPlugin`](#openplugin) to download and install plugins - [`refreshPlugin`](#refreshplugin) to refresh plugins ## openPlugin Query parameters: - `src` Source URL Examples: see [test page](App/BitBar/incoming-url-tests.html) ## refreshPlugin Query parameters: - `name` Filename, allowing wildcards. `?` matches one character and `*` matches zero or more characters This allows for refreshing from the command line using `open`, passing the URL. For example by chaining it with a semicolon after a command when `terminal=false` can't be used ([full example](https://github.com/matryer/xbar-plugins/blob/master/System/downloads.1h.sh)). Example: ``` bitbar://refreshPlugin?name=brew-updates.*?.sh ``` Here `*?` was used to omit the time. ================================================ FILE: archive/bitbar/Makefile ================================================ PROJECT_NAME ?= BitBar PROJECT = $(shell find . -name 'BitBar.xcodeproj') all: build clean: rm -r ./**/build build: git submodule init && git submodule update xcodebuild -project $(PROJECT) ps aux | grep $(PROJECT_NAME) | grep -v grep >/dev/null 2>&1 && killall $(PROJECT_NAME) open $(PROJECT)/../build/Release/$(PROJECT_NAME).app ================================================ FILE: archive/bitbar/README.md ================================================ # [BREAKING: BITBAR IS GETTING A REBOOT](https://github.com/matryer/bitbar/issues/607) << read # ![BitBar](https://github.com/matryer/bitbar/raw/master/Docs/bitbar-32.png) BitBar BitBar (by [Mat Ryer - @matryer](https://twitter.com/matryer)) lets you put the output from any script/program in your Mac OS X Menu Bar. * [Download latest BitBar release](https://github.com/matryer/bitbar/releases/latest) - requires Mac OS X Lion or newer (>= 10.7) * [Visit the app homepage at https://getbitbar.com](https://getbitbar.com) to install plugins * [Get started](#get-started) and [installing plugins](#installing-plugins) Digging deeper: * [Browse plugin repository](https://github.com/matryer/xbar-plugins) * [Guide to writing your own plugins](#writing-plugins) * [Distributing pre-configured BitBar](https://github.com/matryer/bitbar/blob/master/Docs/DistributingBitBar.md) * [Learn about integrating with bitbar via the bitbar:// URL scheme](https://github.com/matryer/bitbar/blob/master/Docs/URLScheme.md) And finally... * [Read the story about how BitBar unexpectedly got going](https://medium.com/@matryer/what-happens-when-your-old-open-source-project-unexpectedly-gets-to-the-top-of-hacker-news-31114c6c6efb#.fznvtgskb) * [Contributing](#contributing) and [special thanks](#thanks) ## Examples Example showing the latest Buy and Sell figures for Bitcoin: ![BitBar Example showing BitCoins plugin](https://raw.github.com/matryer/bitbar/master/Docs/BitBar-Example-Bitcoins.png) Click to see the full output, and more options: ![BitBar Example showing menu open](https://raw.github.com/matryer/bitbar/master/Docs/BitBar-Example-Menu.png) Example showing your internal and external IP addresses: ![BitBar Example showing IP Addresses](https://raw.github.com/matryer/bitbar/master/Docs/BitBar-Example-IPs.png) ## Get started ### Installing BitBar There are two ways to install BitBar on your Mac: Use Homebrew: brew install bitbar Or download .app file directly: [Get the latest version of BitBar](https://github.com/matryer/bitbar/releases). Then copy it to your Applications folder and run it - it will ask you to (create and) select a plugins folder, do so. ## Installing plugins There are two ways to install BitBar plugins on your Mac: [Browse our plugins](https://github.com/matryer/xbar-plugins) to find useful scripts, or [write your own](https://github.com/matryer/bitbar#writing-plugins). Or just download the plugin of your choice into your BitBar plugins directory and choose `Refresh` from one of the BitBar menus. Remember to use `chmod +x {pathname}` before you use the plugins. * `pathname` - The pathname of the file ### Configure the refresh time The refresh time is in the filename of the plugin, following this format: {name}.{time}.{ext} * `name` - The name of the file * `time` - The refresh rate (see below) * `ext` - The file extension For example: * `date.1m.sh` would refresh every minute. Most plugins will come with a default, but you can change it to anything you like: * 10s - ten seconds * 1m - one minute * 2h - two hours * 1d - a day ### Ensure you have execution rights Ensure the plugin is executable by running `chmod +x plugin.sh`. ### Using symlinks Because Git will ignore everything in `Plugins/Enabled`, you can use it to maintain your own plugins directory while still benefitting from tracking (upstream) changes. #### Example cd Plugins/Enabled # Enable spotify plugin ln -s ../Music/spotify.10s.sh # Enable uptime plugin and change update interval to 30 seconds ln -s ../System/uptime.1m.sh uptime.30s.sh Then select the `Enabled` folder in your BitBar preferences. #### Resetting Plugin Directory In case you made the mistake of choosing a directory with thousands of files as the plugin directory and BitBar getting stuck forever, do this from terminal to reset it: `defaults delete com.matryer.BitBar` ## Contributing * Help us [solve bugs](https://github.com/matryer/bitbar/issues?q=is%3Aopen+is%3Aissue+label%3Abug) or [build new features](https://github.com/matryer/bitbar/issues?q=is%3Aopen+is%3Aissue+label%3A%22♡+todo%22). * If you want to contribute a plugin, please head over to the [Plugin repository](https://github.com/matryer/xbar-plugins) and submit a pull request. Be sure to read our [guide to writing plugins](https://github.com/matryer/bitbar#writing-plugins) below. ### BitBar app To work on the BitBar app, fork, then clone this repo. In terminal, navigate to the project directory and run: ``` git submodule init && git submodule update ``` ## Thanks * Special thanks to [@muhqu](https://github.com/muhqu) and [@tylerb](https://github.com/tylerb) for all their help (see commit history for details) * Thanks to [Chris Ryer](http://www.chrisryer.co.uk/) for the app logo - and to [@mazondo](https://twitter.com/mazondo) for the original * Thanks for all our [plugin contributors](https://github.com/matryer/xbar-plugins) who have come up with some pretty genius things # Writing plugins We're always looking for new plugins, so please send us pull requests if you write anything cool or useful. [Join the conversation with plugin authors and BitBar maintainers on Slack](https://getbitbar.herokuapp.com/). ### Got ideas? If you've got ideas, or want to report a bug, nip over to our [issues page](=https://github.com/matryer/xbar-plugins/issues) and let us know. If you want to contribute, please send us a pull request and we'll add it to our repos. * Ensure the plugin is executable * Be sure to include [appropriate Metadata](#metadata) to enhance the plugin's entry on getbitbar.com ## Plugin API * To write a plugin, just write some form of executable script that outputs to the standard output. * Multiple lines will be cycled through over and over. * If your output contains a line consisting only of `---`, the lines below it will appear in the dropdown for that plugin, but won't appear in the menu bar itself. * Lines beginning with `--` will appear in submenus. * Use `----` etc. for nested submenus. Two dashes per level of nesting. * Your lines might contain `|` to separate the title from other parameters, such as... * `href=..` to make the item clickable * `color=..` to change their text color. eg. `color=red` or `color=#ff0000` * `font=..` to change their text font. eg. `font=UbuntuMono-Bold` * `size=..` to change their text size. eg. `size=12` * `bash=..` to make the item run a given script terminal with your script e.g. `bash=/Users/user/BitBar_Plugins/scripts/nginx.restart.sh` if there are spaces in the file path you will need quotes e.g. `bash="/Users/user/BitBar Plugins/scripts/nginx.restart.sh"` * `param1=` to specify arguments to the script. additional params like this `param2=foo param3=bar` full example `bash="/Users/user/BitBar_Plugins/scripts/nginx.restart.sh" param1=--verbose` assuming that nginx.restart.sh is executable or `bash=/usr/bin/ruby param1=/Users/user/rubyscript.rb param2=arg1 param3=arg2` if script is not executable * `terminal=..` start bash script without opening Terminal. `true` or `false` * `refresh=..` to make the item refresh the plugin it belongs to. If the item runs a script, refresh is performed after the script finishes. eg. `refresh=true` * `dropdown=..` May be set to `true` or `false`. If `false`, the line will only appear and cycle in the status bar but not in the dropdown * `length=..` to truncate the line to the specified number of characters. A `…` will be added to any truncated strings, as well as a tooltip displaying the full string. eg. `length=10` * `trim=..` whether to trim leading/trailing whitespace from the title. `true` or `false` (defaults to `true`) * `alternate=true` to mark a line as an alternate to the previous one for when the Option key is pressed in the dropdown * `templateImage=..` set an image for this item. The image data must be passed as base64 encoded string and should consist of only black and clear pixels. The alpha channel in the image can be used to adjust the opacity of black content, however. This is the recommended way to set an image for the statusbar. Use a 144 DPI resolution to support Retina displays. The imageformat can be any of the formats supported by Mac OS X * `image=..` set an image for this item. The image data must be passed as base64 encoded string. Use a 144 DPI resolution to support Retina displays. The imageformat can be any of the formats supported by Mac OS X * `emojize=false` will disable parsing of github style `:mushroom:` into :mushroom: * `ansi=false` turns off parsing of ANSI codes. ### Metadata To enhance your entry on [getbitbar.com](https://getbitbar.com/), add the following metadata to your source code (usually in comments somewhere): ``` # Title goes here # v1.0 # Your Name # your-github-username # Short description of what your plugin does. # http://www.hosted-somewhere/pluginimage # python,ruby,node # http://url-to-about.com/ ``` * The comment characters can be anything - use what is suitable for your language * `bitbar.title` - The title of the plugin * `bitbar.version` - The version of the plugin (start with `v1.0`) * `bitbar.author` - Your name * `bitbar.author.github` - Your github username (without `@`) * `bitbar.desc` - A short description of what your plugin does * `bitbar.image` - A hosted image showing a preview of your plugin (ideally open) * `bitbar.dependencies` - Comma separated list of dependencies * `bitbar.abouturl` - Absolute URL to about information For a real example, see the [Cycle text and detail plugin source code](https://github.com/matryer/xbar-plugins/blob/master/Tutorial/cycle_text_and_detail.sh). ### Useful tips * If you're writing scripts, ensure it has a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) at the top. * You can add to `PATH` by including something like `export PATH='/usr/local/bin:/usr/bin:$PATH'` in your plugin script. * You can use emoji in the output (find an example in the Music/vox Plugin). * If your bash script generates text in another language, set the `LANG` variable with: `export LANG="es_ES.UTF-8"` (for Spanish) to show the text in correct format. * If you want to call the plugin script for action, you can use `bash=$0` * If your plugin should support Retina displays, export your icon at 36x36 with a resolution of 144 DPI (see [this issue](https://github.com/matryer/bitbar/issues/314) for a more thorough explanation). ### Examples #### One line plugin #!/bin/bash date #### Multi-line plugin #!/bin/bash # the current date and time date # the current username echo $USER # the current user id id -u #### Multi-line plugin with extra data #!/bin/bash echo "One" echo "Two" echo "Three" echo "---" echo "Four" echo "Five" echo "Six" * Only One, Two and Three will appear in the top bar * Clicking the plugin menu item will show all lines #### Multi-line plugin with links and colors #!/bin/bash curl -m 1 http://example.com -I >/dev/null 2>&1 [ $? -gt 0 ] && echo "FAIL | color=red" || echo "OK | color=green" echo "---" echo "Show Graphs | color=#123def href=http://example.com/graph?foo=bar" echo "Show KPI Report | color=purple href=http://example.com/report" #### Multi-line plugin with fonts and colors ![BitBar Example showing colored fonts](https://raw.github.com/matryer/bitbar/master/Docs/BitBar-Example-Menu-Colors-Fonts.png) #!/bin/zsh FONT=( 'size=14' 'font=UbuntuMono' ) if ((0)); then echo "DO | $FONT color=orange" else echo "DO | $FONT color=cadetblue" echo "---" ... ### Tested languages Anything that can write to standard out is supported, but here is a list that have been explicitly tested. 1. Ruby - Status: Working - Output: `puts "your string here"` 1. Python2 - Status: Working - Output: `print "your string here"` 1. Python3 - Status: Working - Output: `print("your string here")` - Caveats: To output unicode shebang has to be in the format `#!/usr/bin/env PYTHONIOENCODING=UTF-8 /path/to/the/python3` 1. JavaScript (`node`) - Status: Working - Caveats: Shebang has to be in the format `#!/usr/bin/env /path/to/the/node/executable` - Output: `console.log("your string here")` - Notes: - `process.stdout.write` doesn't output desired text. - There may be a better way to run JavaScript files. - Tips: - Use the Node.js [`bitbar` module](https://github.com/sindresorhus/bitbar) to simplify plugin creation. 1. CoffeeScript (`coffee`) - Status: Working - Caveats: - Shebang has to be in the format `#!/usr/bin/env /path/to/the/coffee/executable` - `coffee` shebang also had to be modified. - `#!/usr/bin/env /path/to/the/node/executable` - Output: `console.log "your string here"` - Notes: - `process.stdout.write` doesn't output desired text. - There may be a better way to run CoffeeScript files. 1. Swift (Interpreted) - Status: Working - Output: `print("your string here")` 1. Swift (Compiled) - Status: Working - Caveats: You still need a file extension (`file.1s.cswift`) - Output: `print("your string here")` - Notes: - To compile a swift file, use: `xcrun -sdk macosx swiftc -o file.1s.cswift file.1s.swift` 1. Go (Interpreted) - Status: Working - Caveats: - Your script's shebang must be: `//usr/bin/env go run $0 $@; exit` - `go` must be in your `PATH` - Output: `Println("your string here")` 1. Go (Compiled) - Status: Working - Caveats: You still need a file extension (`file.1s.gotool`) - Output: `Println("your string here")` - Notes - To compile a Go file, use: `go build file.1s.go` 1. Lisp - Status: Working - Caveats: `lisp`/`clisp` must be in your `PATH` - Output: `(format t "your string here")` 1. Perl5 - Status: Working - Output: `print "your string here"` - Notes - Add `-l` to shebang to automatic add newline to print function: `#!/usr/bin/perl -l` 1. PHP - Status: Working - Output: `echo 'your string here'` - Notes - Add shebang `#!/usr/bin/php` - Utilities: - BitBar PHP Formatter - ================================================ FILE: archive/bitbar/Scripts/bitbar-bundler ================================================ #!/bin/bash [ "$#" -ge "2" ] || { echo "usage: $0 /path/to/BitBar.app /path/to/first-plugin /path/to/second-plugin ..."; exit 1; } codesign --deep --force --verbose --sign - "$1" app="$1/Contents/MacOS/" shift # copy plugins into the app's executables directory cp -v "$@" "$app" # ensure they are executable chmod -R +x "$app" ================================================ FILE: pkg/metadata/categories_metadata.go ================================================ package metadata import ( "path" "time" ) // Category is a path segment, like a category. type Category struct { Path string `json:"path"` Text string `json:"text"` Children []Category `json:"children"` ChildrenCategories map[string]Category `json:"-"` CategoryPathSegments []PathItem `json:"categoryPathSegments"` LastUpdated time.Time `json:"lastUpdated"` } // LastUpdatedFormatted is a formatted string. func (c Category) LastUpdatedFormatted() string { return c.LastUpdated.Format(time.RFC822) } // CategoryWalk walks all category objects in the tree calling // fn for each. func CategoryWalk(categories map[string]Category, fn func(Category)) { for _, category := range categories { fn(category) if len(category.ChildrenCategories) > 0 { CategoryWalk(category.ChildrenCategories, fn) } } } func CategoryEnsurePath(categories map[string]Category, parentPath []string, pathCategorySegments []string) { if len(pathCategorySegments) == 0 { // no work to do return } categoryName := pathCategorySegments[0] category, ok := categories[categoryName] if !ok { pathSegs := append(parentPath, categoryName) categoryPath := path.Join(pathSegs...) category = Category{ Path: categoryPath, Text: categoryName, ChildrenCategories: make(map[string]Category), LastUpdated: time.Now(), CategoryPathSegments: CategoryPathSegments(categoryPath), } categories[categoryName] = category } if len(pathCategorySegments) > 1 { CategoryEnsurePath(category.ChildrenCategories, append(parentPath, pathCategorySegments[0]), pathCategorySegments[1:]) } } ================================================ FILE: pkg/metadata/categories_metadata_test.go ================================================ package metadata import ( "testing" "github.com/matryer/is" ) func TestCategoryMapper(t *testing.T) { is := is.New(t) categories := make(map[string]Category) CategoryEnsurePath(categories, nil, []string{"parent1", "child1", "grandchild1"}) CategoryEnsurePath(categories, nil, []string{"parent1", "child2", "grandchild1"}) CategoryEnsurePath(categories, nil, []string{"parent2", "Child1", "Grandchild1"}) CategoryEnsurePath(categories, nil, []string{"parent3", "child1"}) is.Equal(len(categories), 3) // parents is.Equal(len(categories["parent1"].ChildrenCategories), 2) // parent1 children is.Equal(len(categories["parent2"].ChildrenCategories["Child1"].ChildrenCategories), 1) // parent2 child1 grandchildren // b, err := json.MarshalIndent(segments, "", "\t") // is.NoErr(err) // log.Println(string(b)) } ================================================ FILE: pkg/metadata/go.mod ================================================ module github.com/matryer/xbar/pkg/metadata go 1.15 require ( github.com/leaanthony/go-ansi-parser v1.2.0 github.com/matryer/is v1.4.0 github.com/pkg/errors v0.9.1 ) ================================================ FILE: pkg/metadata/go.sum ================================================ github.com/leaanthony/go-ansi-parser v1.2.0 h1:CcBhxqkxPATj7Lgdp9EgPUGv2o3FGkg3A5eN7ermsbM= github.com/leaanthony/go-ansi-parser v1.2.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= ================================================ FILE: pkg/metadata/plugin_metadata.go ================================================ package metadata import ( "fmt" "github.com/leaanthony/go-ansi-parser" "math/rand" "os" "path" "regexp" "strconv" "strings" "time" "github.com/pkg/errors" ) // Plugin is the plugin metadata payload returned by Parse. type Plugin struct { // Files are the files that make up this Plugin. Files []File `json:"files"` // Path is the unique path to this plugin. Path string `json:"path"` // Filename is the filename for this plugin. Filename string `json:"filename"` // Dir is the virtual directory of this plugin. Dir string `json:"dir"` // DocsPath is the path to the documentation for this // plugin. DocsPlugin string `json:"docsPlugin"` // CategoryPath is the path of the category this plugin is in. CategoryPath string `json:"-"` // DocsCategory is the path to the documentation for this // plugin. DocsCategory string `json:"docsCategory"` // PathSegments are the segments that describe the path // of this plugin. Each subsequent item is a child of the previous segment. PathSegments []string `json:"pathSegments"` // PathSegments are the segments that describe the path // of this plugin. Each subsequent item is a child of the previous segment. CategoryPathSegments []PathItem `json:"categoryPathSegments"` // Title is the plugin title. Title string `json:"title"` // Version is the latest version number. Version string `json:"version"` // Author is the list of authors. Use Authors for structured data. Author string `json:"author"` // Authors contains information about the people who contributed to this plugin. Authors []Person `json:"authors"` // Desc is a short description of this plugin. Desc string `json:"desc"` // ImageURL is a public URL containing the preview image for this plugin. ImageURL string `json:"imageURL"` // Dependencies are a list of explicit dependencies this plugin requires to run. Dependencies []string `json:"dependencies"` // AboutURL is the public URL to learn more about the plugin, including // to contact the author. AboutURL string `json:"aboutURL"` // LastUpdated is when this data was last updated. LastUpdated time.Time `json:"lastUpdated"` // Vars are the configurable values for this Plugin. Vars []PluginVar `json:"vars"` // ProcessingNotes is a list of errors/warnings/notes that are set during // the processing of this plugin. ProcessingNotes []string `json:"processingNotes"` } // Validate checks the plugin data. func (p Plugin) Validate() error { if p.Title == "" { return errors.New("missing xbar.title") } if p.Desc == "" { return errors.New("missing xbar.desc") } if !HasImage(p.ImageURL) { return errors.New("missing xbar.image") } if len(p.Authors) == 0 { return errors.New("missing xbar.author") } return nil // ok } // NiceDesc gets a nice description. func (p Plugin) NiceDesc() string { if p.Desc == "" { return p.Title } return truncate(p.Desc, 75) } // File is a single file. type File struct { // Path is the path of the File. Path string `json:"path"` // Filename is the file name of this File. Filename string `json:"filename"` // Content is the content of the File. Content string `json:"content"` } // PluginVar describes a configurable value for a Plugin. type PluginVar struct { // Type is the type of the value. One of "string", "number", "boolean", or "select". Type string `json:"type"` // Name is the name of the variable. Name string `json:"name"` // Label is the display text for this variable (derived from Name). Label string `json:"label"` // Default is the default value. Default string `json:"default"` // Desc is a description of the variable. Desc string `json:"desc"` // Options are the available options for "select" types. Options []string `json:"options"` } // DefaultValue gets the Default value in the correct type. func (p PluginVar) DefaultValue() interface{} { switch p.Type { case "select": return p.Default case "string": return p.Default case "number": f, err := strconv.ParseFloat(p.Default, 64) if err != nil { return 0.0 } return f case "boolean": if p.Default == "true" { return true } return false } return p.Default } // LastUpdatedFormatted is a formatted string. func (p Plugin) LastUpdatedFormatted() string { return p.LastUpdated.Format(time.RFC822) } // Person represents a human. type Person struct { Name string `json:"name"` GitHubUsername string `json:"githubUsername"` ImageURL string `json:"imageURL"` Bio string `json:"bio"` Primary bool `json:"primary"` } // Desc gets a nice description of this person. func (p Person) Desc() string { desc := p.Name if desc == "" { desc = "@" + p.GitHubUsername } else { desc += " (@" + p.GitHubUsername + ")" } if p.Bio != "" { desc += " - " + p.Bio } return strings.TrimSpace(desc) } // NiceBio gets a nice bio for this Person. func (p Person) NiceBio() string { if p.Bio == "" { name := p.Name if name == "" { name = p.GitHubUsername } if name == "" { name = "This person" } return name + " is an xbar plugin contributor." } return p.Bio } // Complete checks the metadata to see if it is complete. // If not, it will return an error that would be associated with // the plugin, until it is next checked. func (p Plugin) Complete() error { if p.Title == "" { return errMissingMetadata("xbar.title") } return nil } // Parse parses the s input extracting bitbar or xbar metadata. func Parse(debugf DebugFunc, filename, s string) (Plugin, error) { var p Plugin p.LastUpdated = time.Now() p.Files = []File{ { Path: filename, Filename: path.Base(filename), Content: s, }, } p.Filename = path.Base(filename) p.Dir = path.Dir(filename) p.PathSegments = strings.Split(path.Dir(filename), string(os.PathSeparator)) p.CategoryPathSegments = CategoryPathSegments(path.Dir(filename)) bitbarMatches, err := regexp.Compile(`<([bitbar].*?)>(.*)`) if err != nil { return p, err } xbarMatches, err := regexp.Compile(`<([xbar].*?)>(.*)`) if err != nil { return p, err } submatchall := append(bitbarMatches.FindAllStringSubmatch(s, -1), xbarMatches.FindAllStringSubmatch(s, -1)...) for _, element := range submatchall { debugf("%s: %s ", element[1], element[2]) switch strings.ToLower(element[1]) { case "bitbar.title", "xbar.title": p.Title = element[2] debugf("✓\n") case "bitbar.version", "xbar.version": p.Version = element[2] debugf("✓\n") case "bitbar.author", "xbar.author": authorNames := strings.Split(element[2], ",") p.Author = strings.Join(authorNames, ", ") for i, authorName := range authorNames { authorName = strings.TrimSpace(authorName) if len(p.Authors) < i+1 { p.Authors = append(p.Authors, Person{}) } p.Authors[i].Name = authorName } debugf("✓\n") case "bitbar.author.github", "xbar.author.github": authorUsernames := strings.Split(element[2], ",") for i, authorUsername := range authorUsernames { authorUsername = strings.TrimSpace(authorUsername) if len(p.Authors) < i+1 { p.Authors = append(p.Authors, Person{}) } p.Authors[i].GitHubUsername = authorUsername } debugf("✓\n") case "bitbar.desc", "xbar.desc": p.Desc = element[2] debugf("✓\n") case "bitbar.image", "xbar.image": p.ImageURL = element[2] debugf("✓\n") case "bitbar.abouturl", "xbar.abouturl": p.AboutURL = element[2] debugf("✓\n") case "bitbar.dependencies", "xbar.dependencies": p.Dependencies = splitList(element[2]) debugf("✓\n") case "xbar.var": v, err := parsePluginVar(element[2]) if err != nil { return p, err } p.Vars = append(p.Vars, v) debugf("✓\n") default: debugf("(skipping) unknown parameter %s\n", element[1]) } } if len(p.Authors) > 0 { // the first author is the "primary" one p.Authors[0].Primary = true } return p, nil } // PathItem is a path segment. type PathItem struct { Path string `json:"path"` Text string `json:"text"` IsLast bool `json:"isLast"` } // CategoryPathSegments splits a category path string into segments to // help making navigation breadcrumbs. func CategoryPathSegments(categoryPath string) []PathItem { segs := strings.Split(categoryPath, "/") var path string items := make([]PathItem, len(segs)) for i, seg := range segs { if path != "" { path += "/" } path += seg items[i].Path = path items[i].Text = seg } items[len(items)-1].IsLast = true return items } func splitList(s string) []string { segs := strings.Split(s, ",") cleanSegs := make([]string, 0, len(segs)) for i := range segs { segs[i] = strings.TrimSpace(segs[i]) if segs[i] != "" { cleanSegs = append(cleanSegs, segs[i]) } } return cleanSegs } // DebugFunc is a function that writes debug information. // Use DebugfNoop for silence. type DebugFunc func(format string, v ...interface{}) // DebugfNoop is a silent DebugFunc. func DebugfNoop(format string, v ...interface{}) {} // DebugfLog uses log.Print to write debug information. func DebugfLog(format string, v ...interface{}) { fmt.Printf(format, v...) } // DefaultPluginImage is the image to use when none is set. const DefaultPluginImage = "https://xbarapp.com/public/img/xbar-2048.png" // HasImage tests whether the plugin's ImageURL is a specific // image or not. func HasImage(imageURL string) bool { if imageURL == "" { return false } if imageURL == DefaultPluginImage { return false } return true } // RandomPlugins selects n random plugins from the func RandomPlugins(pluginsByPath map[string][]Plugin, pathPrefix string, n int) []Plugin { var pluginsWithImages []Plugin for _, plugins := range pluginsByPath { for _, plugin := range plugins { if !HasImage(plugin.ImageURL) { continue } if plugin.Author == "" { continue } if !strings.HasPrefix(plugin.Path, pathPrefix) { continue } pluginsWithImages = append(pluginsWithImages, plugin) } } if len(pluginsWithImages) <= n { return pluginsWithImages } rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(pluginsWithImages), func(i, j int) { pluginsWithImages[i], pluginsWithImages[j] = pluginsWithImages[j], pluginsWithImages[i] }) return pluginsWithImages[:n] } func parsePluginVar(s string) (PluginVar, error) { var v PluginVar varLineRegexp, err := regexp.Compile(`(.+)\((.+)\):\s(.+)`) if err != nil { return v, errors.Wrap(err, "var line regexp") } segments := varLineRegexp.FindAllStringSubmatch(s, -1) if len(segments) != 1 { return v, errParse{ src: s, err: errors.New("malformed xbar.var format"), } } if len(segments[0]) != 4 { return v, errParse{ src: s, err: errors.New("malformed xbar.var format"), } } segs := segments[0] v.Type = segs[1] v.Desc = segs[3] v.Name = segs[2] if strings.Contains(v.Name, "=") { nameSegs := strings.Split(v.Name, "=") v.Name = nameSegs[0] v.Default = strings.Trim(nameSegs[1], `"'`) } v.Label = v.Name if strings.HasPrefix(v.Name, "VAR_") { v.Label = strings.ToLower(strings.TrimPrefix(v.Name, "VAR_")) v.Label = strings.ToUpper(v.Label[0:1]) + v.Label[1:] v.Label = strings.ReplaceAll(v.Label, "_", " ") } switch v.Type { case "string", "number", "boolean": // valid types - but no work to do case "select": // extract options from description listSegs := strings.Split(v.Desc, `[`) if len(listSegs) < 2 { return v, errParse{ src: s, err: errors.New("malformed xbar.var format (missing select options)"), } } v.Desc = strings.TrimSpace(listSegs[0]) optionsStr := strings.TrimSuffix(listSegs[1], `]`) for _, option := range strings.Split(optionsStr, ",") { cleanStr := strings.TrimSpace(option) if cleanStr == "" { continue // skip empty lines } v.Options = append(v.Options, cleanStr) } if len(v.Options) == 0 { return v, errParse{ src: s, err: errors.New("malformed xbar.var format (empty select options)"), } } defaultFound := false for _, val := range v.Options { if val == v.Default { defaultFound = true break } } if !defaultFound { return v, errParse{ src: s, err: errors.New("malformed xbar.var format (default not in select options)"), } } default: // invalid type return v, errors.Errorf("unknown xbar.var type: %s", v.Type) } return v, nil } type errMissingMetadata string func (e errMissingMetadata) Error() string { return fmt.Sprintf("missing <%[1]s>", string(e)) } type errParse struct { src string err error } func (e errParse) Error() string { if e.src != "" { return fmt.Sprintf("%s: %q", e.err, e.src) } return e.err.Error() } // truncate shrinks a string if it's too long. func truncate(s string, max int) string { truncated, err := ansi.Truncate(s, max) if err != nil { // If, for some reason, there's an error when // parsing, do what we used to do runes := []rune(s) if max > 0 && len(runes) > max { s = string(runes[:max-1]) + "…" } return s } length, _ := ansi.Length(truncated) if length == max-1 { truncated += "…" } return truncated } ================================================ FILE: pkg/metadata/plugin_metadata_test.go ================================================ package metadata import ( "strings" "testing" "github.com/matryer/is" ) func TestParse(t *testing.T) { is := is.New(t) debugf := DebugfNoop md, err := Parse(debugf, "test.txt", ` # Title goes here # v1.0 # Your Name,Another name # your-github-username,another-github-author # Short description of what your plugin does. # http://www.hosted-somewhere/pluginimage # python,ruby,node # http://url-to-about.com/ `) is.NoErr(err) is.Equal(md.Title, "Title goes here") is.Equal(md.Version, "v1.0") is.Equal(len(md.Authors), 2) is.Equal(md.Author, "Your Name, Another name") is.Equal(md.Authors[0].Primary, true) is.Equal(md.Authors[0].Name, "Your Name") is.Equal(md.Authors[0].GitHubUsername, "your-github-username") is.Equal(md.Authors[1].Primary, false) is.Equal(md.Authors[1].Name, "Another name") is.Equal(md.Authors[1].GitHubUsername, "another-github-author") is.Equal(md.Desc, "Short description of what your plugin does.") is.Equal(len(md.Dependencies), 3) is.Equal(md.Dependencies[0], "python") is.Equal(md.Dependencies[1], "ruby") is.Equal(md.Dependencies[2], "node") is.Equal(md.AboutURL, "http://url-to-about.com/") md, err = Parse(debugf, "test.txt", ` # Title goes here # v1.0 # Your Name # your-github-username # Short description of what your plugin does. # http://www.hosted-somewhere/pluginimage # python,ruby,node # https://url-to-about.com/ `) is.NoErr(err) is.Equal(md.Title, "Title goes here") is.Equal(md.Version, "v1.0") is.Equal(len(md.Authors), 1) is.Equal(md.Authors[0].Name, "Your Name") is.Equal(md.Authors[0].GitHubUsername, "your-github-username") is.Equal(md.Desc, "Short description of what your plugin does.") is.Equal(len(md.Dependencies), 3) is.Equal(md.Dependencies[0], "python") is.Equal(md.Dependencies[1], "ruby") is.Equal(md.Dependencies[2], "node") is.Equal(md.AboutURL, "https://url-to-about.com/") md, err = Parse(DebugfNoop, "test.txt", ` /* Amother other kinds of comments: Title goes here v1.0 Your Name your-github-username Short description of what your plugin does. http://www.hosted-somewhere/pluginimage python,ruby,node http://url-to-about.com/ */ `) is.NoErr(err) is.Equal(md.Title, "Title goes here") is.Equal(md.Version, "v1.0") is.Equal(len(md.Authors), 1) is.Equal(md.Authors[0].Name, "Your Name") is.Equal(md.Authors[0].GitHubUsername, "your-github-username") is.Equal(md.Desc, "Short description of what your plugin does.") is.Equal(len(md.Dependencies), 3) is.Equal(md.Dependencies[0], "python") is.Equal(md.Dependencies[1], "ruby") is.Equal(md.Dependencies[2], "node") is.Equal(md.AboutURL, "http://url-to-about.com/") is.Equal(md.DocsCategory, "") } func TestPluginCategoryPathSegments(t *testing.T) { is := is.New(t) p := &Plugin{} p.CategoryPath = "Parent/Child/Grandchild/You" items := CategoryPathSegments(p.CategoryPath) is.Equal(4, len(items)) is.Equal(items[0].Path, "Parent") is.Equal(items[0].Text, "Parent") is.Equal(items[1].Path, "Parent/Child") is.Equal(items[1].Text, "Child") is.Equal(items[2].Path, "Parent/Child/Grandchild") is.Equal(items[2].Text, "Grandchild") is.Equal(items[3].Path, "Parent/Child/Grandchild/You") is.Equal(items[3].Text, "You") } func TestVariables(t *testing.T) { is := is.New(t) md, err := Parse(DebugfNoop, "test.txt", ` /* Variables can be specified and will become configurable via the xbar app. string(VAR_NAME="Mat Ryer"): Your name. number(VAR_COUNTER=1): A counter. boolean(VAR_VERBOSE=true): Whether to be verbose or not. select(VAR_DISPLAY_STYLE="normal"): Which style to use. [small, normal, big] Variables without VAR_ prefix won't be formatted: string(TWITTER_USERNAME="@matryer"): Your Twitter username. */ `) is.NoErr(err) is.Equal(len(md.Vars), 5) // len(md.Vars) is.Equal(md.Vars[0].Name, "VAR_NAME") // Name is.Equal(md.Vars[0].Label, "Name") // Label is.Equal(md.Vars[0].Type, "string") // Type is.Equal(md.Vars[0].Desc, "Your name.") // Desc is.Equal(md.Vars[0].Default, "Mat Ryer") // Default is.Equal(md.Vars[1].Name, "VAR_COUNTER") // Name is.Equal(md.Vars[1].Label, "Counter") // Label is.Equal(md.Vars[1].Type, "number") // Type is.Equal(md.Vars[1].Desc, "A counter.") // Desc is.Equal(md.Vars[1].Default, "1") // Default is.Equal(md.Vars[2].Name, "VAR_VERBOSE") // Name is.Equal(md.Vars[2].Label, "Verbose") // Label is.Equal(md.Vars[2].Type, "boolean") // Type is.Equal(md.Vars[2].Desc, "Whether to be verbose or not.") // Desc is.Equal(md.Vars[2].Default, "true") // Default is.Equal(md.Vars[3].Name, "VAR_DISPLAY_STYLE") // Name is.Equal(md.Vars[3].Label, "Display style") // Label is.Equal(md.Vars[3].Type, "select") // Type is.Equal(md.Vars[3].Desc, "Which style to use.") // Desc is.Equal(md.Vars[3].Default, "normal") // Default is.Equal(len(md.Vars[3].Options), 3) // Options is.Equal(md.Vars[3].Options[0], "small") // Options is.Equal(md.Vars[3].Options[1], "normal") // Options is.Equal(md.Vars[3].Options[2], "big") // Options is.Equal(md.Vars[4].Name, "TWITTER_USERNAME") // Name is.Equal(md.Vars[4].Label, "TWITTER_USERNAME") // Label is.Equal(md.Vars[4].Type, "string") // Type is.Equal(md.Vars[4].Desc, "Your Twitter username.") // Desc is.Equal(md.Vars[4].Default, "@matryer") // Default } func TestErrors(t *testing.T) { is := is.New(t) errs := map[string]string{ "missing select options": ` select(VAR_STYLE="normal"): Missing options. `, "empty select options": ` select(VAR_STYLE="normal"): Empty options. [] `, "default not in select options": ` select(VAR_STYLE="not-in-select"): Default not in the select of options. [one,two,three] `, "malformed": ` select(VAR_STYLE="): Missing options. `, } for expected, src := range errs { t.Run(expected, func(t *testing.T) { is := is.New(t) _, err := Parse(DebugfNoop, "test.script", src) is.True(err != nil) // expected to error is.True(strings.Contains(err.Error(), expected)) }) } } ================================================ FILE: pkg/plugins/README.md ================================================ # `xbar` Package The `xbar` Go package provides the core xbar functionality. ## Usage Run all the plugins in a directory: ```go func refreshFunc(ctx context.Context, p *Plugin, err error) { // todo: update the menu } func cycleFunc(ctx context.Context, p *Plugin) { // todo: update menu bar label } ps, err := plugins.Dir(filepath.Join("path", "to", "plugins")) if err != nil { return err } for i := range ps { ps[i].OnRefresh = refreshFunc ps[i].OnCycle = cycleFunc ps[i].Debugf = plugins.DebugfLog } ctx := context.Background() ps.Run(ctx) ``` ================================================ FILE: pkg/plugins/action.go ================================================ package plugins import ( "bytes" "context" "fmt" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" ) // ActionFunc is a function that handles the // menu item clicks/selections. type ActionFunc func(ctx context.Context) // actionTimeout is the amount of time xbar will wait for an // action to complete. const actionTimeout = 10 * time.Second // Action returns a function that will handle the // action should this item be clicked/selected. // nil response indicates no action, so you must check // for nil before calling. // The following code should be called: // actionFunc := item.Action() // if actionFunc != nil { // actionFunc(ctx) // } func (i *Item) Action() ActionFunc { debugf := DebugfNoop if i.Plugin != nil { debugf = i.Plugin.Debugf } var actions []ActionFunc if i.Params.Href != "" { actions = append(actions, actionHref(debugf, i.Params.Href)) } if i.Params.Shell != "" { actions = append(actions, actionShell(debugf, i, i.Plugin.AppleScriptTemplate, i.Params.Shell, i.Params.ShellParams, i.Plugin.Variables)) } if i.Params.Refresh { shouldDelayBeforeRefresh := false if len(actions) > 0 { // there are actions other than refresh, so let's introduce a // delay to let those other actions work before triggering // the refresh. shouldDelayBeforeRefresh = true } actions = append(actions, actionRefresh(debugf, func(ctx context.Context) { if shouldDelayBeforeRefresh { time.Sleep(500 * time.Millisecond) } i.Plugin.TriggerRefresh() })) } if len(actions) == 0 { return nil // no actions } return actionFuncs(actions...) } // actionFuncs makes an ActionFunc that runs multuple functions // in order. func actionFuncs(actions ...ActionFunc) ActionFunc { return func(ctx context.Context) { for i := range actions { if err := ctx.Err(); err != nil { return // don't bother - context cancelled } fn := actions[i] fn(ctx) } } } // actionHref gets an ActionFunc that opens a URL. func actionHref(debugf DebugFunc, href string) ActionFunc { return func(ctx context.Context) { debugf("action href: %s", href) commandCtx, cancel := context.WithTimeout(ctx, actionTimeout) defer cancel() var err error switch runtime.GOOS { case "linux": cmd := exec.CommandContext(commandCtx, "xdg-open", href) Setpgid(cmd) cmd.Run() case "windows": cmd := exec.CommandContext(commandCtx, "rundll32", "url.dll,FileProtocolHandler", href) Setpgid(cmd) cmd.Run() case "darwin": cmd := exec.CommandContext(commandCtx, "open", href) Setpgid(cmd) cmd.Run() default: err = fmt.Errorf("unsupported platform") } if err != nil { debugf("ERR: action href: %s", err) return } } } // actionShell gets an ActionFunc that runs a shell command. func actionShell(debugf DebugFunc, item *Item, appleScriptTemplate, command string, params, envVars []string) ActionFunc { if item.Params.Terminal { return actionShellTerminal(debugf, item, appleScriptTemplate, command, params, envVars) } return func(ctx context.Context) { var commandExec string var commandArgs []string commandExec = command commandArgs = params debugf("exec: %s %s", commandExec, strings.Join(commandArgs, " ")) cmd := exec.CommandContext(context.Background(), commandExec, commandArgs...) Setpgid(cmd) // wd should be where the plugin is running cmd.Dir = filepath.Dir(item.Plugin.Command) // and it can inherit the environment cmd.Env = append(cmd.Env, os.Environ()...) var stderr bytes.Buffer cmd.Stderr = &stderr err := cmd.Run() if err != nil { debugf("ERR: action shell: %s", errExec{ err: err, Stderr: stderr.String(), }) return } } } // actionShellTerminal runs shell commands where terminal=true. func actionShellTerminal(debugf DebugFunc, item *Item, appleScriptTemplate, command string, params, envVars []string) ActionFunc { return func(ctx context.Context) { debugf("exec: RunInTerminal...") command := strconv.Quote(command) command = command[1 : len(command)-1] // trim quotes off for i := range params { params[i] = strconv.Quote(params[i]) params[i] = params[i][1 : len(params[i])-1] // trim quotes off } paramsStr := strconv.Quote(strings.Join(params, " ")) err := item.Plugin.runInTerminal(appleScriptTemplate, command, paramsStr, envVars) if err != nil { debugf("exec: RunInTerminal: err=%s", err) return } } } // actionRefresh gets an ActionFunc that manually refreshes the // Plugin. func actionRefresh(debugf DebugFunc, refreshFunc func(ctx context.Context)) ActionFunc { return func(ctx context.Context) { debugf("action refresh") refreshFunc(ctx) } } ================================================ FILE: pkg/plugins/action_test.go ================================================ package plugins import ( "context" "testing" "github.com/matryer/is" ) func OffTestItemAction(t *testing.T) { is := is.New(t) // work in progress // action is called when the item is // clicked var action ActionFunc item := Item{ Text: "Item", Params: ItemParams{}, } action = item.Action() is.True(action == nil) item = Item{ Text: "Item", Params: ItemParams{ Href: "https://xbarapp.com", }, } action = item.Action() action(context.Background()) item = Item{ Text: "Item", Params: ItemParams{ Shell: `echo`, ShellParams: []string{`Hello`}, }, } action = item.Action() action(context.Background()) item = Item{ Text: "Item", Params: ItemParams{ Refresh: true, }, } action = item.Action() action(context.Background()) } func TestTerminal(t *testing.T) { p := NewPlugin("/dev/null") p.Debugf = DebugfLog item := Item{ Plugin: p, Text: "Item", Params: ItemParams{ Terminal: true, Shell: `echo \"hello\"`, ShellParams: []string{}, }, } action := item.Action() action(context.Background()) } ================================================ FILE: pkg/plugins/colors.go ================================================ package plugins import "regexp" var colorRegexp = regexp.MustCompile(`^#(?:(?:[\da-f]{3}){1,2}|(?:[\da-f]{4}){1,2})$`) var namedColors = map[string]string{ "lightseagreen": "#20b2aa", "floralwhite": "#fffaf0", "lightgray": "#d3d3d3", "darkgoldenrod": "#b8860b", "paleturquoise": "#afeeee", "goldenrod": "#daa520", "skyblue": "#87ceeb", "indianred": "#cd5c5c", "darkgray": "#a9a9a9", "khaki": "#f0e68c", "blue": "#0000ff", "darkred": "#8b0000", "lightyellow": "#ffffe0", "midnightblue": "#191970", "chartreuse": "#7fff00", "lightsteelblue": "#b0c4de", "slateblue": "#6a5acd", "firebrick": "#b22222", "moccasin": "#ffe4b5", "salmon": "#fa8072", "sienna": "#a0522d", "slategray": "#708090", "teal": "#008080", "lightsalmon": "#ffa07a", "pink": "#ffc0cb", "burlywood": "#deb887", "gold": "#ffd700", "springgreen": "#00ff7f", "lightcoral": "#f08080", "black": "#000000", "blueviolet": "#8a2be2", "chocolate": "#d2691e", "aqua": "#00ffff", "darkviolet": "#9400d3", "indigo": "#4b0082", "darkcyan": "#008b8b", "orange": "#ffa500", "antiquewhite": "#faebd7", "peru": "#cd853f", "silver": "#c0c0c0", "purple": "#800080", "saddlebrown": "#8b4513", "lawngreen": "#7cfc00", "dodgerblue": "#1e90ff", "lime": "#00ff00", "linen": "#faf0e6", "lightblue": "#add8e6", "darkslategray": "#2f4f4f", "lightskyblue": "#87cefa", "mintcream": "#f5fffa", "olive": "#808000", "hotpink": "#ff69b4", "papayawhip": "#ffefd5", "mediumseagreen": "#3cb371", "mediumspringgreen": "#00fa9a", "cornflowerblue": "#6495ed", "plum": "#dda0dd", "seagreen": "#2e8b57", "palevioletred": "#db7093", "bisque": "#ffe4c4", "beige": "#f5f5dc", "darkorchid": "#9932cc", "royalblue": "#4169e1", "darkolivegreen": "#556b2f", "darkmagenta": "#8b008b", "orange red": "#ff4500", "lavender": "#e6e6fa", "fuchsia": "#ff00ff", "darkseagreen": "#8fbc8f", "lavenderblush": "#fff0f5", "wheat": "#f5deb3", "steelblue": "#4682b4", "lightgoldenrodyellow": "#fafad2", "lightcyan": "#e0ffff", "mediumaquamarine": "#66cdaa", "turquoise": "#40e0d0", "dark blue": "#00008b", "darkorange": "#ff8c00", "brown": "#a52a2a", "dimgray": "#696969", "deeppink": "#ff1493", "powderblue": "#b0e0e6", "red": "#ff0000", "darkgreen": "#006400", "ghostwhite": "#f8f8ff", "white": "#ffffff", "navajowhite": "#ffdead", "navy": "#000080", "ivory": "#fffff0", "palegreen": "#98fb98", "whitesmoke": "#f5f5f5", "gainsboro": "#dcdcdc", "mediumslateblue": "#7b68ee", "olivedrab": "#6b8e23", "mediumpurple": "#9370db", "darkslateblue": "#483d8b", "blanchedalmond": "#ffebcd", "darkkhaki": "#bdb76b", "green": "#008000", "limegreen": "#32cd32", "snow": "#fffafa", "tomato": "#ff6347", "darkturquoise": "#00ced1", "orchid": "#da70d6", "yellow": "#ffff00", "green yellow": "#adff2f", "azure": "#f0ffff", "mistyrose": "#ffe4e1", "cadetblue": "#5f9ea0", "oldlace": "#fdf5e6", "gray": "#808080", "honeydew": "#f0fff0", "peachpuff": "#ffdab9", "tan": "#d2b48c", "thistle": "#d8bfd8", "palegoldenrod": "#eee8aa", "mediumorchid": "#ba55d3", "rosybrown": "#bc8f8f", "mediumturquoise": "#48d1cc", "lemonchiffon": "#fffacd", "maroon": "#800000", "mediumvioletred": "#c71585", "violet": "#ee82ee", "yellow green": "#9acd32", "coral": "#ff7f50", "lightgreen": "#90ee90", "cornsilk": "#fff8dc", "mediumblue": "#0000cd", "aliceblue": "#f0f8ff", "forestgreen": "#228b22", "aquamarine": "#7fffd4", "deepskyblue": "#00bfff", "lightslategray": "#778899", "darksalmon": "#e9967a", "crimson": "#dc143c", "sandybrown": "#f4a460", "lightpink": "#ffb6c1", "seashell": "#fff5ee", } ================================================ FILE: pkg/plugins/emoji.go ================================================ package plugins import "strings" /* This map is based by https://github.com/melborne/emot/blob/master/lib/emot/map.rb ================================================================================= Copyright (c) 2014 kyoendo MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var name2codes = map[string][]rune{ "+1": {0x1F44D}, "-1": {0x1F44E}, "-100": {0x1F4AF}, "-1234": {0x1F522}, "8ball": {0x1F3B1}, "a": {0x1F170}, "ab": {0x1F18E}, "abc": {0x1F524}, "abcd": {0x1F521}, "accept": {0x1F251}, "aerial_tramway": {0x1F6A1}, "airplane": {0x2708, 0xFE0F}, "alarm_clock": {0x23F0}, "alien": {0x1F47D}, "ambulance": {0x1F691}, "anchor": {0x2693, 0xFE0F}, "angel": {0x1F47C}, "anger": {0x1F4A2}, "angry": {0x1F620}, "anguished": {0x1F627}, "ant": {0x1F41C}, "apple": {0x1F34E}, "aquarius": {0x2652, 0xFE0F}, "aries": {0x2648, 0xFE0F}, "arrow_backward": {0x25C0, 0xFE0F}, "arrow_double_down": {0x23EC}, "arrow_double_up": {0x23EB}, "arrow_down": {0x2B07, 0xFE0F}, "arrow_down_small": {0x1F53D}, "arrow_forward": {0x25B6, 0xFE0F}, "arrow_heading_down": {0x2935, 0xFE0F}, "arrow_heading_up": {0x2934, 0xFE0F}, "arrow_left": {0x2B05, 0xFE0F}, "arrow_lower_left": {0x2199, 0xFE0F}, "arrow_lower_right": {0x2198, 0xFE0F}, "arrow_right": {0x27A1, 0xFE0F}, "arrow_right_hook": {0x21AA, 0xFE0F}, "arrow_up": {0x2B06, 0xFE0F}, "arrow_up_down": {0x2195, 0xFE0F}, "arrow_up_small": {0x1F53C}, "arrow_upper_left": {0x2196, 0xFE0F}, "arrow_upper_right": {0x2197, 0xFE0F}, "arrows_clockwise": {0x1F503}, "arrows_counterclockwise": {0x1F504}, "art": {0x1F3A8}, "articulated_lorry": {0x1F69B}, "astonished": {0x1F632}, "athletic_shoe": {0x1F45F}, "atm": {0x1F3E7}, "b": {0x1F171}, "baby": {0x1F476}, "baby_bottle": {0x1F37C}, "baby_chick": {0x1F424}, "baby_symbol": {0x1F6BC}, "back": {0x1F519}, "baggage_claim": {0x1F6C4}, "balloon": {0x1F388}, "ballot_box_with_check": {0x2611, 0xFE0F}, "bamboo": {0x1F38D}, "banana": {0x1F34C}, "bangbang": {0x203C, 0xFE0F}, "bank": {0x1F3E6}, "bar_chart": {0x1F4CA}, "barber": {0x1F488}, "baseball": {0x26BE, 0xFE0F}, "basketball": {0x1F3C0}, "bath": {0x1F6C0}, "bathtub": {0x1F6C1}, "battery": {0x1F50B}, "bear": {0x1F43B}, "bee": {0x1F41D}, "beer": {0x1F37A}, "beers": {0x1F37B}, "beetle": {0x1F41E}, "beginner": {0x1F530}, "bell": {0x1F514}, "bento": {0x1F371}, "bicyclist": {0x1F6B4}, "bike": {0x1F6B2}, "bikini": {0x1F459}, "bird": {0x1F426}, "birthday": {0x1F382}, "black_circle": {0x26AB, 0xFE0F}, "black_joker": {0x1F0CF}, "black_large_square": {0x2B1B, 0xFE0F}, "black_medium_small_square": {0x25FE, 0xFE0F}, "black_medium_square": {0x25FC, 0xFE0F}, "black_nib": {0x2712, 0xFE0F}, "black_small_square": {0x25AA, 0xFE0F}, "black_square_button": {0x1F532}, "blossom": {0x1F33C}, "blowfish": {0x1F421}, "blue_book": {0x1F4D8}, "blue_car": {0x1F699}, "blue_heart": {0x1F499}, "blush": {0x1F60A}, "boar": {0x1F417}, "boat": {0x26F5, 0xFE0F}, "bomb": {0x1F4A3}, "book": {0x1F4D6}, "bookmark": {0x1F516}, "bookmark_tabs": {0x1F4D1}, "books": {0x1F4DA}, "boom": {0x1F4A5}, "boot": {0x1F462}, "bouquet": {0x1F490}, "bow": {0x1F647}, "bowling": {0x1F3B3}, "boy": {0x1F466}, "bread": {0x1F35E}, "bride_with_veil": {0x1F470}, "bridge_at_night": {0x1F309}, "briefcase": {0x1F4BC}, "broken_heart": {0x1F494}, "bug": {0x1F41B}, "bulb": {0x1F4A1}, "bullettrain_front": {0x1F685}, "bullettrain_side": {0x1F684}, "bus": {0x1F68C}, "busstop": {0x1F68F}, "bust_in_silhouette": {0x1F464}, "busts_in_silhouette": {0x1F465}, "cactus": {0x1F335}, "cake": {0x1F370}, "calendar": {0x1F4C6}, "calling": {0x1F4F2}, "camel": {0x1F42B}, "camera": {0x1F4F7}, "cancer": {0x264B, 0xFE0F}, "candy": {0x1F36C}, "capital_abcd": {0x1F520}, "capricorn": {0x2651, 0xFE0F}, "car": {0x1F697}, "card_index": {0x1F4C7}, "carousel_horse": {0x1F3A0}, "cat": {0x1F431}, "cat2": {0x1F408}, "cd": {0x1F4BF}, "chart": {0x1F4B9}, "chart_with_downwards_trend": {0x1F4C9}, "chart_with_upwards_trend": {0x1F4C8}, "checkered_flag": {0x1F3C1}, "cherries": {0x1F352}, "cherry_blossom": {0x1F338}, "chestnut": {0x1F330}, "chicken": {0x1F414}, "children_crossing": {0x1F6B8}, "chocolate_bar": {0x1F36B}, "christmas_tree": {0x1F384}, "church": {0x26EA, 0xFE0F}, "cinema": {0x1F3A6}, "circus_tent": {0x1F3AA}, "city_sunrise": {0x1F307}, "city_sunset": {0x1F306}, "cl": {0x1F191}, "clap": {0x1F44F}, "clapper": {0x1F3AC}, "clipboard": {0x1F4CB}, "clock1": {0x1F550}, "clock10": {0x1F559}, "clock1030": {0x1F565}, "clock11": {0x1F55A}, "clock1130": {0x1F566}, "clock12": {0x1F55B}, "clock1230": {0x1F567}, "clock130": {0x1F55C}, "clock2": {0x1F551}, "clock230": {0x1F55D}, "clock3": {0x1F552}, "clock330": {0x1F55E}, "clock4": {0x1F553}, "clock430": {0x1F55F}, "clock5": {0x1F554}, "clock530": {0x1F560}, "clock6": {0x1F555}, "clock630": {0x1F561}, "clock7": {0x1F556}, "clock730": {0x1F562}, "clock8": {0x1F557}, "clock830": {0x1F563}, "clock9": {0x1F558}, "clock930": {0x1F564}, "closed_book": {0x1F4D5}, "closed_lock_with_key": {0x1F510}, "closed_umbrella": {0x1F302}, "cloud": {0x2601}, "clubs": {0x2663}, "cn": {0x1F1E8, 0x1F1F3}, "cocktail": {0x1F378}, "coffee": {0x2615}, "cold_sweat": {0x1F630}, "collision": {0x1F4A5}, "computer": {0x1F4BB}, "confetti_ball": {0x1F38A}, "confounded": {0x1F616}, "confused": {0x1F615}, "congratulations": {0x3297, 0xFE0F}, "construction": {0x1F6A7}, "construction_worker": {0x1F477}, "convenience_store": {0x1F3EA}, "cookie": {0x1F36A}, "cool": {0x1F192}, "cop": {0x1F46E}, "copyright": {0xA9}, "corn": {0x1F33D}, "couple": {0x1F46B}, "couple_with_heart": {0x1F491}, "couplekiss": {0x1F48F}, "cow": {0x1F42E}, "cow2": {0x1F404}, "credit_card": {0x1F4B3}, "crescent_moon": {0x1F319}, "crocodile": {0x1F40A}, "crossed_flags": {0x1F38C}, "crown": {0x1F451}, "cry": {0x1F622}, "crying_cat_face": {0x1F63F}, "crystal_ball": {0x1F52E}, "cupid": {0x1F498}, "curly_loop": {0x27B0}, "currency_exchange": {0x1F4B1}, "curry": {0x1F35B}, "custard": {0x1F36E}, "customs": {0x1F6C3}, "cyclone": {0x1F300}, "dancer": {0x1F483}, "dancers": {0x1F46F}, "dango": {0x1F361}, "dart": {0x1F3AF}, "dash": {0x1F4A8}, "date": {0x1F4C5}, "de": {0x1F1E9, 0x1F1EA}, "deciduous_tree": {0x1F333}, "department_store": {0x1F3EC}, "diamond_shape_with_a_dot_inside": {0x1F4A0}, "diamonds": {0x2666, 0xFE0F}, "disappointed": {0x1F61E}, "disappointed_relieved": {0x1F625}, "dizzy": {0x1F4AB}, "dizzy_face": {0x1F635}, "do_not_litter": {0x1F6AF}, "dog": {0x1F436}, "dog2": {0x1F415}, "dollar": {0x1F4B5}, "dolls": {0x1F38E}, "dolphin": {0x1F42C}, "door": {0x1F6AA}, "doughnut": {0x1F369}, "dragon": {0x1F409}, "dragon_face": {0x1F432}, "dress": {0x1F457}, "dromedary_camel": {0x1F42A}, "droplet": {0x1F4A7}, "dvd": {0x1F4C0}, "e-mail": {0x1F4E7}, "ear": {0x1F442}, "ear_of_rice": {0x1F33E}, "earth_africa": {0x1F30D}, "earth_americas": {0x1F30E}, "earth_asia": {0x1F30F}, "egg": {0x1F373}, "eggplant": {0x1F346}, "eight": {0x38, 0xFE0F, 0x20E3}, "eight_pointed_black_star": {0x2734, 0xFE0F}, "eight_spoked_asterisk": {0x2733, 0xFE0F}, "electric_plug": {0x1F50C}, "elephant": {0x1F418}, "email": {0x2709, 0xFE0F}, "end": {0x1F51A}, "envelope": {0x2709, 0xFE0F}, "envelope_with_arrow": {0x1F4E9}, "es": {0x1F1EA, 0x1F1F8}, "euro": {0x1F4B6}, "european_castle": {0x1F3F0}, "european_post_office": {0x1F3E4}, "evergreen_tree": {0x1F332}, "exclamation": {0x2757, 0xFE0F}, "expressionless": {0x1F611}, "eyeglasses": {0x1F453}, "eyes": {0x1F440}, "facepunch": {0x1F44A}, "factory": {0x1F3ED}, "fallen_leaf": {0x1F342}, "family": {0x1F46A}, "fast_forward": {0x23E9}, "fax": {0x1F4E0}, "fearful": {0x1F628}, "feet": {0x1F43E}, "ferris_wheel": {0x1F3A1}, "file_folder": {0x1F4C1}, "fire": {0x1F525}, "fire_engine": {0x1F692}, "fireworks": {0x1F386}, "first_quarter_moon": {0x1F313}, "first_quarter_moon_with_face": {0x1F31B}, "fish": {0x1F41F}, "fish_cake": {0x1F365}, "fishing_pole_and_fish": {0x1F3A3}, "fist": {0x270A}, "five": {0x35, 0xFE0F, 0x20E3}, "flags": {0x1F38F}, "flashlight": {0x1F526}, "flipper": {0x1F42C}, "floppy_disk": {0x1F4BE}, "flower_playing_cards": {0x1F3B4}, "flushed": {0x1F633}, "foggy": {0x1F301}, "football": {0x1F3C8}, "footprints": {0x1F463}, "fork_and_knife": {0x1F374}, "fountain": {0x26F2, 0xFE0F}, "four": {0x34, 0xFE0F, 0x20E3}, "four_leaf_clover": {0x1F340}, "fr": {0x1F1EB, 0x1F1F7}, "free": {0x1F193}, "fried_shrimp": {0x1F364}, "fries": {0x1F35F}, "frog": {0x1F438}, "frowning": {0x1F626}, "fuelpump": {0x26FD, 0xFE0F}, "full_moon": {0x1F315}, "full_moon_with_face": {0x1F31D}, "game_die": {0x1F3B2}, "gb": {0x1F1EC, 0x1F1E7}, "gem": {0x1F48E}, "gemini": {0x264A, 0xFE0F}, "ghost": {0x1F47B}, "gift": {0x1F381}, "gift_heart": {0x1F49D}, "girl": {0x1F467}, "globe_with_meridians": {0x1F310}, "goat": {0x1F410}, "golf": {0x26F3, 0xFE0F}, "grapes": {0x1F347}, "green_apple": {0x1F34F}, "green_book": {0x1F4D7}, "green_heart": {0x1F49A}, "grey_exclamation": {0x2755}, "grey_question": {0x2754}, "grimacing": {0x1F62C}, "grin": {0x1F601}, "grinning": {0x1F600}, "guardsman": {0x1F482}, "guitar": {0x1F3B8}, "gun": {0x1F52B}, "haircut": {0x1F487}, "hamburger": {0x1F354}, "hammer": {0x1F528}, "hamster": {0x1F439}, "hand": {0x270B}, "handbag": {0x1F45C}, "hankey": {0x1F4A9}, "hash": {0x23, 0xFE0F, 0x20E3}, "hatched_chick": {0x1F425}, "hatching_chick": {0x1F423}, "headphones": {0x1F3A7}, "hear_no_evil": {0x1F649}, "heart": {0x2764, 0xFE0F}, "heart_decoration": {0x1F49F}, "heart_eyes": {0x1F60D}, "heart_eyes_cat": {0x1F63B}, "heartbeat": {0x1F493}, "heartpulse": {0x1F497}, "hearts": {0x2665, 0xFE0F}, "heavy_check_mark": {0x2714, 0xFE0F}, "heavy_division_sign": {0x2797}, "heavy_dollar_sign": {0x1F4B2}, "heavy_exclamation_mark": {0x2757, 0xFE0F}, "heavy_minus_sign": {0x2796}, "heavy_multiplication_x": {0x2716, 0xFE0F}, "heavy_plus_sign": {0x2795}, "helicopter": {0x1F681}, "herb": {0x1F33F}, "hibiscus": {0x1F33A}, "high_brightness": {0x1F506}, "high_heel": {0x1F460}, "hocho": {0x1F52A}, "honey_pot": {0x1F36F}, "honeybee": {0x1F41D}, "horse": {0x1F434}, "horse_racing": {0x1F3C7}, "hospital": {0x1F3E5}, "hotel": {0x1F3E8}, "hotsprings": {0x2668, 0xFE0F}, "hourglass": {0x231B, 0xFE0F}, "hourglass_flowing_sand": {0x23F3}, "house": {0x1F3E0}, "house_with_garden": {0x1F3E1}, "hushed": {0x1F62F}, "ice_cream": {0x1F368}, "icecream": {0x1F366}, "id": {0x1F194}, "ideograph_advantage": {0x1F250}, "imp": {0x1F47F}, "inbox_tray": {0x1F4E5}, "incoming_envelope": {0x1F4E8}, "information_desk_person": {0x1F481}, "information_source": {0x2139, 0xFE0F}, "innocent": {0x1F607}, "interrobang": {0x2049, 0xFE0F}, "iphone": {0x1F4F1}, "it": {0x1F1EE, 0x1F1F9}, "izakaya_lantern": {0x1F3EE}, "jack_o_lantern": {0x1F383}, "japan": {0x1F5FE}, "japanese_castle": {0x1F3EF}, "japanese_goblin": {0x1F47A}, "japanese_ogre": {0x1F479}, "jeans": {0x1F456}, "joy": {0x1F602}, "joy_cat": {0x1F639}, "jp": {0x1F1EF, 0x1F1F5}, "key": {0x1F511}, "keycap_ten": {0x1F51F}, "kimono": {0x1F458}, "kiss": {0x1F48B}, "kissing": {0x1F617}, "kissing_cat": {0x1F63D}, "kissing_closed_eyes": {0x1F61A}, "kissing_heart": {0x1F618}, "kissing_smiling_eyes": {0x1F619}, "knife": {0x1F52A}, "koala": {0x1F428}, "koko": {0x1F201}, "kr": {0x1F1F0, 0x1F1F7}, "lantern": {0x1F3EE}, "large_blue_circle": {0x1F535}, "large_blue_diamond": {0x1F537}, "large_orange_diamond": {0x1F536}, "last_quarter_moon": {0x1F317}, "last_quarter_moon_with_face": {0x1F31C}, "laughing": {0x1F606}, "leaves": {0x1F343}, "ledger": {0x1F4D2}, "left_luggage": {0x1F6C5}, "left_right_arrow": {0x2194, 0xFE0F}, "leftwards_arrow_with_hook": {0x21A9, 0xFE0F}, "lemon": {0x1F34B}, "leo": {0x264C, 0xFE0F}, "leopard": {0x1F406}, "libra": {0x264E, 0xFE0F}, "light_rail": {0x1F688}, "link": {0x1F517}, "lips": {0x1F444}, "lipstick": {0x1F484}, "lock": {0x1F512}, "lock_with_ink_pen": {0x1F50F}, "lollipop": {0x1F36D}, "loop": {0x27BF}, "loud_sound": {0x1F50A}, "loudspeaker": {0x1F4E2}, "love_hotel": {0x1F3E9}, "love_letter": {0x1F48C}, "low_brightness": {0x1F505}, "m": {0x24C2, 0xFE0F}, "mag": {0x1F50D}, "mag_right": {0x1F50E}, "mahjong": {0x1F004, 0xFE0F}, "mailbox": {0x1F4EB}, "mailbox_closed": {0x1F4EA}, "mailbox_with_mail": {0x1F4EC}, "mailbox_with_no_mail": {0x1F4ED}, "man": {0x1F468}, "man_with_gua_pi_mao": {0x1F472}, "man_with_turban": {0x1F473}, "mans_shoe": {0x1F45E}, "maple_leaf": {0x1F341}, "mask": {0x1F637}, "massage": {0x1F486}, "meat_on_bone": {0x1F356}, "mega": {0x1F4E3}, "melon": {0x1F348}, "memo": {0x1F4DD}, "mens": {0x1F6B9}, "metro": {0x1F687}, "microphone": {0x1F3A4}, "microscope": {0x1F52C}, "milky_way": {0x1F30C}, "minibus": {0x1F690}, "minidisc": {0x1F4BD}, "mobile_phone_off": {0x1F4F4}, "money_with_wings": {0x1F4B8}, "moneybag": {0x1F4B0}, "monkey": {0x1F412}, "monkey_face": {0x1F435}, "monorail": {0x1F69D}, "moon": {0x1F314}, "mortar_board": {0x1F393}, "mount_fuji": {0x1F5FB}, "mountain_bicyclist": {0x1F6B5}, "mountain_cableway": {0x1F6A0}, "mountain_railway": {0x1F69E}, "mouse": {0x1F42D}, "mouse2": {0x1F401}, "movie_camera": {0x1F3A5}, "moyai": {0x1F5FF}, "muscle": {0x1F4AA}, "mushroom": {0x1F344}, "musical_keyboard": {0x1F3B9}, "musical_note": {0x1F3B5}, "musical_score": {0x1F3BC}, "mute": {0x1F507}, "nail_care": {0x1F485}, "name_badge": {0x1F4DB}, "necktie": {0x1F454}, "negative_squared_cross_mark": {0x274E}, "neutral_face": {0x1F610}, "new": {0x1F195}, "new_moon": {0x1F311}, "new_moon_with_face": {0x1F31A}, "newspaper": {0x1F4F0}, "ng": {0x1F196}, "night_with_stars": {0x1F303}, "nine": {0x39, 0xFE0F, 0x20E3}, "no_bell": {0x1F515}, "no_bicycles": {0x1F6B3}, "no_entry": {0x26D4, 0xFE0F}, "no_entry_sign": {0x1F6AB}, "no_good": {0x1F645}, "no_mobile_phones": {0x1F4F5}, "no_mouth": {0x1F636}, "no_pedestrians": {0x1F6B7}, "no_smoking": {0x1F6AD}, "non-potable_water": {0x1F6B1}, "nose": {0x1F443}, "notebook": {0x1F4D3}, "notebook_with_decorative_cover": {0x1F4D4}, "notes": {0x1F3B6}, "nut_and_bolt": {0x1F529}, "o": {0x2B55, 0xFE0F}, "o2": {0x1F17E}, "ocean": {0x1F30A}, "octopus": {0x1F419}, "oden": {0x1F362}, "office": {0x1F3E2}, "ok": {0x1F197}, "ok_hand": {0x1F44C}, "ok_woman": {0x1F646}, "older_man": {0x1F474}, "older_woman": {0x1F475}, "on": {0x1F51B}, "oncoming_automobile": {0x1F698}, "oncoming_bus": {0x1F68D}, "oncoming_police_car": {0x1F694}, "oncoming_taxi": {0x1F696}, "one": {0x31, 0xFE0F, 0x20E3}, "open_book": {0x1F4D6}, "open_file_folder": {0x1F4C2}, "open_hands": {0x1F450}, "open_mouth": {0x1F62E}, "ophiuchus": {0x26CE}, "orange_book": {0x1F4D9}, "outbox_tray": {0x1F4E4}, "ox": {0x1F402}, "package": {0x1F4E6}, "page_facing_up": {0x1F4C4}, "page_with_curl": {0x1F4C3}, "pager": {0x1F4DF}, "palm_tree": {0x1F334}, "panda_face": {0x1F43C}, "paperclip": {0x1F4CE}, "parking": {0x1F17F, 0xFE0F}, "part_alternation_mark": {0x303D, 0xFE0F}, "partly_sunny": {0x26C5, 0xFE0F}, "passport_control": {0x1F6C2}, "paw_prints": {0x1F43E}, "peach": {0x1F351}, "pear": {0x1F350}, "pencil": {0x1F4DD}, "pencil2": {0x270F, 0xFE0F}, "penguin": {0x1F427}, "pensive": {0x1F614}, "performing_arts": {0x1F3AD}, "persevere": {0x1F623}, "person_frowning": {0x1F64D}, "person_with_blond_hair": {0x1F471}, "person_with_pouting_face": {0x1F64E}, "phone": {0x260E, 0xFE0F}, "pig": {0x1F437}, "pig2": {0x1F416}, "pig_nose": {0x1F43D}, "pill": {0x1F48A}, "pineapple": {0x1F34D}, "pisces": {0x2653, 0xFE0F}, "pizza": {0x1F355}, "point_down": {0x1F447}, "point_left": {0x1F448}, "point_right": {0x1F449}, "point_up": {0x261D, 0xFE0F}, "point_up_2": {0x1F446}, "police_car": {0x1F693}, "poodle": {0x1F429}, "poop": {0x1F4A9}, "post_office": {0x1F3E3}, "postal_horn": {0x1F4EF}, "postbox": {0x1F4EE}, "potable_water": {0x1F6B0}, "pouch": {0x1F45D}, "poultry_leg": {0x1F357}, "pound": {0x1F4B7}, "pouting_cat": {0x1F63E}, "pray": {0x1F64F}, "princess": {0x1F478}, "punch": {0x1F44A}, "purple_heart": {0x1F49C}, "purse": {0x1F45B}, "pushpin": {0x1F4CC}, "put_litter_in_its_place": {0x1F6AE}, "question": {0x2753}, "rabbit": {0x1F430}, "rabbit2": {0x1F407}, "racehorse": {0x1F40E}, "radio": {0x1F4FB}, "radio_button": {0x1F518}, "rage": {0x1F621}, "railway_car": {0x1F683}, "rainbow": {0x1F308}, "raised_hand": {0x270B}, "raised_hands": {0x1F64C}, "raising_hand": {0x1F64B}, "ram": {0x1F40F}, "ramen": {0x1F35C}, "rat": {0x1F400}, "recycle": {0x267B, 0xFE0F}, "red_car": {0x1F697}, "red_circle": {0x1F534}, "registered": {0xAE}, "relaxed": {0x263A, 0xFE0F}, "relieved": {0x1F60C}, "repeat": {0x1F501}, "repeat_one": {0x1F502}, "restroom": {0x1F6BB}, "revolving_hearts": {0x1F49E}, "rewind": {0x23EA}, "ribbon": {0x1F380}, "rice": {0x1F35A}, "rice_ball": {0x1F359}, "rice_cracker": {0x1F358}, "rice_scene": {0x1F391}, "ring": {0x1F48D}, "rocket": {0x1F680}, "roller_coaster": {0x1F3A2}, "rooster": {0x1F413}, "rose": {0x1F339}, "rotating_light": {0x1F6A8}, "round_pushpin": {0x1F4CD}, "rowboat": {0x1F6A3}, "ru": {0x1F1F7, 0x1F1FA}, "rugby_football": {0x1F3C9}, "runner": {0x1F3C3}, "running": {0x1F3C3}, "running_shirt_with_sash": {0x1F3BD}, "sa": {0x1F202}, "sagittarius": {0x2650, 0xFE0F}, "sailboat": {0x26F5, 0xFE0F}, "sake": {0x1F376}, "sandal": {0x1F461}, "santa": {0x1F385}, "satellite": {0x1F4E1}, "satisfied": {0x1F606}, "saxophone": {0x1F3B7}, "school": {0x1F3EB}, "school_satchel": {0x1F392}, "scissors": {0x2702, 0xFE0F}, "scorpius": {0x264F, 0xFE0F}, "scream": {0x1F631}, "scream_cat": {0x1F640}, "scroll": {0x1F4DC}, "seat": {0x1F4BA}, "secret": {0x3299, 0xFE0F}, "see_no_evil": {0x1F648}, "seedling": {0x1F331}, "seven": {0x37, 0xFE0F, 0x20E3}, "shaved_ice": {0x1F367}, "sheep": {0x1F411}, "shell": {0x1F41A}, "ship": {0x1F6A2}, "shirt": {0x1F455}, "shit": {0x1F4A9}, "shoe": {0x1F45E}, "shower": {0x1F6BF}, "signal_strength": {0x1F4F6}, "six": {0x36, 0xFE0F, 0x20E3}, "six_pointed_star": {0x1F52F}, "ski": {0x1F3BF}, "skull": {0x1F480}, "sleeping": {0x1F634}, "sleepy": {0x1F62A}, "slot_machine": {0x1F3B0}, "small_blue_diamond": {0x1F539}, "small_orange_diamond": {0x1F538}, "small_red_triangle": {0x1F53A}, "small_red_triangle_down": {0x1F53B}, "smile": {0x1F604}, "smile_cat": {0x1F638}, "smiley": {0x1F603}, "smiley_cat": {0x1F63A}, "smiling_imp": {0x1F608}, "smirk": {0x1F60F}, "smirk_cat": {0x1F63C}, "smoking": {0x1F6AC}, "snail": {0x1F40C}, "snake": {0x1F40D}, "snowboarder": {0x1F3C2}, "snowflake": {0x2744, 0xFE0F}, "snowman": {0x26C4, 0xFE0F}, "sob": {0x1F62D}, "soccer": {0x26BD, 0xFE0F}, "soon": {0x1F51C}, "sos": {0x1F198}, "sound": {0x1F509}, "space_invader": {0x1F47E}, "spades": {0x2660, 0xFE0F}, "spaghetti": {0x1F35D}, "sparkle": {0x2747, 0xFE0F}, "sparkler": {0x1F387}, "sparkles": {0x2728}, "sparkling_heart": {0x1F496}, "speak_no_evil": {0x1F64A}, "speaker": {0x1F508}, "speech_balloon": {0x1F4AC}, "speedboat": {0x1F6A4}, "star": {0x2B50, 0xFE0F}, "star2": {0x1F31F}, "stars": {0x1F320}, "station": {0x1F689}, "statue_of_liberty": {0x1F5FD}, "steam_locomotive": {0x1F682}, "stew": {0x1F372}, "straight_ruler": {0x1F4CF}, "strawberry": {0x1F353}, "stuck_out_tongue": {0x1F61B}, "stuck_out_tongue_closed_eyes": {0x1F61D}, "stuck_out_tongue_winking_eye": {0x1F61C}, "sun_with_face": {0x1F31E}, "sunflower": {0x1F33B}, "sunglasses": {0x1F60E}, "sunny": {0x2600, 0xFE0F}, "sunrise": {0x1F305}, "sunrise_over_mountains": {0x1F304}, "surfer": {0x1F3C4}, "sushi": {0x1F363}, "suspension_railway": {0x1F69F}, "sweat": {0x1F613}, "sweat_drops": {0x1F4A6}, "sweat_smile": {0x1F605}, "sweet_potato": {0x1F360}, "swimmer": {0x1F3CA}, "symbols": {0x1F523}, "syringe": {0x1F489}, "tada": {0x1F389}, "tanabata_tree": {0x1F38B}, "tangerine": {0x1F34A}, "taurus": {0x2649, 0xFE0F}, "taxi": {0x1F695}, "tea": {0x1F375}, "telephone": {0x260E, 0xFE0F}, "telephone_receiver": {0x1F4DE}, "telescope": {0x1F52D}, "tennis": {0x1F3BE}, "tent": {0x26FA, 0xFE0F}, "thought_balloon": {0x1F4AD}, "three": {0x33, 0xFE0F, 0x20E3}, "thumbsdown": {0x1F44E}, "thumbsup": {0x1F44D}, "ticket": {0x1F3AB}, "tiger": {0x1F42F}, "tiger2": {0x1F405}, "tired_face": {0x1F62B}, "tm": {0x2122}, "toilet": {0x1F6BD}, "tokyo_tower": {0x1F5FC}, "tomato": {0x1F345}, "tongue": {0x1F445}, "top": {0x1F51D}, "tophat": {0x1F3A9}, "tractor": {0x1F69C}, "traffic_light": {0x1F6A5}, "train": {0x1F68B}, "train2": {0x1F686}, "tram": {0x1F68A}, "triangular_flag_on_post": {0x1F6A9}, "triangular_ruler": {0x1F4D0}, "trident": {0x1F531}, "triumph": {0x1F624}, "trolleybus": {0x1F68E}, "trophy": {0x1F3C6}, "tropical_drink": {0x1F379}, "tropical_fish": {0x1F420}, "truck": {0x1F69A}, "trumpet": {0x1F3BA}, "tshirt": {0x1F455}, "tulip": {0x1F337}, "turtle": {0x1F422}, "tv": {0x1F4FA}, "twisted_rightwards_arrows": {0x1F500}, "two": {0x32, 0xFE0F, 0x20E3}, "two_hearts": {0x1F495}, "two_men_holding_hands": {0x1F46C}, "two_women_holding_hands": {0x1F46D}, "u5272": {0x1F239}, "u5408": {0x1F234}, "u55b6": {0x1F23A}, "u6307": {0x1F22F, 0xFE0F}, "u6708": {0x1F237}, "u6709": {0x1F236}, "u6e80": {0x1F235}, "u7121": {0x1F21A, 0xFE0F}, "u7533": {0x1F238}, "u7981": {0x1F232}, "u7a7a": {0x1F233}, "uk": {0x1F1EC, 0x1F1E7}, "umbrella": {0x2614, 0xFE0F}, "unamused": {0x1F612}, "underage": {0x1F51E}, "unlock": {0x1F513}, "up": {0x1F199}, "us": {0x1F1FA, 0x1F1F8}, "v": {0x270C, 0xFE0F}, "vertical_traffic_light": {0x1F6A6}, "vhs": {0x1F4FC}, "vibration_mode": {0x1F4F3}, "video_camera": {0x1F4F9}, "video_game": {0x1F3AE}, "violin": {0x1F3BB}, "virgo": {0x264D, 0xFE0F}, "volcano": {0x1F30B}, "vs": {0x1F19A}, "walking": {0x1F6B6}, "waning_crescent_moon": {0x1F318}, "waning_gibbous_moon": {0x1F316}, "warning": {0x26A0, 0xFE0F}, "watch": {0x231A, 0xFE0F}, "water_buffalo": {0x1F403}, "watermelon": {0x1F349}, "wave": {0x1F44B}, "wavy_dash": {0x3030}, "waxing_crescent_moon": {0x1F312}, "waxing_gibbous_moon": {0x1F314}, "wc": {0x1F6BE}, "weary": {0x1F629}, "wedding": {0x1F492}, "whale": {0x1F433}, "whale2": {0x1F40B}, "wheelchair": {0x267F, 0xFE0F}, "white_check_mark": {0x2705}, "white_circle": {0x26AA, 0xFE0F}, "white_flower": {0x1F4AE}, "white_large_square": {0x2B1C, 0xFE0F}, "white_medium_small_square": {0x25FD, 0xFE0F}, "white_medium_square": {0x25FB, 0xFE0F}, "white_small_square": {0x25AB, 0xFE0F}, "white_square_button": {0x1F533}, "wind_chime": {0x1F390}, "wine_glass": {0x1F377}, "wink": {0x1F609}, "wolf": {0x1F43A}, "woman": {0x1F469}, "womans_clothes": {0x1F45A}, "womans_hat": {0x1F452}, "womens": {0x1F6BA}, "worried": {0x1F61F}, "wrench": {0x1F527}, "x": {0x274C}, "yellow_heart": {0x1F49B}, "yen": {0x1F4B4}, "yum": {0x1F60B}, "zap": {0x26A1, 0xFE0F}, "zero": {0x30, 0xFE0F, 0x20E3}, "zzz": {0x1F4A4}, } // Emojize converts emoji names (e.g., :pizza:) to emoji characters. func Emojize(s string) string { for name, emoji := range name2codes { marker := ":" + name + ":" s = strings.ReplaceAll(s, marker, string(emoji)) } return s } ================================================ FILE: pkg/plugins/go.mod ================================================ module github.com/matryer/xbar/pkg/plugins go 1.15 require ( github.com/leaanthony/go-ansi-parser v1.2.0 github.com/matryer/is v1.4.0 github.com/matryer/xbar/pkg/metadata v0.0.0-00010101000000-000000000000 github.com/pkg/errors v0.9.1 ) replace github.com/matryer/xbar/pkg/metadata => ../metadata ================================================ FILE: pkg/plugins/go.sum ================================================ github.com/leaanthony/go-ansi-parser v1.2.0 h1:CcBhxqkxPATj7Lgdp9EgPUGv2o3FGkg3A5eN7ermsbM= github.com/leaanthony/go-ansi-parser v1.2.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= ================================================ FILE: pkg/plugins/install.go ================================================ package plugins import ( "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "path" "path/filepath" "strings" "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" ) // example: https://xbarapp.com/docs/plugins/Finance/currency-tracker.1h.py.json // Installer installs plugins from the xbar plugin API to the user's local xbar // installation. type Installer struct { Client *http.Client PluginDir string } // Uninstall removes an installed plugin. func (i Installer) Uninstall(installedPluginPath string) error { err := os.RemoveAll(filepath.Join(i.PluginDir, installedPluginPath)) if err != nil { return err } return nil } // Install installs the plugin whose metadata and content is located at // pluginPath. func (i Installer) Install(pluginPath *url.URL) (string, error) { err := os.MkdirAll(i.PluginDir, 0777) if err != nil { return "", errors.Wrap(err, "make plugin directory") } plugin, err := i.fetchPlugin(pluginPath) if err != nil { return "", errors.Wrapf(err, "fetchPlugin: %s", pluginPath) } dest, err := i.getInstalledPluginName(plugin) if err != nil { return "", errors.Wrap(err, "getInstalledPluginName") } if err := i.writePluginFiles(dest, plugin); err != nil { return "", errors.Wrap(err, "writePluginFiles") } installedPluginPath, err := filepath.Rel(i.PluginDir, dest) if err != nil { return "", errors.Wrap(err, "filepath.Rel") } return installedPluginPath, nil } // fetchPlugin fetches the plugin metadata and file contents from the xbar website. func (i Installer) fetchPlugin(pluginPath *url.URL) (metadata.Plugin, error) { resp, err := i.Client.Get(pluginPath.String()) if err != nil { return metadata.Plugin{}, err } if resp.StatusCode != http.StatusOK { return metadata.Plugin{}, errors.Errorf("error fetching plugin %s: %s", pluginPath, resp.Status) } defer resp.Body.Close() var responseBody struct { Plugin metadata.Plugin `json:"plugin"` } if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil { return metadata.Plugin{}, errors.Wrapf(err, "error decoding plugin %s", pluginPath) } return responseBody.Plugin, nil } // getInstalledPluginName builds a name for the file or folder that will be // created in the plugin installation directory. Since a plugin can be installed // multiple times, each installed plugin will be given a sequence number to // ensure unique naming in the filesystem. func (i Installer) getInstalledPluginName(plugin metadata.Plugin) (string, error) { var ( count int candidatePath string err error ) for err == nil { count++ candidateBaseName := fmt.Sprintf("%03d-%s", count, plugin.Filename) candidatePath = filepath.Join(i.PluginDir, candidateBaseName) _, err = os.Stat(candidatePath) } if !os.IsNotExist(err) { return "", err } return candidatePath, nil } // writePluginFiles iterates through the files defined for the plugin, and // writes them to the plugin installation directory, and sets the entry point // to be executable. func (i Installer) writePluginFiles(dstPath string, plugin metadata.Plugin) error { if len(plugin.Files) == 0 { return errors.New("no plugin files") } if len(plugin.Files) > 1 { return errors.Errorf("only one plugin file supported: found %d.", len(plugin.Files)) } for _, f := range plugin.Files { pluginFile := dstPath dir := path.Dir(pluginFile) if err := os.MkdirAll(dir, 0777); err != nil { return errors.Wrapf(err, "create directory %s for plugin", dir) } writer, err := os.Create(pluginFile) if err != nil { return errors.Wrapf(err, "create plugin file %s", f.Path) } defer writer.Close() reader := strings.NewReader(f.Content) if _, err := io.Copy(writer, reader); err != nil { return errors.Wrapf(err, "write plugin file %s", f.Path) } // If the Filename property of the current file matches the Filename // property of the plugin, this is the entry point, so set it to be // executable. if f.Filename != plugin.Filename { continue } if err := os.Chmod(pluginFile, 0755); err != nil { return errors.Wrap(err, "set executable permission on plugin entry point") } // write the default variables plugin, err := metadata.Parse(metadata.DebugfNoop, pluginFile, f.Content) if err != nil { log.Println("install plugin: unable to parse metadata:", err) } if len(plugin.Vars) > 0 { defaultVars := make(map[string]interface{}) for _, pluginVar := range plugin.Vars { defaultVars[pluginVar.Name] = pluginVar.DefaultValue() } err := SaveVariableValues(i.PluginDir, pluginFile, defaultVars) if err != nil { return errors.Wrap(err, "write default variables") } } } return nil } ================================================ FILE: pkg/plugins/install_test.go ================================================ package plugins import ( "net/http" "net/http/httptest" "net/url" "os" "path/filepath" "testing" "github.com/matryer/is" "github.com/matryer/xbar/pkg/metadata" ) func TestInstall(t *testing.T) { var ( apiPath = filepath.Join("testdata", "stub-api") srv = httptest.NewServer(http.FileServer(http.Dir(apiPath))) ) t.Cleanup(srv.Close) t.Run("single-file plugin installation", func(t *testing.T) { var ( is = is.New(t) pluginDir = filepath.Join("testdata", "single_file_install_tests") ) t.Cleanup(func() { err := os.RemoveAll(pluginDir) is.NoErr(err) }) const plugin = "currency-tracker.1h.py" installer := Installer{ Client: srv.Client(), PluginDir: pluginDir, } pluginMetadataAPIPath, err := url.Parse(srv.URL + "/" + plugin + ".json") is.NoErr(err) installedPluginPath, err := installer.Install(pluginMetadataAPIPath) is.NoErr(err) is.Equal(installedPluginPath, "001-currency-tracker.1h.py") expected := filepath.Join(pluginDir, "001-"+plugin) fi, err := os.Stat(expected) is.True(os.IsNotExist(err) == false) is.Equal(fi.Mode(), os.FileMode(0755)) }) // note: this feature isn't yet implemented // t.Run("folder plugin installation", func(t *testing.T) { // var ( // is = is.New(t) // pluginDir = filepath.Join("testdata", "folder_install_tests") // ) // t.Cleanup(func() { // err := os.RemoveAll(pluginDir) // is.NoErr(err) // }) // const plugin = "folder-based.1h.sh" // installer := Installer{ // Client: srv.Client(), // PluginDir: pluginDir, // } // pluginMetadataAPIPath, err := url.Parse(srv.URL + "/" + plugin + ".json") // is.NoErr(err) // installedPluginPath, err := installer.Install(pluginMetadataAPIPath) // is.NoErr(err) // is.Equal(installedPluginPath, "abc") // // Ensure that the entry point is set executable and the rest are not. // // This assumes umask 022 is set, since xbar sets the file mode for the // // entry point to 0777. The mode for non-entry point files is not // // explicitly set. // var ( // installedAt = filepath.Join(pluginDir, "001-"+plugin) // wantFiles = map[string]os.FileMode{ // "folder-based.1h.sh": 0755, // "data-1.txt": 0644, // "data-2.txt": 0644, // } // ) // gotFiles, err := ioutil.ReadDir(installedAt) // is.NoErr(err) // for _, file := range gotFiles { // mode, ok := wantFiles[file.Name()] // is.True(ok) // Unexpected file in installation directory. // is.Equal(file.Mode(), mode) // } // }) t.Run("multiple installations of single plugin", func(t *testing.T) { var ( is = is.New(t) pluginDir = filepath.Join("testdata", "multi_install_tests") ) t.Cleanup(func() { err := os.RemoveAll(pluginDir) is.NoErr(err) }) const plugin = "currency-tracker.1h.py" // Fake an existing installation of the plugin. We need to create the // plugin directory ourselves here in order to fake the // already-installed plugin, even though Installer#Install would do this // for us normally. Directory creation is tested in the other test cases // here, so it's fine to bypass it here. err := os.MkdirAll(pluginDir, 0777) is.NoErr(err) _, err = os.Create(filepath.Join(pluginDir, "001-"+plugin)) is.NoErr(err) installer := Installer{ Client: srv.Client(), PluginDir: pluginDir, } pluginMetadataAPIPath, err := url.Parse(srv.URL + "/" + plugin + ".json") is.NoErr(err) installedPluginPath, err := installer.Install(pluginMetadataAPIPath) is.NoErr(err) is.Equal(installedPluginPath, "002-currency-tracker.1h.py") expected := filepath.Join(pluginDir, "002-"+plugin) fi, err := os.Stat(expected) is.True(os.IsNotExist(err) == false) is.Equal(fi.Mode(), os.FileMode(0755)) }) } func TestGetInstalledPluginName(t *testing.T) { const installDir = "testdata/plugins" t.Run("not already installed", func(t *testing.T) { is := is.New(t) installer := Installer{PluginDir: "testdata/plugins"} plugin := metadata.Plugin{ Filename: "does-not-exist.1s.sh", } name, err := installer.getInstalledPluginName(plugin) is.NoErr(err) is.Equal(name, filepath.Join(installDir, "001-does-not-exist.1s.sh")) }) t.Run("already installed", func(t *testing.T) { is := is.New(t) installer := Installer{PluginDir: "testdata/plugins"} plugin := metadata.Plugin{ Filename: "multiple.1s.sh", } name, err := installer.getInstalledPluginName(plugin) is.NoErr(err) is.Equal(name, filepath.Join(installDir, "003-multiple.1s.sh")) }) } // func TestRewritePluginFileDest(t *testing.T) { // t.Run("single file plugin", func(t *testing.T) { // is := is.New(t) // originalFile := metadata.File{Path: "Category/plugin-name.1s.py"} // const want = "001-plugin-name.1s.py" // got, err := rewritePluginFileDest(want, originalFile) // is.NoErr(err) // is.Equal(got, want) // }) // t.Run("folder-based plugin", func(t *testing.T) { // is := is.New(t) // originalFile := metadata.File{Path: "Category/plugin-name.1s.py/plugin-name.1s.py"} // const installDir = "001-plugin-name.1s.py" // got, err := rewritePluginFileDest(installDir, originalFile) // is.NoErr(err) // is.Equal(got, filepath.Join(installDir, "plugin-name.1s.py")) // }) // t.Run("invalid plugin filename", func(t *testing.T) { // is := is.New(t) // originalFile := metadata.File{Path: "Category/"} // _, err := rewritePluginFileDest("does-not-matter-here", originalFile) // is.True(err != nil) // }) // } // const simplePlugin = `#!/bin/bash // echo "Hello, xbar." // ` ================================================ FILE: pkg/plugins/installed_plugins.go ================================================ package plugins import ( "fmt" "io/ioutil" "os" "path/filepath" "sort" "strings" "github.com/pkg/errors" ) // InstalledPlugin is a plugin that has been installed. type InstalledPlugin struct { Counter int `json:"counter"` Name string `json:"name"` Path string `json:"path"` Enabled bool `json:"enabled"` RefreshInterval RefreshInterval `json:"refreshInterval"` } // disabledPluginExtension is the extension that is toggled at the // end of plugins to enable/disable them. const disabledPluginExtension = ".off" // IsPluginEnabled gets whether the plugin is disabled or not. func IsPluginEnabled(pluginPath string) bool { return !strings.HasSuffix(pluginPath, disabledPluginExtension) } // SetEnabled sets a plugin to enabled or disabled state, depending on the value // of the enabled parameter. func SetEnabled(pluginDirectory, installedPluginPath string, enabled bool) (string, error) { fullPluginPath := filepath.Join(pluginDirectory, installedPluginPath) // Enable a disabled plugin. if enabled && !IsPluginEnabled(installedPluginPath) { newFullPluginPath := strings.TrimSuffix(fullPluginPath, disabledPluginExtension) newInstalledPluginPath := strings.TrimSuffix(installedPluginPath, disabledPluginExtension) err := os.Rename(fullPluginPath, newFullPluginPath) return newInstalledPluginPath, err } // Disable an enabled plugin. if !enabled && IsPluginEnabled(installedPluginPath) { newFullPluginPath := fullPluginPath + disabledPluginExtension newInstalledPluginPath := installedPluginPath + disabledPluginExtension err := os.Rename(fullPluginPath, newFullPluginPath) return newInstalledPluginPath, err } return installedPluginPath, nil } // GetInstalledPlugins gets the installed fplugins from the pluginDirectory. func GetInstalledPlugins(pluginDirectory string) ([]InstalledPlugin, error) { files, err := ioutil.ReadDir(pluginDirectory) if err != nil { if os.IsNotExist(err) { // no plugins directory - so no installed plugins // but not an error return nil, nil } return nil, errors.Wrap(err, "ReadDir") } var installedPlugins []InstalledPlugin for _, file := range files { if file.IsDir() { continue } if strings.HasPrefix(file.Name(), ".") { continue } if strings.HasSuffix(file.Name(), variableJSONFileExt) { // ignore variable payload files continue } enabled := !strings.HasSuffix(file.Name(), disabledPluginExtension) name := file.Name() installedPlugin := InstalledPlugin{ Name: name, Path: file.Name(), Enabled: enabled, } _, _ = fmt.Sscanf(file.Name(), "%d-%v", &installedPlugin.Counter, &installedPlugin.Name) if !enabled { installedPlugin.Name = strings.TrimSuffix(installedPlugin.Name, disabledPluginExtension) } if installedPlugin.Counter == 0 { installedPlugin.Counter = 1 } installedPlugins = append(installedPlugins, installedPlugin) } // sort them by name sort.Slice(installedPlugins, func(i, j int) bool { return installedPlugins[i].Name < installedPlugins[j].Name }) return installedPlugins, nil } ================================================ FILE: pkg/plugins/installed_plugins_test.go ================================================ package plugins import ( "os" "path/filepath" "testing" "github.com/matryer/is" ) func TestGetInstalledPlugins(t *testing.T) { is := is.New(t) installedPlugins, err := GetInstalledPlugins(filepath.Join("testdata", "plugins")) is.NoErr(err) is.Equal(len(installedPlugins), 9) is.Equal(installedPlugins[0].Name, "expanded.1m.sh") is.Equal(installedPlugins[0].Counter, 1) is.Equal(installedPlugins[0].Path, "expanded.1m.sh") is.Equal(installedPlugins[0].Enabled, true) is.Equal(installedPlugins[1].Name, "expanded.1m.sh") is.Equal(installedPlugins[1].Counter, 1) is.Equal(installedPlugins[1].Path, "expanded.1m.sh.off") is.Equal(installedPlugins[1].Enabled, false) is.Equal(installedPlugins[2].Name, "expanded.1s.sh") is.Equal(installedPlugins[2].Counter, 1) is.Equal(installedPlugins[2].Path, "expanded.1s.sh") is.Equal(installedPlugins[3].Name, "multiple.1s.sh") // Name should exclude counter is.Equal(installedPlugins[3].Counter, 1) // Counter is.Equal(installedPlugins[3].Path, "001-multiple.1s.sh") // Path should include counter is.Equal(installedPlugins[4].Name, "multiple.1s.sh") // Name should exclude counter is.Equal(installedPlugins[4].Counter, 2) // Counter is.Equal(installedPlugins[4].Path, "002-multiple.1s.sh") // Path should include counter } func TestIsPluginEnabled(t *testing.T) { is := is.New(t) is.Equal(IsPluginEnabled("nope.sh"+disabledPluginExtension), false) is.Equal(IsPluginEnabled("yep.sh"), true) } func TestSetEnabled(t *testing.T) { const ( mainTestdir = "testdata/set_enabled_test" installedPluginPath = "set-enabled-plugin.1h.sh" ) t.Cleanup(func() { is := is.New(t) err := os.RemoveAll(mainTestdir) is.NoErr(err) }) scaffold := func(t *testing.T, testcase string) string { is := is.New(t) testdir := filepath.Join(mainTestdir, testcase) err := os.MkdirAll(testdir, 0777) is.NoErr(err) t.Cleanup(func() { err := os.RemoveAll(testdir) is.NoErr(err) }) return testdir } t.Run("enabled to disabled", func(t *testing.T) { var ( is = is.New(t) testdir = scaffold(t, "enable") pluginPath = filepath.Join(testdir, installedPluginPath) ) _, err := os.Create(pluginPath) is.NoErr(err) newpath, err := SetEnabled(testdir, installedPluginPath, false) is.NoErr(err) is.Equal(newpath, "set-enabled-plugin.1h.sh.off") is.Equal(IsPluginEnabled(newpath), false) _, err = os.Stat(pluginPath + disabledPluginExtension) is.NoErr(err) }) t.Run("disabled to enabled", func(t *testing.T) { var ( is = is.New(t) testdir = scaffold(t, "disable") disabledPluginFilename = installedPluginPath + disabledPluginExtension pluginPath = filepath.Join(testdir, installedPluginPath+disabledPluginExtension) ) _, err := os.Create(pluginPath) is.NoErr(err) newpath, err := SetEnabled(testdir, disabledPluginFilename, false) is.NoErr(err) is.Equal(newpath, "set-enabled-plugin.1h.sh.off") is.Equal(IsPluginEnabled(newpath), false) _, err = os.Stat(pluginPath) is.NoErr(err) }) t.Run("enable already enabled: no op", func(t *testing.T) { var ( is = is.New(t) testdir = scaffold(t, "enable-noop") pluginPath = filepath.Join(testdir, installedPluginPath) ) _, err := os.Create(pluginPath) is.NoErr(err) newpath, err := SetEnabled(testdir, installedPluginPath, true) is.NoErr(err) is.Equal(newpath, "set-enabled-plugin.1h.sh") is.Equal(IsPluginEnabled(newpath), true) _, err = os.Stat(pluginPath) is.NoErr(err) _, err = os.Stat(pluginPath + disabledPluginExtension) is.True(os.IsNotExist(err)) }) t.Run("disable aldready disabled: no op", func(t *testing.T) { var ( is = is.New(t) testdir = scaffold(t, "disable-noop") pluginPath = filepath.Join(testdir, installedPluginPath) disabledPluginFilename = installedPluginPath + disabledPluginExtension ) _, err := os.Create(filepath.Join(testdir, disabledPluginFilename)) is.NoErr(err) newpath, err := SetEnabled(testdir, disabledPluginFilename, false) is.NoErr(err) is.Equal(newpath, "set-enabled-plugin.1h.sh.off") is.Equal(IsPluginEnabled(newpath), false) _, err = os.Stat(filepath.Join(testdir, disabledPluginFilename)) is.NoErr(err) _, err = os.Stat(pluginPath) is.True(os.IsNotExist(err)) }) } ================================================ FILE: pkg/plugins/item_params.go ================================================ package plugins import ( "strconv" "strings" "github.com/leaanthony/go-ansi-parser" "github.com/pkg/errors" ) // Items hold the menu items for a plugin. type Items struct { // CycleItems are the items that appear in the menu bar. CycleItems []*Item `json:"cycleItems"` // ExpandedItems are the items that appear when the menu // is open. ExpandedItems []*Item `json:"expandedItems"` } // Item is a single menu item. type Item struct { // Plugin is the Plugin that this item belong to. Plugin *Plugin `json:"-"` // Text is the content of the menu item. Text string `json:"text"` // Params are the parameters associated with this Item. Params ItemParams `json:"params"` // Items are a collection of items that appear as a // sub menu. Items []*Item `json:"items"` // Alternate is an Item to be used in place of this when // the option key is held by the user. // These are added to the previous line when printed with the alternate=true // parameter. Alternate *Item `json:"alternate"` } // DisplayText gets the text that should be displayed for // this item. // It takes into account the Length parameter. // @matryer - is there a better way to handle these errors? func (i Item) DisplayText() string { var err error displayText := i.Text if i.Params.ANSI { displayText, err = strconv.Unquote(`"` + displayText + `"`) if err != nil { displayText = i.Text } } return truncate(displayText, i.Params.Length) } // ItemParams represent parameters for an Item. type ItemParams struct { // Disabled indicates that this Item should appear // disabled. Disabled bool `json:"disabled"` // Separator indicates that this Item is a separator. Separator bool `json:"separator"` // Href is the URL to open when the item is clicked. Href string `json:"href"` // Key is the accelerator (shortcut) for this item. Key string `json:"key"` // Color is the color of the text. Color string `json:"color"` // Font is the font for the text. Font string `json:"font"` // Size is the font size. Size int `json:"size"` // Shell is a shell script to run when the item is clicked. Shell string `json:"shell"` // ShellParams are the arguments to pass to the shell executable. ShellParams []string `json:"shell_params"` // Terminal indicates whether to run the shell command in a terminal or not. // Default is false. Terminal bool `json:"terminal"` // // appleScriptTemplate3 is the template for the AppleScript // // to run this action in a terminal. // AppleScriptTemplate string `json:"-"` // Refresh indicates whether clicking this item will cause the plugin // to refresh or not. Refresh bool `json:"refresh"` // Dropdown indicates whether the item appears in the dropdown // or not. Dropdown bool `json:"dropdown"` // Length is the maximum length of the item before the text will // be truncated. Length int `json:"length"` // Trim indicates whether to trim whitespace from the text or not. Trim bool `json:"trim"` // Alternate indicates that this item is an alternative for the // previous item. It will be shown when the option key is depressed. Alternate bool `json:"alternate"` // TemplateImage is the te`mplate image for this item. TemplateImage string `json:"template_image"` // Image is the item for this item. Image string `json:"image"` // Emojize indicates whether to process emoji strings (like :mushroom:) // or not. Emojize bool `json:"emojize"` // ANSI indicates whether to parsing ANSI codes. ANSI bool `json:"ansi"` } // parseParams parses the parameters from a single line. // The string without parameters is returned, along with the // typed ItemParams. func parseParams(s string) (string, ItemParams, error) { params := defaultParams pipeIndex := strings.Index(s, "|") if pipeIndex < 0 { // no params return s, params, nil } text := s[:pipeIndex] paramStr := s[pipeIndex+1:] if err := parseParamStr(¶ms, paramStr); err != nil { return text, params, err } return text, params, nil } // parseParamStr parses the parameter string, updating params. func parseParamStr(params *ItemParams, s string) error { var splitStr, endStr string for { s = strings.TrimSpace(s) if len(s) == 0 { return nil } splitStr = `=` endStr = ` ` i := strings.Index(s, splitStr) if i < 0 { return errors.New("malformed parameters: missing equals") } if len(s) > i+1 && (s[i+1] == '"' || s[i+1] == '\'') { // quotes endStr = string(s[i+1]) splitStr = "=" + endStr } offset := i + len(splitStr) key := s[:i] if key[0] == '|' { key = key[1:] key = strings.TrimSpace(key) } valuePart := s[offset:] end := strings.Index(valuePart, endStr) if end < 0 { end = strings.Index(valuePart, "|") } if end < 0 { end = len(s) } else { end += offset } value := s[i+len(splitStr) : end] if err := params.setValueByKey(key, value); err != nil { return err } if end+1 > len(s) { return nil } s = s[end+1:] } } // defaultParams are the default ItemParams. var defaultParams = ItemParams{ Dropdown: true, Trim: true, Emojize: true, ANSI: true, } func (p *ItemParams) setValueByKey(key, value string) error { switch key { case "disabled": var err error p.Disabled, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "key": p.Key = value case "href": p.Href = value case "color": var err error p.Color, err = parseColor(value) if err != nil { return errors.Wrap(err, key) } case "font": p.Font = value case "size": val, err := parseInt(value) if err != nil { return errors.Wrap(err, key) } p.Size = val case "shell", "bash": p.Shell = value case "templateImage": p.TemplateImage = value case "image": p.Image = value case "terminal": var err error p.Terminal, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "refresh": var err error p.Refresh, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "dropdown": var err error p.Dropdown, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "length": val, err := parseInt(value) if err != nil { return errors.Wrap(err, key) } p.Length = val case "trim": var err error p.Trim, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "alternate": var err error p.Alternate, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "emojize": var err error p.Emojize, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } case "ansi": var err error p.ANSI, err = parseBool(value) if err != nil { return errors.Wrap(err, key) } default: if strings.HasPrefix(key, "param") { paramIndex, err := strconv.Atoi(key[5:]) if err != nil { return errors.Errorf("bad parameter: %s (should be paramN)", key) } for len(p.ShellParams) < paramIndex { // ensure the slice is big enough p.ShellParams = append(p.ShellParams, "") } p.ShellParams[paramIndex-1] = value return nil } return errors.Errorf("unknown parameter: %s", key) } return nil } // parseBool parses a boolean from a string (either `true` or `false`), // returning a nice error if it fails. func parseBool(s string) (bool, error) { b, err := strconv.ParseBool(s) if err != nil { return false, errors.Errorf(`expected "true" or "false", not "%s"`, s) } return b, nil } // parseColor parses the color value given. // Valid values: named color, #RGB, #RGBA, #RRGGBB, #RRGGBBAA. // Returns a nice error if it fails. func parseColor(s string) (string, error) { if len(s) == 0 { return "", errors.Errorf("expected hex string or named color") // Probably an error? } s = strings.ToLower(s) if s[0] == '#' { // Matches #RGB #RGBA #RRGGBB #RRGGBBAA if !colorRegexp.Match([]byte(s)) { return "", errors.Errorf(`invalid hex format "%s"`, s) } return s, nil } hexValue, valid := namedColors[s] if !valid { return "", errors.Errorf(`invalid named color "%s"`, s) } return hexValue, nil } // parseInt parses an int from a string, returning a nice // error if it fails. func parseInt(s string) (int, error) { i, err := strconv.ParseInt(s, 10, 64) if err != nil { return 0, errors.Errorf(`expected an int, not "%s"`, s) } return int(i), nil } // truncate shrinks a string if it's too long. func truncate(s string, max int) string { truncated, err := ansi.Truncate(s, max-1) if err != nil { // If, for some reason, there's an error when // parsing, do what we used to do runes := []rune(s) if max > 0 && len(runes) > max { s = string(runes[:max-1]) + "…" } return s } length, _ := ansi.Length(truncated) if length == max-1 { truncated += "…" } return truncated } ================================================ FILE: pkg/plugins/item_params_test.go ================================================ package plugins import ( "context" "strings" "testing" "github.com/matryer/is" ) func TestParseParamStr(t *testing.T) { is := is.New(t) params := defaultParams // pipe separated err := parseParamStr(¶ms, strings.Join([]string{ `href="https://xbarapp.com"`, `color=red`, `font="MyFont"`, `size=12`, `shell="script.sh"`, `terminal=false`, `refresh=true`, `dropdown=false`, `length=10`, `trim=false`, `alternate=true`, `templateImage="abc123"`, `image='abc123'`, `emojize=false`, `ansi=false`, `param1=parameterValue1`, `param2=parameterValue2`, `param3=parameterValue3`, `param4=parameterValue4`, `param5=parameterValue5`, `param6=parameterValue6`, `param7=parameterValue7`, `param8=parameterValue8`, `param9=parameterValue9`, `param10=parameterValue10`, `key=shift+g`, `disabled=true`, }, " | ")) is.NoErr(err) is.Equal(params.Href, "https://xbarapp.com") is.Equal(params.Color, "#ff0000") is.Equal(params.Font, "MyFont") is.Equal(params.Shell, "script.sh") is.Equal(params.Terminal, false) is.Equal(params.Refresh, true) is.Equal(params.Dropdown, false) is.Equal(params.Length, 10) is.Equal(params.Trim, false) is.Equal(params.Alternate, true) is.Equal(params.TemplateImage, "abc123") is.Equal(params.Image, "abc123") is.Equal(params.Emojize, false) is.Equal(params.ANSI, false) is.Equal(params.Key, "shift+g") is.Equal(params.Disabled, true) is.Equal(len(params.ShellParams), 10) is.Equal(params.ShellParams[0], "parameterValue1") is.Equal(params.ShellParams[1], "parameterValue2") is.Equal(params.ShellParams[2], "parameterValue3") is.Equal(params.ShellParams[3], "parameterValue4") is.Equal(params.ShellParams[4], "parameterValue5") is.Equal(params.ShellParams[5], "parameterValue6") is.Equal(params.ShellParams[6], "parameterValue7") is.Equal(params.ShellParams[7], "parameterValue8") is.Equal(params.ShellParams[8], "parameterValue9") is.Equal(params.ShellParams[9], "parameterValue10") // space separator err = parseParamStr(¶ms, strings.Join([]string{ `href="https://xbarapp.com"`, `color=red`, `font="MyFont"`, }, " ")) is.NoErr(err) is.Equal(params.Href, "https://xbarapp.com") is.Equal(params.Color, "#ff0000") is.Equal(params.Font, "MyFont") // tight pipe separator err = parseParamStr(¶ms, `|href="https://xbarapp.com"|color=red|font="MyFont"`) is.NoErr(err) is.Equal(params.Href, "https://xbarapp.com") is.Equal(params.Color, "#ff0000") is.Equal(params.Font, "MyFont") // bash should work as well as shell params = defaultParams err = parseParamStr(¶ms, strings.Join([]string{ "bash=script.sh", }, " ")) is.NoErr(err) is.Equal(params.Shell, "script.sh") } // TestLength tests the length truncation parameter. func TestLength(t *testing.T) { is := is.New(t) item := Item{ Text: strings.Repeat("x", 20), Params: ItemParams{ Length: 10, }, } displayText := item.DisplayText() is.Equal(displayText, "xxxxxxxxx…") item = Item{ Text: strings.Repeat("x", 20), Params: ItemParams{ Length: 30, }, } displayText = item.DisplayText() is.Equal(displayText, strings.Repeat("x", 20)) } // TestDropdown excludes dropdown=false entries from the dropdown. func TestDropdown(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` cycle1 cycle2 cycle3 --- yes1 no | dropdown=false yes2 `))) is.NoErr(err) is.Equal(len(items.CycleItems), 3) // CycleItems is.Equal(len(items.ExpandedItems), 2) // ExpandedItems is.Equal(items.ExpandedItems[0].Text, "yes1") is.Equal(items.ExpandedItems[1].Text, "yes2") } func TestAlternate(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` cycle1 cycle2 cycle3 --- before1 after1 | alternate=true before2 before3 | alternate=false `))) is.NoErr(err) is.Equal(len(items.CycleItems), 3) // CycleItems is.Equal(len(items.ExpandedItems), 3) // ExpandedItems is.Equal(items.ExpandedItems[0].Text, "before1") is.True(items.ExpandedItems[0].Alternate != nil) // should be alternate is.Equal(items.ExpandedItems[0].Alternate.Text, "after1") // after text is.Equal(items.ExpandedItems[1].Text, "before2") is.Equal(items.ExpandedItems[2].Text, "before3") } func TestTrim(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` cycle1 | trim=true cycle2 | trim=false cycle3 `))) is.NoErr(err) is.Equal(len(items.CycleItems), 3) // CycleItems is.Equal(items.CycleItems[0].Text, `cycle1`) is.Equal(items.CycleItems[1].Text, ` cycle2 `) is.Equal(items.CycleItems[2].Text, `cycle3`) } func TestSeparator(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` cycle1 | trim=true cycle2 | trim=false cycle --- one --- two --- three`))) is.NoErr(err) is.Equal(len(items.CycleItems), 3) // CycleItems is.Equal(len(items.ExpandedItems), 5) // ExtendedItems is.Equal(items.ExpandedItems[1].Params.Separator, true) is.Equal(items.ExpandedItems[3].Params.Separator, true) } func TestBlankLines(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` items --- one two three `))) is.NoErr(err) is.Equal(len(items.CycleItems), 1) is.Equal(len(items.ExpandedItems), 5) //is.Equal(items.ExpandedItems[1].Params.Size, 5) // items.ExpandedItems[1].Params.Size } func TestErrors(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` Click me | href= `))) is.NoErr(err) is.Equal(len(items.CycleItems), 1) items, err = p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` Go to --- Open | href="https://xbarapp.com" `))) is.NoErr(err) is.Equal(len(items.ExpandedItems), 1) is.Equal(items.ExpandedItems[0].Text, "Open") is.Equal(items.ExpandedItems[0].Params.Href, "https://xbarapp.com") } func TestTruncate(t *testing.T) { is := is.New(t) const maxLen = 10 for input, expected := range map[string]string{ "basic characters": "basic cha…", "På tide å logge av": "På tide å…", } { is.Equal(truncate(input, maxLen), expected) } } func TestGoodColors(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "colors.txt", strings.NewReader(strings.TrimSpace(` named | color=red RGB | color=#333 RGBA | color=#3338 RRGGBB | color=#4078C0 RRGGBBAA | color=#33333388 darkviolet | color=DarkViolet `))) is.NoErr(err) is.Equal(len(items.CycleItems), 6) // CycleItems is.Equal(items.CycleItems[0].Params.Color, `#ff0000`) is.Equal(items.CycleItems[1].Params.Color, `#333`) is.Equal(items.CycleItems[2].Params.Color, `#3338`) is.Equal(items.CycleItems[3].Params.Color, `#4078c0`) is.Equal(items.CycleItems[4].Params.Color, `#33333388`) is.Equal(items.CycleItems[5].Params.Color, `#9400d3`) } func TestBadColors(t *testing.T) { is := is.New(t) ctx := context.Background() cols := map[string]string{ ``: "colors.txt:1: color: expected hex string or named color", `""`: "colors.txt:1: color: expected hex string or named color", `#`: "colors.txt:1: color: invalid hex format \"#\"", `#fmty`: "colors.txt:1: color: invalid hex format \"#fmty\"", `badname`: "colors.txt:1: color: invalid named color \"badname\"", `#12`: "colors.txt:1: color: invalid hex format \"#12\"", `#12345`: "colors.txt:1: color: invalid hex format \"#12345\"", `#1234567`: "colors.txt:1: color: invalid hex format \"#1234567\"", } for col, errorMessage := range cols { t.Run(col, func(t *testing.T) { is := is.New(t) p := &Plugin{} _, err := p.parseOutput(ctx, "colors.txt", strings.NewReader(strings.TrimSpace(` bad | color=`+col))) is.True(err != nil) is.Equal(err.Error(), errorMessage) }) } } func TestQuotedParams(t *testing.T) { is := is.New(t) ctx := context.Background() p := &Plugin{} items, err := p.parseOutput(ctx, "test.txt", strings.NewReader(strings.TrimSpace(` cycle1 | param1="this is quoted" `))) is.NoErr(err) is.Equal(len(items.CycleItems), 1) is.Equal(items.CycleItems[0].Params.ShellParams[0], "this is quoted") } ================================================ FILE: pkg/plugins/parse.go ================================================ package plugins import ( "bufio" "context" "io" "strings" "github.com/pkg/errors" ) const ( nesting = "--" separator = "---" ) // parseOutput parses the output of a plugin run, and returns the // Items. func (p *Plugin) parseOutput(ctx context.Context, filename string, r io.Reader) (Items, error) { var ( items Items params ItemParams depthPrefix string previousItem *Item ancestorItems []*Item captureExpanded bool line int text string err error readErr error ) br := bufio.NewReader(r) for readErr == nil { // keep reading until we hit io.EOF line++ text, readErr = br.ReadString('\n') if readErr != nil && readErr != io.EOF { // some other error reading (io.EOF is fine) break } if readErr == io.EOF && text == "" { // io.EOF and no text - looks like were done. break } if readErr != io.EOF { // not io.EOF, to trim off the delimiter text = text[:len(text)-1] } text, params, err = parseParams(text) if err != nil { return items, &errParsing{ filename: filename, line: line, text: text, err: err, } } if !captureExpanded && strings.TrimSpace(text) == separator { // first --- means end of cycle items, // start collecting expanded items now captureExpanded = true continue } text, isSeparator := parseSeparator(text) for !strings.HasPrefix(text, depthPrefix) { // drop a level ancestorItems = ancestorItems[:len(ancestorItems)-1] depthPrefix = strings.TrimPrefix(depthPrefix, nesting) } if strings.HasPrefix(text, depthPrefix+nesting) { // if this is a separator in the submenu, // then don't treat it as a another submenu. if strings.TrimPrefix(text, depthPrefix) != separator { // increase a level ancestorItems = append(ancestorItems, previousItem) depthPrefix += nesting } } text = strings.TrimPrefix(text, depthPrefix) if captureExpanded && isSeparator { params.Separator = true separatorItem := &Item{ Plugin: p, Text: text, Params: params, } if len(ancestorItems) > 0 { parentItem := ancestorItems[len(ancestorItems)-1] parentItem.Items = append(parentItem.Items, separatorItem) } else { items.ExpandedItems = append(items.ExpandedItems, separatorItem) } continue } if params.Trim { text = strings.TrimSpace(text) } if params.Emojize { text = Emojize(text) } item := &Item{ Plugin: p, Text: text, Params: params, } if captureExpanded { if len(ancestorItems) > 0 { parentItem := ancestorItems[len(ancestorItems)-1] parentItem.Items = append(parentItem.Items, item) } else { if item.Params.Alternate { // add to previous item, as Alternate previousItem.Alternate = item } else if item.Params.Dropdown { // if Dropdown=false then don't include it items.ExpandedItems = append(items.ExpandedItems, item) } } } else { items.CycleItems = append(items.CycleItems, item) } previousItem = item } if err != nil && err != io.EOF { return items, errors.Wrap(err, "reading") } return items, nil } func parseSeparator(src string) (string, bool) { text := strings.TrimSpace(src) if text == separator { return "", true } for strings.HasPrefix(text, nesting) { text = strings.TrimPrefix(text, nesting) if text == separator { return src[:len(src)-3], true } } return src, false } ================================================ FILE: pkg/plugins/parse_test.go ================================================ package plugins import ( "context" "os" "path/filepath" "strings" "testing" "time" "github.com/matryer/is" ) func TestPluginParams(t *testing.T) { is := is.New(t) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() p := NewPlugin(filepath.Join("testdata", "plugins", "params.3s.sh")) is.Equal(p.RefreshInterval.Duration(), 3*time.Second) p.Refresh(ctx) is.Equal(len(p.Items.CycleItems), 3) is.Equal(p.Items.CycleItems[0].Text, "one") is.Equal(p.Items.CycleItems[0].Params.Color, "#ff0000") is.Equal(p.Items.CycleItems[1].Text, "two") is.Equal(p.Items.CycleItems[1].Params.Color, "#0000ff") is.Equal(p.Items.CycleItems[2].Text, "three") is.Equal(p.Items.CycleItems[2].Params.Href, "https://xbarapp.com") } func TestParseParams(t *testing.T) { is := is.New(t) var s string var params ItemParams var err error // check default values s, params, err = parseParams(`no params`) is.NoErr(err) is.Equal(s, "no params") is.Equal(params.Terminal, false) // Terminal is.Equal(params.Refresh, false) // Refresh is.Equal(params.Dropdown, true) // Dropdown is.Equal(params.Length, 0) // Length is.Equal(params.Trim, true) // Trim is.Equal(params.Alternate, false) // Alternate is.Equal(params.Emojize, true) // Emojize is.Equal(params.ANSI, true) // ANSI s, params, err = parseParams(`Before params |color=#123def`) is.NoErr(err) is.Equal(s, "Before params ") is.Equal(params.Color, "#123def") // with quotes s, params, err = parseParams(`Before params | shell="/annoying path with spaces/file.sh"`) is.NoErr(err) is.Equal(s, "Before params ") is.Equal(params.Shell, "/annoying path with spaces/file.sh") _, params, err = parseParams(`Before params | nope=badparam`) is.True(err != nil) is.Equal(err.Error(), "unknown parameter: nope") } func TestParseErrors(t *testing.T) { iss := is.New(t) assertParseErr := func(is *is.I, err string, s string) { p := &Plugin{} _, actualErr := p.parseOutput(context.Background(), "text.txt", strings.NewReader(s)) is.True(actualErr != nil) is.Equal(err, actualErr.Error()) } assertParseErr(iss, `text.txt:3: terminal: expected "true" or "false", not "123"`, ` one | terminal=123 `) assertParseErr(iss, `text.txt:2: length: expected an int, not "false"`, ` one | length=false `) } func TestParseEmoji(t *testing.T) { const text = "I sure would like some :spaghetti: and :pizza:." var ( is = is.New(t) p = &Plugin{} r = strings.NewReader(text) ) items, err := p.parseOutput(context.Background(), "with-emoji.txt", r) is.NoErr(err) is.Equal(len(items.CycleItems), 1) is.Equal(items.CycleItems[0].Text, "I sure would like some 🍝 and 🍕.") r = strings.NewReader(text + " | emojize=false") items, err = p.parseOutput(context.Background(), "no-emoji.txt", r) is.NoErr(err) is.Equal(len(items.CycleItems), 1) is.Equal(items.CycleItems[0].Text, text) } func TestParseMultiplePipes(t *testing.T) { is := is.New(t) const text = `cloudflare | bash=/tmp/bitbar_dns_switcher_cloudflare | terminal=false | refresh=true google | bash=/tmp/bitbar_dns_switcher_google | terminal=true | refresh=false ` p := &Plugin{} items, err := p.parseOutput(context.Background(), "optional-pipes.txt", strings.NewReader(text)) is.NoErr(err) is.Equal(items.CycleItems[0].Text, "cloudflare") is.Equal(items.CycleItems[0].Params.Terminal, false) is.Equal(items.CycleItems[0].Params.Refresh, true) is.Equal(items.CycleItems[1].Text, "google") is.Equal(items.CycleItems[1].Params.Terminal, true) is.Equal(items.CycleItems[1].Params.Refresh, false) } func TestStartWithPipe(t *testing.T) { is := is.New(t) const src = `| templateImage=iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB70lEQVR4AWJwL/ABtFsOMHfDURSft2iOjdnlvMUv1oJ5wWej/Z5ZjcEQc2YwxWOcOZoRzIid7Jzx2Uza5Jde9d7zbvuSf0NxBbgCXAEpjizLayRJega+i6L4pZqwJ3sLgrA6n4BD4BMeOAcuVJlz7M0ZOQVA4TEUnK/VutmbM/Jt4CgKLtVKAHtzRkUCcE1AnYSVbgbrCW1VVUXmai4Aw7agxgAnFEW5T2CfBAnmai6Ag0ZGRoKGYeyJRCKdhPbo6GgAAuL12ICpaZrtOE5XMrqum8hF67GBvV6vdx+uvmR8Pp/DXD02sJ+/FkM7ksFWDOT21UNAdGhoKGzb9q5k+F0gF6vHK/APDg6GLMvamczw8HCA4uqxgUBTCmCsLAGbNm2ahgcn/xFwBFws0MSXSwBykXzPsjdn0F68ePEUj8czlUEJ10GIuA4+wP6B+23c74I7hD64BZvx1z09PSY+vB3JMIaad6xhbRZu/un9AVyBncDs+X/XOhfORgR3wu7nh8ZV/yEIP0xQExUE4Ulvb28sXUBfX18UtTxL2MCBve8v8A32Ab1gG88duM8o60TE94y/nC9dAGPIWTU/kkF9f3t7+zFd14c1TRshtBnj9mouYNmyZbN4wlEU5TNW+xa8ps3YihUrZrqn4pYT4Ar4CW6NezCnH1ZyAAAAAElFTkSuQmCC` p := &Plugin{} items, err := p.parseOutput(context.Background(), "handoff.sh", strings.NewReader(src)) is.NoErr(err) is.Equal(len(items.CycleItems), 1) is.True(items.CycleItems[0].Params.TemplateImage != "") } // TestTokenTooLong tests for really long tokens. // https://github.com/matryer/xbar/issues/629 func TestTokenTooLong(t *testing.T) { is := is.New(t) f, err := os.Open(filepath.Join("testdata", "token-too-long", "jma.1h.sh.output")) is.NoErr(err) // os.Open t.Cleanup(func() { err := f.Close() is.NoErr(err) // f.Close }) p := Plugin{} ctx := context.Background() items, err := p.parseOutput(ctx, "jma.1h.sh", f) is.NoErr(err) is.Equal(len(items.CycleItems), 1) } // TestNestedSeparators ensures separators work inside submenus. // https://github.com/matryer/xbar/issues/648 func TestNestedSeparators(t *testing.T) { is := is.New(t) src := `a --- b --c ----- --d ----e ------- ----f ----- --g --- h` p := &Plugin{} items, err := p.parseOutput(context.Background(), "nesting.txt", strings.NewReader(src)) is.NoErr(err) is.Equal(len(items.ExpandedItems), 3) // root items is.Equal(items.ExpandedItems[0].Text, "b") is.Equal(items.ExpandedItems[1].Params.Separator, true) is.Equal(items.ExpandedItems[2].Text, "h") // b submenu is.Equal(len(items.ExpandedItems[0].Items), 5) is.Equal(items.ExpandedItems[0].Items[0].Text, "c") is.Equal(items.ExpandedItems[0].Items[1].Params.Separator, true) // should be separator is.Equal(items.ExpandedItems[0].Items[2].Text, "d") is.Equal(items.ExpandedItems[0].Items[3].Params.Separator, true) is.Equal(items.ExpandedItems[0].Items[4].Text, "g") // d submenu is.Equal(len(items.ExpandedItems[0].Items[2].Items), 3) is.Equal(items.ExpandedItems[0].Items[2].Items[0].Text, "e") is.Equal(items.ExpandedItems[0].Items[2].Items[1].Params.Separator, true) is.Equal(items.ExpandedItems[0].Items[2].Items[2].Text, "f") } func TestParseSeparator(t *testing.T) { is := is.New(t) text, isSep := parseSeparator("no") is.Equal(isSep, false) is.Equal(text, "no") text, isSep = parseSeparator(separator) is.Equal(isSep, true) is.Equal(text, "") text, isSep = parseSeparator(nesting + separator) is.Equal(isSep, true) is.Equal(text, nesting) text, isSep = parseSeparator(nesting + nesting + separator) is.Equal(isSep, true) is.Equal(text, nesting+nesting) text, isSep = parseSeparator(nesting + nesting + "item") is.Equal(isSep, false) is.Equal(text, nesting+nesting+"item") } ================================================ FILE: pkg/plugins/plugin.go ================================================ package plugins import ( "bytes" "context" "fmt" "io" "io/ioutil" "log" "os" "os/exec" "path/filepath" "runtime" "strings" "sync" "time" "github.com/pkg/errors" ) type ( // RefreshFunc is a callback fired after a Plugin is refreshed. RefreshFunc func(ctx context.Context, p *Plugin, err error) // CycleFunc is a callback fired after a Plugin's CycleIndex // has changed. CycleFunc func(ctx context.Context, p *Plugin) // DebugFunc is a function that records debug information. DebugFunc func(format string, v ...interface{}) ) // Plugin is a single executable xbar plugin. type Plugin struct { // Command is the excutable file that this plugin calls. Command string // Variables are the values in the accompanying .vars.json file. Variables []string // Items are the menu items for this plugin. Items Items // RefreshInterval is the duration at which this Plugin should // update. RefreshInterval RefreshInterval // CycleInterval is the interval at which the Items.CycleItems // will change. CycleInterval time.Duration // CycleIndex is the currently active Item from CycleItems. CycleIndex int // Timeout is the time.Duration within which a plugin execution // must complete before being cancelled. Timeout time.Duration // Debugf is a function that writes debug information. Debugf DebugFunc // OnRefresh is called when the plugin has been updated. // Ignored if nil. OnRefresh RefreshFunc // OnCycle is called when the Plugin's CycleIndex has changed. OnCycle CycleFunc // Stdout is a writer that will have stdout written to if not nil. Stdout io.Writer // Stderr is a writer that will have stderr written to if not nil. Stderr io.Writer // refreshSignal is a signal which will trigger the plugin to refresh. // Called via TriggerRefresh(). refreshSignal chan (struct{}) // cycleSignal is a signal channel which will trigger the plugin to // update its cycle. // Called in TriggerRefresh() when updating the plugin menu to the // refreshing state, before refreshSignal is triggered. cycleSignal chan (struct{}) // appleScriptTemplate3 is the template for the AppleScript // to run this action in a terminal. AppleScriptTemplate string } // CleanFilename gets a clean human readable representation of the // filename. Specifically by stripping off any 001- prefixes. func (p Plugin) CleanFilename() string { fn := filepath.Base(p.Command) var count int _, _ = fmt.Sscanf(fn, "%d-%v", &count, &fn) return fn } // cycle advances the CycleIndex, and wraps around if // we've reached the end. func (p *Plugin) cycle(ctx context.Context) { p.CycleIndex++ if p.CycleIndex == len(p.Items.CycleItems) { p.CycleIndex = 0 } if p.OnCycle != nil { p.OnCycle(ctx, p) } } // Plugins are many plugins that can be executed // synchronously. type Plugins []*Plugin // Run executes the plugins at regular intervals // updating the menu items based on the output of the // executable. // Use the context for cancelation. func (p Plugins) Run(ctx context.Context) { var wg sync.WaitGroup for i := range p { wg.Add(1) go func(p *Plugin) { p.Run(ctx) wg.Done() }(p[i]) } wg.Wait() } // Exist checks whether a plugin exists. func (p Plugins) Exist(path string) bool { filename := filepath.Base(path) for i := range p { if filename == filepath.Base(p[i].Command) { return true } } return false } // Dir gets Plugins from a directory. func Dir(path string) (Plugins, error) { files, err := ioutil.ReadDir(path) if err != nil { return nil, err } plugins := make(Plugins, 0, len(files)) for _, file := range files { filename := file.Name() if strings.HasPrefix(filename, ".") { // ignore .dot files continue } if file.IsDir() { // ignore directories continue } if strings.HasSuffix(filename, variableJSONFileExt) { // ignore .vars.json files continue } if !IsPluginEnabled(filename) { // ignore disabled plugins continue } command := filepath.Join(path, filename) plugins = append(plugins, NewPlugin(command)) } return plugins, nil } // NewPlugin makes a new Plugin with the specified executable // file. func NewPlugin(command string) *Plugin { filename := filepath.Base(command) p := &Plugin{ Timeout: 2 * time.Minute, CycleInterval: 5 * time.Second, Command: command, Debugf: DebugfNoop, refreshSignal: make(chan struct{}, 1), cycleSignal: make(chan struct{}, 1), } var err error p.RefreshInterval, err = ParseFilenameInterval(filename) if err != nil { p.Debugf("failed to process interval: %s: %s (using default %v)", filename, err, defaultRefreshInterval) p.RefreshInterval = defaultRefreshInterval } return p } // Run executes the plugin at regular intervals // updating the menu items based on the output of the // executable. // Use the context for cancelation. func (p *Plugin) Run(ctx context.Context) { var err error p.Variables, err = p.loadVariablesAsEnvVars() if err != nil { p.Debugf("ERR: %s", err) p.OnErr(err) } p.Refresh(ctx) cycleReset := make(chan struct{}) var wg sync.WaitGroup // cycle loop wg.Add(1) go func() { defer wg.Done() for { select { case <-cycleReset: // this will loop round and start the CycleInterval // timer again. p.CycleIndex = 0 continue case <-p.cycleSignal: p.Debugf("cycling: %s", filepath.Base(p.Command)) p.cycle(ctx) case <-time.After(p.CycleInterval): p.Debugf("cycling: %s", filepath.Base(p.Command)) p.cycle(ctx) case <-ctx.Done(): return } } }() // refresh (reexecutation) loop wg.Add(1) go func() { defer wg.Done() for { select { case <-p.refreshSignal: p.Debugf("refreshing: %s", filepath.Base(p.Command)) p.Refresh(ctx) cycleReset <- struct{}{} case <-time.After(p.RefreshInterval.Duration()): p.Debugf("refreshing: %s", filepath.Base(p.Command)) p.Refresh(ctx) cycleReset <- struct{}{} case <-ctx.Done(): return } } }() wg.Wait() p.Debugf("finished") } // TriggerRefresh triggers a refresh on this Plugin. func (p *Plugin) TriggerRefresh() { // disable the menu p.CycleIndex = 0 // reset // just keep the current item currentItem := p.CurrentCycleItem() //currentItem.Text = "…" currentItem.Params.Disabled = true p.Items.CycleItems = []*Item{ currentItem, } p.CycleIndex = 0 // reset if p.OnCycle != nil { p.cycleSignal <- struct{}{} } // trigger the actual refresh p.refreshSignal <- struct{}{} } // Refresh executes and updates the Plugin. // The menu is updated in an instant, unlike with Refresh(). // Run calls this method periodically. func (p *Plugin) Refresh(ctx context.Context) { err := p.refresh(ctx) if err != nil { p.Debugf("ERR: %s", err) p.OnErr(err) } p.CycleIndex = 0 // reset if p.OnRefresh != nil { p.OnRefresh(ctx, p, err) } } // CurrentCycleItem returns the Item related to the current cycle. func (p *Plugin) CurrentCycleItem() *Item { if len(p.Items.CycleItems) == 0 { return nil } if p.CycleIndex > len(p.Items.CycleItems)-1 { p.CycleIndex = 0 } return p.Items.CycleItems[p.CycleIndex] } // RunInTerminal runs this plugin in a terminal using the template // apple script. func (p *Plugin) RunInTerminal(appleScriptTemplate3 string) error { return p.runInTerminal(appleScriptTemplate3, p.Command, "", p.Variables) } // refresh runs the plugin and parses the output, updating the // state of Plugin. func (p *Plugin) refresh(ctx context.Context) error { commandCtx, cancel := context.WithTimeout(ctx, p.Timeout) defer cancel() var path string if runtime.GOOS == "windows" { path = p.Command } else { path = "./" + filepath.Base(p.Command) } cmd := exec.CommandContext(commandCtx, path) Setpgid(cmd) cmd.Dir = filepath.Dir(p.Command) // inherit outside environment cmd.Env = append(cmd.Env, os.Environ()...) // add variables from .vars.json file cmd.Env = append(cmd.Env, p.Variables...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if p.Stdout != nil { cmd.Stdout = io.MultiWriter(cmd.Stdout, p.Stdout) } if p.Stderr != nil { cmd.Stderr = io.MultiWriter(cmd.Stderr, p.Stderr) } if err := cmd.Run(); err != nil { return errExec{ err: err, Stderr: stderr.String(), } } var err error p.Items, err = p.parseOutput(ctx, filepath.Base(p.Command), &stdout) if err != nil { return errors.Wrap(err, "parse stdout") } return nil } // OnErr is called when something has gone wrong at some point. func (p *Plugin) OnErr(err error) { p.Items.CycleItems = []*Item{ { Plugin: p, Text: "⚠️ " + p.CleanFilename(), }, } p.Items.ExpandedItems = p.stringToItems(err.Error()) } // errExec is used for plugin execution errors. type errExec struct { // Stderr is the data captured from stderr. Stderr string // err is the cause. err error } func (e errExec) Error() string { if e.Stderr != "" { return e.err.Error() + ": " + e.Stderr } return e.err.Error() } // stringToItems turns a string into one or more Item objects, // breaking long strings down effectively wrapping them. func (p *Plugin) stringToItems(s string) []*Item { var items []*Item for _, str := range strings.Split(s, "\n") { if len(strings.TrimSpace(str)) == 0 { // skip empty lines continue } items = append(items, &Item{ Params: ItemParams{ Dropdown: true, }, Plugin: p, Text: str, }) } if strings.Contains(s, "fork/exec") && strings.Contains(s, "exec format error") { // add a tip items = append(items, &Item{ Params: ItemParams{ Separator: true, }, }) items = append(items, &Item{ Params: ItemParams{ Dropdown: true, }, Plugin: p, Text: "👉 Don't forget your shebang at the top of the plugin script file", }) } if strings.Contains(s, "fork/exec") && strings.Contains(s, "permission denied") { // add a tip items = append(items, &Item{ Params: ItemParams{ Separator: true, }, }) items = append(items, &Item{ Params: ItemParams{ Dropdown: true, }, Plugin: p, Text: "👉 Make your script executable: chmod +x " + filepath.Base(p.Command), }) } return items } // DebugfNoop is a silent DebugFunc. func DebugfNoop(format string, v ...interface{}) {} // DebugfLog uses log.Print to write debug information. func DebugfLog(format string, v ...interface{}) { log.Printf(format, v...) } // DebugfPrefix wraps a DebugFunc and prepends a prefix string. func DebugfPrefix(prefix string, debugf DebugFunc) DebugFunc { return func(format string, v ...interface{}) { s := fmt.Sprintf(format, v...) if len(prefix) > 0 { lines := strings.Split(s, "\n") for i := range lines { lines[i] = prefix + ": " + lines[i] } s = strings.Join(lines, "\n") } debugf("%s", s) } } // errParsing is used for output parsing errors. type errParsing struct { filename string line int text string err error } func (e *errParsing) Error() string { return fmt.Sprintf("%s:%d: %v", e.filename, e.line, e.err) } func variablesEnvString(vars []string) string { quotesVars := make([]string, 0, len(vars)) for i := range vars { split := strings.Index(vars[i], "=") if split == -1 { // skip malformed variable (shouldn't happen) log.Println("skipping malformed variable:", vars[i]) continue } quotesVars = append(quotesVars, fmt.Sprintf("%s=%q", vars[i][:split], vars[i][split+1:])) } return strings.Join(quotesVars, " ") } ================================================ FILE: pkg/plugins/plugin_darwin.go ================================================ package plugins import ( "bytes" "fmt" "log" "os/exec" "syscall" "text/template" "github.com/pkg/errors" ) func Setpgid(cmd *exec.Cmd) { cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, } } func (p *Plugin) runInTerminal(appleScriptTemplate3, command, paramsStr string, vars []string) error { tpl, err := template.New("appleScriptTemplate3").Parse(appleScriptTemplate3) if err != nil { return err } commandLine := command var renderedScript bytes.Buffer err = tpl.Execute(&renderedScript, struct { Command string Vars string Params string }{ Command: commandLine, Vars: fmt.Sprintf("%q", variablesEnvString(vars)), Params: paramsStr, }) if err != nil { return err } appleScript := renderedScript.String() log.Println(p.Command, "RunInTerminal", appleScript) cmd := exec.Command("osascript", "-s", "h", "-e", appleScript) var stderr bytes.Buffer cmd.Stderr = &stderr err = cmd.Run() if err != nil { p.Debugf("(ignoring) RunInTerminal failed: %s", err) } if cmd.ProcessState != nil && cmd.ProcessState.ExitCode() != 0 { return errors.Errorf("run in terminal script failed: %s", stderr.String()) } return nil } ================================================ FILE: pkg/plugins/plugin_linux.go ================================================ package plugins import ( "bytes" "log" "os/exec" "syscall" "github.com/pkg/errors" ) func Setpgid(cmd *exec.Cmd) { cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, } } func (p *Plugin) runInTerminal(appleScriptTemplate3, command, paramsStr string, vars []string) error { log.Println(p.Command, "RunInTerminal", command) // x-terminal-emulator is provided by the 'alternatives' system // on most (all?) common Linux distributions, this is mapped to the default terminal application cmd := exec.Command("x-terminal-emulator", "-e", command) cmd.Env = append(cmd.Env, vars...) var stderr bytes.Buffer cmd.Stderr = &stderr err := cmd.Run() if err != nil { p.Debugf("(ignoring) RunInTerminal failed: %s", err) } if cmd.ProcessState != nil && cmd.ProcessState.ExitCode() != 0 { return errors.Errorf("run in terminal script failed: %s", stderr.String()) } return nil } ================================================ FILE: pkg/plugins/plugin_test.go ================================================ package plugins import ( "context" "path/filepath" "strings" "sync" "testing" "time" "github.com/matryer/is" "github.com/pkg/errors" ) func TestRun(t *testing.T) { is := is.New(t) plugins, err := Dir(filepath.Join("testdata", "plugins")) is.NoErr(err) var lock sync.Mutex // protects counters var counters struct { cycles int refreshes int } for i := range plugins { plugins[i].Timeout = 2 * time.Second plugins[i].Debugf = func(format string, v ...interface{}) { /* silent */ } plugins[i].CycleInterval = 100 * time.Millisecond plugins[i].RefreshInterval = RefreshInterval{N: 250, Unit: "milliseconds"} plugins[i].OnRefresh = func(ctx context.Context, p *Plugin, err error) { lock.Lock() counters.refreshes++ lock.Unlock() } plugins[i].OnCycle = func(ctx context.Context, p *Plugin) { lock.Lock() counters.cycles++ lock.Unlock() } // plugin := plugins[i] // plugins[i].Debugf = func(format string, v ...interface{}) { // log.Printf(filepath.Base(plugin.Command)+": "+format, v...) // } } ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() plugins.Run(ctx) is.True(counters.refreshes > 10) is.True(counters.cycles > 20) is.True(counters.cycles > counters.refreshes) } func TestRunStderr(t *testing.T) { is := is.New(t) ps, err := Dir(filepath.Join("testdata", "broken-plugins")) is.NoErr(err) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) t.Cleanup(cancel) ps[0].Run(ctx) is.Equal(ps[0].Items.CycleItems[0].Text, "⚠️ broken.1m.sh") is.Equal(ps[0].Items.ExpandedItems[0].Text, "exit status 1: an error printed to stderr") } func TestPluginsDir(t *testing.T) { is := is.New(t) plugins, err := Dir(filepath.Join("testdata", "plugins")) is.NoErr(err) is.Equal(len(plugins), 8) } func TestPluginsExist(t *testing.T) { is := is.New(t) plugins, err := Dir(filepath.Join("testdata", "plugins")) is.NoErr(err) is.Equal(plugins.Exist("simple.1s.sh"), true) is.Equal(plugins.Exist("missing.1s.sh"), false) } func TestPluginOnRefresh(t *testing.T) { is := is.New(t) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() p := NewPlugin(filepath.Join("testdata", "plugins", "simple.1s.sh")) OnRefreshCalls := 0 p.OnRefresh = func(refreshCtx context.Context, plugin *Plugin, err error) { is.NoErr(err) // err is.Equal(ctx, refreshCtx) // ctx is.Equal(p, plugin) // plugin OnRefreshCalls++ is.Equal(p.CycleIndex, 0) // CycleIndex should get reset } is.Equal(p.RefreshInterval.Duration(), 1*time.Second) p.CycleIndex = 1 p.Refresh(ctx) is.Equal(OnRefreshCalls, 1) // OnRefreshCalls } func TestPluginSimple(t *testing.T) { is := is.New(t) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() p := NewPlugin(filepath.Join("testdata", "plugins", "simple.1s.sh")) is.Equal(p.RefreshInterval.Duration(), 1*time.Second) p.Refresh(ctx) is.Equal(len(p.Items.CycleItems), 3) is.Equal(p.Items.CycleItems[0].Text, "one") is.Equal(p.Items.CycleItems[1].Text, "two") is.Equal(p.Items.CycleItems[2].Text, "three") is.Equal(len(p.Items.ExpandedItems), 0) } func TestPluginExpanded(t *testing.T) { is := is.New(t) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() p := NewPlugin(filepath.Join("testdata", "plugins", "expanded.1s.sh")) is.Equal(p.RefreshInterval.Duration(), 1*time.Second) p.Refresh(ctx) is.Equal(len(p.Items.CycleItems), 3) is.Equal(p.Items.CycleItems[0].Text, "one") is.Equal(p.Items.CycleItems[1].Text, "two") is.Equal(p.Items.CycleItems[2].Text, "three") is.Equal(len(p.Items.ExpandedItems), 3) is.Equal(p.Items.ExpandedItems[0].Text, "four") is.Equal(p.Items.ExpandedItems[1].Text, "five") is.Equal(p.Items.ExpandedItems[2].Text, "six") } func TestCycleItems(t *testing.T) { is := is.New(t) onCycleCalls := 0 ctx := context.Background() p := &Plugin{ OnCycle: func(ctx context.Context, p *Plugin) { onCycleCalls++ }, } p.Items = Items{ CycleItems: []*Item{ // three items { Text: "1", Plugin: p, }, { Text: "2", Plugin: p, }, { Text: "3", Plugin: p, }, }, } is.Equal(p.CycleIndex, 0) // default CycleIndex is.Equal(p.CurrentCycleItem().Text, "1") p.cycle(ctx) is.Equal(p.CycleIndex, 1) // CycleIndex is.Equal(onCycleCalls, 1) // onCycleCalls is.Equal(p.CurrentCycleItem().Text, "2") p.cycle(ctx) is.Equal(p.CycleIndex, 2) // CycleIndex is.Equal(onCycleCalls, 2) // onCycleCalls is.Equal(p.CurrentCycleItem().Text, "3") p.cycle(ctx) is.Equal(p.CycleIndex, 0) // CycleIndex is.Equal(onCycleCalls, 3) // onCycleCalls is.Equal(p.CurrentCycleItem().Text, "1") p.cycle(ctx) is.Equal(p.CycleIndex, 1) // CycleIndex is.Equal(onCycleCalls, 4) // onCycleCalls is.Equal(p.CurrentCycleItem().Text, "2") p.cycle(ctx) is.Equal(p.CycleIndex, 2) // CycleIndex is.Equal(onCycleCalls, 5) // onCycleCalls is.Equal(p.CurrentCycleItem().Text, "3") // go out of range - can happen if refreshed while // cycling p.CycleIndex = 5 is.Equal(p.CurrentCycleItem().Text, "1") // should revert to first one } func TestSubmenus(t *testing.T) { is := is.New(t) p := &Plugin{} items, err := p.parseOutput(context.Background(), "text.txt", strings.NewReader(strings.TrimSpace(` cycle1 cycle2 cycle3 --- parent1 --child1 --child2 --child3 ----subchild1 ----subchild2 ------subsubchild1 --child4 parent2 --child1 --child2 --child3 parent3 `))) is.NoErr(err) is.Equal(len(items.ExpandedItems), 3) // 3 parent items is.Equal(items.ExpandedItems[0].Text, "parent1") is.Equal(len(items.ExpandedItems[0].Items), 4) is.Equal(items.ExpandedItems[0].Items[0].Text, "child1") is.Equal(items.ExpandedItems[0].Items[1].Text, "child2") is.Equal(items.ExpandedItems[0].Items[2].Text, "child3") is.Equal(len(items.ExpandedItems[0].Items[2].Items), 2) is.Equal(items.ExpandedItems[0].Items[2].Items[0].Text, "subchild1") is.Equal(items.ExpandedItems[0].Items[2].Items[1].Text, "subchild2") is.Equal(items.ExpandedItems[0].Items[3].Text, "child4") is.Equal(items.ExpandedItems[1].Text, "parent2") is.Equal(len(items.ExpandedItems[1].Items), 3) is.Equal(items.ExpandedItems[1].Items[0].Text, "child1") is.Equal(items.ExpandedItems[1].Items[1].Text, "child2") is.Equal(items.ExpandedItems[1].Items[2].Text, "child3") is.Equal(items.ExpandedItems[2].Text, "parent3") is.Equal(len(items.ExpandedItems[2].Items), 0) } func TestOnErr(t *testing.T) { is := is.New(t) p := &Plugin{} err := errors.New(`this is a very long item, it contains lots of lines. It also contains a very long line which should be wrapped, because otherwise it will result in a very long menu item that will be impossible to read`) p.OnErr(err) var s string for i := range p.Items.ExpandedItems { s += p.Items.ExpandedItems[i].Text + "\n" } is.Equal(len(p.Items.ExpandedItems), 3) } func TestCleanFilename(t *testing.T) { is := is.New(t) p := Plugin{ Command: "/path/to/001-file.2m.sh", } is.Equal(p.CleanFilename(), "file.2m.sh") p = Plugin{ Command: "/path/to/file.2m.sh", } is.Equal(p.CleanFilename(), "file.2m.sh") p = Plugin{ Command: "file.sh", } is.Equal(p.CleanFilename(), "file.sh") } // TestPluginWontQuit tests to see if plugins that ignore the signal // still end up refreshing. Turns out they do because we're using exec.CommandContext. func TestPluginWontQuit(t *testing.T) { p := Plugin{ Debugf: t.Logf, Command: filepath.Join("testdata", "broken-plugins", "wont-quit.1m.sh"), } ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) t.Cleanup(func() { cancel() }) p.Refresh(ctx) p.Refresh(ctx) p.Refresh(ctx) } func TestVariablesEnvString(t *testing.T) { is := is.New(t) vars := []string{ "one=1", "two=2", "spaces=I have spaces", "malformed", } s := variablesEnvString(vars) is.Equal(s, `one="1" two="2" spaces="I have spaces"`) } // TestPipeElsewhere checks to see if the plugin can handle // a pipe elsewhere in the output. // https://github.com/matryer/xbar/issues/743 func TestPipeElsewhere(t *testing.T) { is := is.New(t) p := &Plugin{} in := `Copy TITLE | font=Monaco size=10 alternate=true shell='/bin/bash' param1='-c' param2="echo -n string | pbcopy" terminal=false` items, err := p.parseOutput(context.Background(), "text.txt", strings.NewReader(in)) is.NoErr(err) is.Equal(len(items.CycleItems), 1) is.Equal(len(items.CycleItems[0].Params.ShellParams), 2) is.Equal(items.CycleItems[0].Params.ShellParams[0], "-c") is.Equal(items.CycleItems[0].Params.ShellParams[1], "echo -n string | pbcopy") } ================================================ FILE: pkg/plugins/plugin_windows.go ================================================ package plugins import ( "bytes" "log" "os/exec" "github.com/pkg/errors" ) // Setpgid is a no-op in Windows func Setpgid(cmd *exec.Cmd) { } func (p *Plugin) runInTerminal(appleScriptTemplate3, command, paramsStr string, vars []string) error { log.Println(p.Command, "RunInTerminal", command) cmd := exec.Command("start", "cmd", "/k", command) cmd.Env = append(cmd.Env, vars...) var stderr bytes.Buffer cmd.Stderr = &stderr err := cmd.Run() if err != nil { p.Debugf("(ignoring) RunInTerminal failed: %s", err) } if cmd.ProcessState != nil && cmd.ProcessState.ExitCode() != 0 { return errors.Errorf("run in terminal script failed: %s", stderr.String()) } return nil } ================================================ FILE: pkg/plugins/refresh_interval.go ================================================ package plugins import ( "fmt" "os" "path/filepath" "strconv" "strings" "time" "github.com/pkg/errors" ) // defaultRefreshInterval is the interval at which plugins will refresh // if no value is specified in the plugin's filename. var defaultRefreshInterval = RefreshInterval{ N: 1, Unit: "minutes", } // RefreshInterval indicates how often a plugin runs and refreshes its output. // This type is exposed to ease interaction between the UI and the backend. type RefreshInterval struct { N int64 `json:"n"` Unit string `json:"unit"` } // Duration gets the time.Duration for this RefreshInterval. func (r RefreshInterval) Duration() time.Duration { switch r.Unit { case "milliseconds": return time.Duration(r.N) * time.Millisecond case "days": return time.Hour * time.Duration(24*r.N) case "hours": return time.Duration(r.N) * time.Hour case "minutes": return time.Duration(r.N) * time.Minute case "seconds": return time.Duration(r.N) * time.Second default: // default return 1 * time.Minute } } // String returns the RefreshInterval as a string that can be used as an xbar // interval, as represented in plugin filenames. func (r RefreshInterval) String() string { switch r.Unit { case "days": return fmt.Sprintf("%dd", r.N) case "hours": return fmt.Sprintf("%dh", r.N) case "minutes": return fmt.Sprintf("%dm", r.N) case "seconds": return fmt.Sprintf("%ds", r.N) case "milliseconds": return fmt.Sprintf("%dms", r.N) default: return "" } } // SetRefreshInterval sets the time interval at which a plugin should be re-run. func SetRefreshInterval(pluginDirectory, installedPluginPath string, refreshInterval RefreshInterval) (string, RefreshInterval, error) { interval := findIntervalInFilename(installedPluginPath) if err := validateRefreshInterval(refreshInterval); err != nil { return "", RefreshInterval{}, errors.Wrap(err, "invalid refresh interval") } oldFullPath := filepath.Join(pluginDirectory, installedPluginPath) newFilename := strings.Replace(installedPluginPath, "."+interval+".", "."+refreshInterval.String()+".", 1) newFullPath := filepath.Join(pluginDirectory, newFilename) if err := os.Rename(oldFullPath, newFullPath); err != nil { return "", RefreshInterval{}, errors.Wrap(err, "rename plugin file to new refresh interval") } _, err := os.Stat(newFullPath) if err != nil { return "", RefreshInterval{}, errors.Wrap(err, "stat plugin file") } oldVarFullPath := oldFullPath + variableJSONFileExt _, err = os.Stat(oldVarFullPath) if err != nil && !os.IsNotExist(err) { return "", RefreshInterval{}, errors.Wrap(err, "stat plugin vars file") } if err != nil && os.IsNotExist(err) { // no variable file, no probs return newFilename, refreshInterval, nil } newVarFilename := newFilename + variableJSONFileExt newVarFullPath := filepath.Join(pluginDirectory, newVarFilename) if err := os.Rename(oldVarFullPath, newVarFullPath); err != nil { return "", RefreshInterval{}, errors.Wrap(err, "rename plugin vars file to new refresh interval") } return newFilename, refreshInterval, nil } func validateRefreshInterval(refreshInterval RefreshInterval) error { if n := refreshInterval.N; n < 1 { return errors.Errorf("bad interval value: %d", n) } for _, unit := range []string{"days", "hours", "minutes", "seconds", "milliseconds"} { if refreshInterval.Unit == unit { return nil } } return errors.Errorf("bad interval unit: %s", refreshInterval.Unit) } // ParseFilenameInterval parses the filename to extract the refresh interval // or returns a default if it is do so. func ParseFilenameInterval(filename string) (RefreshInterval, error) { // ignore disabled piece filename = strings.TrimSuffix(filename, disabledPluginExtension) intervalStr := findIntervalInFilename(filename) if intervalStr == "" { return defaultRefreshInterval, nil } interval, err := parseInterval(intervalStr) if err != nil { return defaultRefreshInterval, errors.Errorf("%s (from %s)", err.Error(), filename) } return interval, nil } func findIntervalInFilename(filename string) string { if filename == "" { return "" } if !IsPluginEnabled(filename) { filename = strings.TrimSuffix(filename, disabledPluginExtension) } fn := filename ext := filepath.Ext(filename) if ext != "" { fn = fn[:len(fn)-len(ext)] } segs := strings.Split(fn, ".") if len(segs) == 1 { return "" } interval := segs[len(segs)-1] return interval } func parseInterval(interval string) (RefreshInterval, error) { var ( unit string valStr string ) switch { case strings.HasSuffix(interval, "ms"): unit = "ms" valStr = interval[:len(interval)-2] default: unit = string(interval[len(interval)-1]) valStr = interval[:len(interval)-1] } val, err := strconv.ParseInt(valStr, 10, 64) if err != nil { return defaultRefreshInterval, errors.Errorf("bad interval value: %s", valStr) } switch unit { case "d": // turn days into hours return RefreshInterval{N: val, Unit: "days"}, nil case "h": return RefreshInterval{N: val, Unit: "hours"}, nil case "m": return RefreshInterval{N: val, Unit: "minutes"}, nil case "s": return RefreshInterval{N: val, Unit: "seconds"}, nil case "ms": return RefreshInterval{N: val, Unit: "milliseconds"}, nil default: return defaultRefreshInterval, errors.Errorf("bad interval unit: %s", string(unit)) } } ================================================ FILE: pkg/plugins/refresh_interval_test.go ================================================ package plugins import ( "os" "path/filepath" "testing" "time" "github.com/matryer/is" "github.com/pkg/errors" ) func TestSetRefreshInterval(t *testing.T) { is := is.New(t) baseTestPath := filepath.Join("testdata", "set-refresh-interval-test") t.Run("single-file plugin", func(t *testing.T) { var ( testpath = filepath.Join(baseTestPath, "single-file-plugin") oldPluginName = "set-refresh-interval.1m.sh" oldPluginPath = filepath.Join(testpath, oldPluginName) newPluginName = "set-refresh-interval.1d.sh" newPluginPath = filepath.Join(testpath, newPluginName) ) err := os.MkdirAll(testpath, 0777) is.NoErr(err) t.Cleanup(func() { os.RemoveAll(testpath) }) _, err = os.Create(oldPluginPath) is.NoErr(err) renamedPlugin, refreshInterval, err := SetRefreshInterval(testpath, oldPluginName, RefreshInterval{N: 1, Unit: "days"}) is.NoErr(err) is.Equal(renamedPlugin, newPluginName) is.Equal(refreshInterval, RefreshInterval{N: 1, Unit: "days"}) _, err = os.Stat(newPluginPath) is.NoErr(err) }) t.Run("update vars json file too", func(t *testing.T) { var ( testpath = filepath.Join(baseTestPath, "single-file-plugin") oldPluginName = "set-refresh-interval.1m.sh" oldPluginPath = filepath.Join(testpath, oldPluginName) oldPluginVarsName = "set-refresh-interval.1m.sh" + variableJSONFileExt oldPluginVarsPath = filepath.Join(testpath, oldPluginVarsName) newPluginName = "set-refresh-interval.1d.sh" newPluginPath = filepath.Join(testpath, newPluginName) newPluginVarsName = "set-refresh-interval.1d.sh" + variableJSONFileExt newPluginVarsPath = filepath.Join(testpath, newPluginVarsName) ) err := os.MkdirAll(testpath, 0777) is.NoErr(err) t.Cleanup(func() { os.RemoveAll(testpath) }) _, err = os.Create(oldPluginPath) is.NoErr(err) _, err = os.Create(oldPluginVarsPath) is.NoErr(err) renamedPlugin, refreshInterval, err := SetRefreshInterval(testpath, oldPluginName, RefreshInterval{N: 1, Unit: "days"}) is.NoErr(err) is.Equal(renamedPlugin, newPluginName) is.Equal(refreshInterval, RefreshInterval{N: 1, Unit: "days"}) _, err = os.Stat(newPluginPath) is.NoErr(err) _, err = os.Stat(newPluginVarsPath) is.NoErr(err) }) t.Run("disabled plugin", func(t *testing.T) { var ( testpath = filepath.Join(baseTestPath, "single-file-plugin") oldPluginName = "set-refresh-interval.1m.sh" + disabledPluginExtension oldPluginPath = filepath.Join(testpath, oldPluginName) newPluginName = "set-refresh-interval.1d.sh" + disabledPluginExtension newPluginPath = filepath.Join(testpath, newPluginName) ) err := os.MkdirAll(testpath, 0777) is.NoErr(err) t.Cleanup(func() { os.RemoveAll(testpath) }) _, err = os.Create(oldPluginPath) is.NoErr(err) renamedPlugin, refreshInterval, err := SetRefreshInterval(testpath, oldPluginName, RefreshInterval{N: 1, Unit: "days"}) is.NoErr(err) is.Equal(renamedPlugin, newPluginName) is.Equal(refreshInterval, RefreshInterval{N: 1, Unit: "days"}) _, err = os.Stat(newPluginPath) is.NoErr(err) }) t.Run("plugin file does not exist", func(t *testing.T) { var ( testpath = filepath.Join(baseTestPath, "plugin-does-not-exist") oldPluginName = "set-refresh-interval.1m.sh" ) err := os.MkdirAll(testpath, 0777) is.NoErr(err) t.Cleanup(func() { os.RemoveAll(testpath) }) // Do not create plugin file here. _, _, err = SetRefreshInterval(testpath, oldPluginName, RefreshInterval{N: 1, Unit: "hours"}) is.True(err != nil) }) t.Run("bad refresh interval", func(t *testing.T) { var ( testpath = filepath.Join(baseTestPath, "bad-refresh-interval") oldPluginName = "set-refresh-interval.1m.sh" pluginFile = filepath.Join(testpath, oldPluginName) ) err := os.MkdirAll(testpath, 0777) is.NoErr(err) t.Cleanup(func() { os.RemoveAll(testpath) }) _, err = os.Create(pluginFile) is.NoErr(err) renamedPlugin, refreshInterval, err := SetRefreshInterval(testpath, oldPluginName, RefreshInterval{}) is.Equal(renamedPlugin, "") is.Equal(refreshInterval, RefreshInterval{}) is.Equal(err.Error(), errors.Errorf("invalid refresh interval: bad interval value: 0").Error()) }) } func TestParseFilenameInterval(t *testing.T) { is := is.New(t) for filename, expected := range map[string]time.Duration{ "": 1 * time.Minute, // default "/path/to/file.sh": 1 * time.Minute, // default "/path/to/file.500ms.sh": 500 * time.Millisecond, // default "/path/to/file.1s.sh": 1 * time.Second, "/path/to/file.2m.sh": 2 * time.Minute, "/path/to/file.3h.sh": 3 * time.Hour, "/path/to/file.1d.sh": 24 * time.Hour, "/path/to/file.2d.sh": 48 * time.Hour, "/path/to/file.5s.sh.off": 5 * time.Second, } { t.Run(filename, func(t *testing.T) { actual, err := ParseFilenameInterval(filename) is.NoErr(err) is.Equal(expected, actual.Duration()) }) } _, err := ParseFilenameInterval("bad.VALd.sh") is.True(err != nil) is.Equal(err.Error(), "bad interval value: VAL (from bad.VALd.sh)") _, err = ParseFilenameInterval("bad.10p.sh") is.True(err != nil) is.Equal(err.Error(), "bad interval unit: p (from bad.10p.sh)") } func TestValidateRefreshInterval(t *testing.T) { is := is.New(t) // Valid refresh intervals. for _, ri := range []RefreshInterval{ {N: 1, Unit: "hours"}, {N: 42, Unit: "days"}, {N: 22, Unit: "minutes"}, {N: 3, Unit: "seconds"}, {N: 500, Unit: "milliseconds"}, } { err := validateRefreshInterval(ri) is.NoErr(err) } // Invalid refresh intervals. for ri, expectedErr := range map[RefreshInterval]error{ {N: -10, Unit: "days"}: errors.New("bad interval value: -10"), // negative value {N: 0, Unit: "hours"}: errors.New("bad interval value: 0"), // zero value {N: 1, Unit: "q"}: errors.New("bad interval unit: q"), // invalid unit {N: 1, Unit: ""}: errors.New("bad interval unit: "), // empty unit } { err := validateRefreshInterval(ri) is.Equal(err.Error(), expectedErr.Error()) } } func TestRefreshIntervalString(t *testing.T) { is := is.New(t) for ri, expected := range map[RefreshInterval]string{ {N: 14, Unit: "days"}: "14d", {N: 22, Unit: "hours"}: "22h", {N: 10, Unit: "minutes"}: "10m", {N: 3, Unit: "seconds"}: "3s", {}: "", } { is.Equal(ri.String(), expected) } } ================================================ FILE: pkg/plugins/testdata/broken-plugins/broken.1m.sh ================================================ #!/bin/bash >&2 echo "an error printed to stderr" exit 1 ================================================ FILE: pkg/plugins/testdata/broken-plugins/wont-quit.1m.sh ================================================ #!/bin/bash function ignore() { echo "ignoring signal (naughty script)" } trap ignore SIGINT while true do echo waiting... sleep 10 done ================================================ FILE: pkg/plugins/testdata/plugins/001-multiple.1s.sh ================================================ #!/usr/bin/env bash echo 'First instance!' ================================================ FILE: pkg/plugins/testdata/plugins/001-multiple.1s.sh.vars.json ================================================ { "variable": "yep" } ================================================ FILE: pkg/plugins/testdata/plugins/002-multiple.1s.sh ================================================ #!/usr/bin/env bash echo 'Second instance!' ================================================ FILE: pkg/plugins/testdata/plugins/dirs-should-be-ignored/.gitkeep ================================================ keep me around baby ================================================ FILE: pkg/plugins/testdata/plugins/expanded.1m.sh ================================================ #!/bin/bash # this plugin has cycle items and extended (dropdown) items. echo "extended: one" echo "extended: two" echo "extended: three" echo "---" echo "this is a longer text to display four | length=10" echo "five" echo "six" ================================================ FILE: pkg/plugins/testdata/plugins/expanded.1m.sh.off ================================================ #!/bin/bash # this plugin is disabled and should be ignored ================================================ FILE: pkg/plugins/testdata/plugins/expanded.1s.sh ================================================ #!/bin/bash # this plugin has cycle items and extended (dropdown) items. # string(VAR_FIRST): The first value to show. echo "one" echo "two" echo "three" echo "---" echo "four" echo "five" echo "six" ================================================ FILE: pkg/plugins/testdata/plugins/params.3s.sh ================================================ #!/bin/bash # this plugin has cycle items only. echo "one | color=red" echo "two | color=\"blue\"" echo "three | href=https://xbarapp.com" ================================================ FILE: pkg/plugins/testdata/plugins/simple.1m.sh ================================================ #!/bin/bash # this plugin has cycle items only. echo "one" echo "two" echo "three" echo "---" ================================================ FILE: pkg/plugins/testdata/plugins/simple.1m.sh.disabled ================================================ #!/bin/bash # this plugin has cycle items only. echo "one" echo "two" echo "three" echo "---" ================================================ FILE: pkg/plugins/testdata/plugins/simple.1s.sh ================================================ #!/bin/bash # this plugin has cycle items only. echo "one" echo "two" echo "three" ================================================ FILE: pkg/plugins/testdata/stub-api/currency-tracker.1h.py.json ================================================ { "plugin": { "files": [ { "path": "Finance/currency-tracker.1h.py", "filename": "currency-tracker.1h.py", "content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n# \u003cbitbar.title\u003eCurrency Tracker\u003c/bitbar.title\u003e\n# \u003cbitbar.version\u003e1.0\u003c/bitbar.version\u003e\n# \u003cbitbar.author\u003eMaxime Bertheau\u003c/bitbar.author\u003e\n# \u003cbitbar.author.github\u003emaxoumime\u003c/bitbar.author.github\u003e\n# \u003cbitbar.desc\u003eKeep an eye on the currencies you choose from your menu bar !\u003c/bitbar.desc\u003e\n# \u003cbitbar.image\u003ehttps://nothingreally.botler.me/bitbar.currency-tracker.png\u003c/bitbar.image\u003e\n\nimport urllib2\nimport json\n\n# Write here the currencies you want to see\n\n# Base comparaison currency\ncurrFrom = \"EUR\"\n# Array of tracked currencies\ncurrTo = [\"CAD\", \"USD\"]\n\nurlParamTo = currTo[0]\nif len(currTo) \u003e 1:\n urlParamTo = \",\".join(currTo)\n\nurl = \"https://api.exchangeratesapi.io/latest?base=\" + currFrom + \"\u0026symbols=\" + urlParamTo\n\nresult = urllib2.urlopen(url).read()\n\njsonCurr = json.loads(result)\n\nrates = jsonCurr[\"rates\"]\nkeys = rates.keys()\n\nfor key in reversed(keys):\n\t# round down to four decimals\n\tprint key + \": \" + (\"%.4f\" % rates[key])\n\nprint \"---\"\nprint \"From: \" + currFrom\n" } ], "path": "Finance/currency-tracker.1h.py", "filename": "currency-tracker.1h.py", "dir": "Finance", "docsPlugin": "Finance/currency-tracker.1h.py.html", "docsCategory": "Finance.html", "pathSegments": [ "Finance" ], "categoryPathSegments": [ { "path": "Finance", "text": "Finance", "isLast": true } ], "title": "Currency Tracker", "version": "1.0", "author": "Maxime Bertheau", "authors": [ { "name": "Maxime Bertheau", "githubUsername": "maxoumime", "imageURL": "https://avatars.githubusercontent.com/u/3897798?v=4", "bio": "Android Engineer @ FrontApp", "primary": true } ], "desc": "Keep an eye on the currencies you choose from your menu bar !", "imageURL": "https://nothingreally.botler.me/bitbar.currency-tracker.png", "dependencies": null, "aboutURL": "", "lastUpdated": "2021-02-12T14:39:48.662249Z", "preferences": null } } ================================================ FILE: pkg/plugins/testdata/token-too-long/jma.1h.sh ================================================ #!/usr/bin/env ruby # Japan Weather # 0.7 # tsurezuregusa # Display local weather in Japan # ruby >= 2.4, activesupport (gem), nokogiri (gem), faraday (gem), rmagick (gem), nkf (gem) require 'open-uri' require 'faraday' require 'time' require 'json' require 'nkf' require 'base64' require 'rubygems' require 'active_support' require 'active_support/core_ext/numeric' require 'nokogiri' require 'rmagick' $place = '東京都渋谷区' # 表示のみ $latlon = '35.6895,139.6917' # API対応 # APIはひとつだけで充分だが、どれもない場合は気象庁のデータのみ使用 # 信頼度が低いようで、観測地点が曖昧なため、現在は補足的 # darkskyは終了する予定で、ほかのなかでclimacellが良さそう $darkskyapi = nil $openweatherapi = nil $openweatherloc = '1850147' # 東京 $visualcrossingapi = nil $climacellapi = nil # https://www.jma.go.jp/bosai/#area_type=offices&area_code=****** $pref = '130000' # 東京都 # area.txtを参照 $area = '130010' # 東京地方 # $local,$quakearea = https://www.jma.go.jp/bosai/#area_type=class20s&area_code=******* $local = '1311300' # 渋谷区 $quakearea = '1310100' # 千代田区 # 地震観測は、中央観測地点が良さそうだ # https://www.jma.go.jp/bosai/map.html#elem=temp&contents=amedas 「この地点の感触表を見る」 # $amedas = amdno=***** $amedas = '44132' # 東京都(千代田区) # 衛星写真 # 可視 => 'B03/ALBD', 赤外 => 'B13/TBB', 蒸気 => 'B08/TBB', true color => 'REP/ETC', 雲頂強調 => 'SND/ETC' $sattype = 'B13/TBB' # 衛星写真座標 # satmap-key.pngを参照 # 下記は本州、首都圏を中心に $satxa = 27 $satxb = 29 $satya = 11 $satyb = 13 # レーダー座標 # radmap-key.pngを参照 # 下記は首都圏 $radxa = 226 $radxb = 227 $radya = 100 $radyb = 101 ### satmap64とradmap64に、mapgen.rbの出力を satmap64 = "iVBORw0KGgoAAAANSUhEUgAAAwAAAAMACAMAAACkX/C8AAAJJmlDQ1BpY2MA AEiJlZVnUJNZF8fv8zzphUASQodQQ5EqJYCUEFoo0quoQOidUEVsiLgCK4qI NEWQRQEXXJUia0UUC4uCAhZ0gywCyrpxFVFBWXDfGZ33HT+8/5l7z2/+c+be c8/5cAEgiINlwct7YlK6wNvJjhkYFMwE3yiMn5bC8fR0A9/VuxEArcR7ut/P +a4IEZFp/OW4uLxy+SmCdACg7GXWzEpPWeGjy0wPj//CZ1dYsFzgMt9Y4eh/ eexLzr8s+pLj681dfhUKABwp+hsO/4b/c++KVDiC9NioyGymT3JUelaYIJKZ ttIJHpfL9BQkR8UmRH5T8P+V/B2lR2anr0RucsomQWx0TDrzfw41MjA0BF9n 8cbrS48hRv9/z2dFX73kegDYcwAg+7564ZUAdO4CQPrRV09tua+UfAA67vAz BJn/eqiVDQ0IgALoQAYoAlWgCXSBETADlsAWOAAX4AF8QRDYAPggBiQCAcgC uWAHKABFYB84CKpALWgATaAVnAad4Dy4Aq6D2+AuGAaPgRBMgpdABN6BBQiC sBAZokEykBKkDulARhAbsoYcIDfIGwqCQqFoKAnKgHKhnVARVApVQXVQE/QL dA66At2EBqGH0Dg0A/0NfYQRmATTYQVYA9aH2TAHdoV94fVwNJwK58D58F64 Aq6HT8Id8BX4NjwMC+GX8BwCECLCQJQRXYSNcBEPJBiJQgTIVqQQKUfqkVak G+lD7iFCZBb5gMKgaCgmShdliXJG+aH4qFTUVlQxqgp1AtWB6kXdQ42jRKjP aDJaHq2DtkDz0IHoaHQWugBdjm5Et6OvoYfRk+h3GAyGgWFhzDDOmCBMHGYz phhzGNOGuYwZxExg5rBYrAxWB2uF9cCGYdOxBdhK7EnsJewQdhL7HkfEKeGM cI64YFwSLg9XjmvGXcQN4aZwC3hxvDreAu+Bj8BvwpfgG/Dd+Dv4SfwCQYLA IlgRfAlxhB2ECkIr4RphjPCGSCSqEM2JXsRY4nZiBfEU8QZxnPiBRCVpk7ik EFIGaS/pOOky6SHpDZlM1iDbkoPJ6eS95CbyVfJT8nsxmpieGE8sQmybWLVY h9iQ2CsKnqJO4VA2UHIo5ZQzlDuUWXG8uIY4VzxMfKt4tfg58VHxOQmahKGE h0SiRLFEs8RNiWkqlqpBdaBGUPOpx6hXqRM0hKZK49L4tJ20Bto12iQdQ2fR efQ4ehH9Z/oAXSRJlTSW9JfMlqyWvCApZCAMDQaPkcAoYZxmjDA+SilIcaQi pfZItUoNSc1Ly0nbSkdKF0q3SQ9Lf5RhyjjIxMvsl+mUeSKLktWW9ZLNkj0i e012Vo4uZynHlyuUOy33SB6W15b3lt8sf0y+X35OQVHBSSFFoVLhqsKsIkPR VjFOsUzxouKMEk3JWilWqUzpktILpiSTw0xgVjB7mSJleWVn5QzlOuUB5QUV loqfSp5Km8oTVYIqWzVKtUy1R1WkpqTmrpar1qL2SB2vzlaPUT+k3qc+r8HS CNDYrdGpMc2SZvFYOawW1pgmWdNGM1WzXvO+FkaLrRWvdVjrrjasbaIdo12t fUcH1jHVidU5rDO4Cr3KfFXSqvpVo7okXY5upm6L7rgeQ89NL0+vU++Vvpp+ sP5+/T79zwYmBgkGDQaPDamGLoZ5ht2GfxtpG/GNqo3uryavdly9bXXX6tfG OsaRxkeMH5jQTNxNdpv0mHwyNTMVmLaazpipmYWa1ZiNsulsT3Yx+4Y52tzO fJv5efMPFqYW6RanLf6y1LWMt2y2nF7DWhO5pmHNhJWKVZhVnZXQmmkdan3U WmijbBNmU2/zzFbVNsK20XaKo8WJ45zkvLIzsBPYtdvNcy24W7iX7RF7J/tC +wEHqoOfQ5XDU0cVx2jHFkeRk4nTZqfLzmhnV+f9zqM8BR6f18QTuZi5bHHp dSW5+rhWuT5z03YTuHW7w+4u7gfcx9aqr01a2+kBPHgeBzyeeLI8Uz1/9cJ4 eXpVez33NvTO9e7zofls9Gn2eedr51vi+9hP0y/Dr8ef4h/i3+Q/H2AfUBog DNQP3BJ4O0g2KDaoKxgb7B/cGDy3zmHdwXWTISYhBSEj61nrs9ff3CC7IWHD hY2UjWEbz4SiQwNCm0MXwzzC6sPmwnnhNeEiPpd/iP8ywjaiLGIm0iqyNHIq yiqqNGo62ir6QPRMjE1MecxsLDe2KvZ1nHNcbdx8vEf88filhICEtkRcYmji uSRqUnxSb7JicnbyYIpOSkGKMNUi9WCqSOAqaEyD0tandaXTlz/F/gzNjF0Z 45nWmdWZ77P8s85kS2QnZfdv0t60Z9NUjmPOT5tRm/mbe3KVc3fkjm/hbKnb Cm0N39qzTXVb/rbJ7U7bT+wg7Ijf8VueQV5p3tudATu78xXyt+dP7HLa1VIg ViAoGN1tubv2B9QPsT8M7Fm9p3LP58KIwltFBkXlRYvF/OJbPxr+WPHj0t6o vQMlpiVH9mH2Je0b2W+z/0SpRGlO6cQB9wMdZcyywrK3BzcevFluXF57iHAo 45Cwwq2iq1Ktcl/lYlVM1XC1XXVbjXzNnpr5wxGHh47YHmmtVagtqv14NPbo gzqnuo56jfryY5hjmceeN/g39P3E/qmpUbaxqPHT8aTjwhPeJ3qbzJqamuWb S1rgloyWmZMhJ+/+bP9zV6tua10bo63oFDiVcerFL6G/jJx2Pd1zhn2m9az6 2Zp2WnthB9SxqUPUGdMp7ArqGjzncq6n27K7/Ve9X4+fVz5ffUHyQslFwsX8 i0uXci7NXU65PHsl+spEz8aex1cDr97v9eoduOZ67cZ1x+tX+zh9l25Y3Th/ 0+LmuVvsW523TW939Jv0t/9m8lv7gOlAxx2zO113ze92D64ZvDhkM3Tlnv29 6/d5928Prx0eHPEbeTAaMip8EPFg+mHCw9ePMh8tPN4+hh4rfCL+pPyp/NP6 37V+bxOaCi+M24/3P/N59niCP/Hyj7Q/Fifzn5Ofl08pTTVNG02fn3Gcufti 3YvJlykvF2YL/pT4s+aV5quzf9n+1S8KFE2+Frxe+rv4jcyb42+N3/bMec49 fZf4bmG+8L3M+xMf2B/6PgZ8nFrIWsQuVnzS+tT92fXz2FLi0tI/QiyQvpNz TVQAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpR PAAAAY9QTFRF//////8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A //8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A//8A ////WY5TeQAAAIN0Uk5TAGZ3M0QRme7MiCKq3VW7n2n5wd/I779OxPd6j+Kv +LaS/IDz1tfj6sC3ayD0jaPereuEgujwl3WO2rXNp/LnW6TR0kBL/TD6w1Cb x7T7zz9g9fFwPent3NTg7P7TxcalXInmssLhpvbZq0zbypSMitWwkNizvpW5 rOV7qXOclryduH3MSNARAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAASAAAAEgA RslrPgAAAAd0SU1FB+UCGRcjB+QoPNMAAAp4elRYdFJhdyBwcm9maWxlIHR5 cGUgaWNjAABYhZ2XbZJkqQ1F/7MKL4FPCS0HBIrw/jfgQ1Z3T7fdM2M7K4jM eg+EkK7uFemf7ukffOpQSfl9Vsk/PvXrtxy52rWO2rXXmsccNlbNf/ZJUqRp 09zLyCN3/9OZf/EJdn0e/XDntHr+H0Mfj/7H+Uu6DG3Svvny7QTqiYNlrdo/ /xedXy/qVCVC+fvz9c1O6VJl//F89+/PE+H8hPHrf58/FujPz4//Yejn55F/ NtTJzJerNX/tMLNj6PfP/2R++vXM3z+19vIOLCbC+0n6m4QIx1XhjYyatX2H RrfeUm9MIkpvGstcxu+j/Hem03/a/m9N//r52/TrO/r8liU+HT/ImrGRiQpv 2P559NdxaLXlVhqQ/V2JRBsPzOW7R6WI+9/V0vfP7+ZJrPo5WpvxFYdmv59Y zd731vmZV+ZZ/z7vY6j2b8iN+YnFFg8QP8iS/GLxR6TyM/gWxQMjwzBULqba KwsGRUvSMnWT2/5a2Vn0duosGjwYeDdY87An8xEOKXlHU31FBzwJ2sSDiYF3 EsOAYcAwsDCweLgwsOs7JIM1zhpnjW8MPeY4LDoYYF6+/L774zVMw9jk5f0p 45CaxjBG5FIHg/cNj0qbjMtReQixFbJecL9gowiLhXfKO7wuEyN4XIw5eFug 1oKnZRcMbX7gYnG+D9+H78v35TtYEIvibAyHhjvjUC/CCEI7yVRhcDSqmcFL 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 e85TBYsWRH+lcAGAnGDmQzMvfH7xh4b8M8iCgUwJhBwwyJgKFaAAAAB6Qji0 VYLzYwdbvU8EpwKcHTHQ6nNbPo+6OpAvDurSIGcCyWs/BQCuh0BSGQAAAAAA AAAAAAAAAAAAYwajZEHWeJR7gJyxqHcFAAAAAAAAAAAAAAAAAAAAAAAAzsz9 9679DAC4HuI9GqhSFgDn5z4ZD0VCkC1EdI8CdsCFEBLX7QBg69EWD/3/S4EU +jQugyG69lMAafT1xnCL+xQcEXbARTAKY0UGgHjzp+t/PHqRgsHNDDLCQIgZ 5Ei50F+uAmaxgPxg8MBAzkgo8IP8KOuQpzDCkbLXfjKHEQN+bmDEFJQGT1hN Xg265sGoaz8DMEnoJcNmM04UDA15BgtuAHAW7ipFX91hJZmKMIURZMcHM+Ff pP/v67eIYGSA/HC8oufMz9VXZsgGEAA1svcx7oro3sNHg50OnyEVyoEO4/pP 0YryA3oK62c4BH/tZ5AbrGIIsQwHiz8GAOBCMHR7gZxB8BNkjRly/QMAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AADn5/8H/voT9+kK5zsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMjVU MjM6MzU6MDcrMDA6MDAnrXK6AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAy LTI1VDIzOjM1OjA3KzAwOjAwVvDKBgAAAC10RVh0aWNjOmNvcHlyaWdodABD b3B5cmlnaHQgQXJ0aWZleCBTb2Z0d2FyZSAyMDExCLrFtAAAADF0RVh0aWNj OmRlc2NyaXB0aW9uAEFydGlmZXggU29mdHdhcmUgc1JHQiBJQ0MgUHJvZmls ZRMMAYYAAAAldEVYdHBzOkhpUmVzQm91bmRpbmdCb3gAMjU2eDI1Nis2OTEy LTMwNzLw/q5oAAAAAElFTkSuQmCC" radmap64 = "iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAAAAADRE4smAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAw0DJTId9b2iAAAB fXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAKJGNU1tuxCAM/OcUPYLfJsdJ IEi9/wVqAqyy292qI0WBMfgxNum7lPTVIW4JOrAWK8ZWnUEuBqza6eKkJC5E oFk33QnATw1zLJDj32J59m0yNHZ2EFRQkAITr/u/0CJqzwgXUZnqyiy8i9ed Uaso7HIwyXEQeUYspf5ylv4T8YbdxNTZeOYyM+YoLcpy8qEM0jQQu4dCsHid frCrdqk1tiaLT+Z3g+dlsKcLudwcSTRgZIT7CtAzsmzLMx7TQMU+XIjAHxyF vK6r5gdWA166hquU4HtHo/o+CSC9a6qTLJOM8iSGJ0TNU6POsrXQbHOyzdzC QjEiwUD0WWOOZLPTmE4qBEzcvyubxwDe0LjmW7aIBnmde2q/4xCXd9mGVFbf OTQYXTxyW8pxencw3sXQphQfQmmNEjDKQvgNbAaSYriMme+eEI7jcgC6Wetg HXunK9NWq198nLv+dZ+Plus+IjdLPz58z3tAMmEwAAAAEGNhTnYAAAEAAAAB AAAAAAAAAAAAIfut5AAAT7hJREFUeNrtvXd8XNd5JvxOw8xg0GZQBr333oje CIIAeydNiaIlWiVy1tlkNxtH+22+35asE2ed+jnr2IqLbFmyuih2UuwVLCAA ovdB78AUYABM/f5An3rLuQUEHv7BwZ1zzz333mfec85bAehFfAXNF0QK+es8 poewCsE7KHrh0jxq+QDNF0SK0d5spoewCq9pFL3QTQC/IZoviBY1GZ5MD2EF XkoUvdBMAKGnmt4LIoamJovpIaxAz0fRC80E8Bij93qo4ZP6jOkhrEDngqIX mgnACfKl94JowdvxLXsk2IYkwNjtMrpXHShRPNTH9BBWsSEJAC0C9qyicCPB 9z7TQ1iDjUmAqCkkmxdGIC26a2Z6DGugE6DoBclKEgfybtJ8QWQQJuZWjzI9 iLUwmXlG8r3QTIDkyWF6L4gM8QWdH8wzPYj10LnMke+EZgKkbVQBEBb6zSTT Y7AEEgLQuwZI2LACQGhk3ftHswqklwBp9bReDiGEs0yPwBo6MYJOaCWAi2Sj CgAQaZkegTV6YhF0QisBfDauIjiOhVbM9hgh+U5oJUDgCJ1XQ4nkySmmh2AN XTsCEUArAfw3rARIZeXipS2GfB+0EkC+UQmQqGSjG0PMnlbyndCpB/CcRbBv ZQSpbLIBLCMt6XMEanU6CbBhBUD8DPuWgIIdhi8XEPRDJwE27BowsInpEVgj ZuEWkn7oXAP4s8qWggPtKUyPwBqpCOZ/AIilkQAubuzTpmLDgAfrnBhCdWh0 asU0EkC2YdWA0B7P9AgsEY1GAJwMpZEAApZZU3GgPYHpEVggLQgNAbL0NBJg 3o2+ayGGciKC6SGsQ0rcOROKfpKmNDQSYMGVvmuhBstEQMRjFZJ+PHs+o1MC bGACiNxFTA9hDQRyRM7J7io6t4E6LhIvRiYQl3qVTQuYkF5Ezqmealr1AFoU DgxMIKL4mpLpMaxFNKroBLoJsEHngMBdF9ilxA7rRdTRFgEwofgKu3TY8lFU RjUPHa0EmNuYBMgZ72F6COvhhsw5xYNeW8DGlAA+qdVMD8ECYlQCwHMmMpRO a6DWj8aLIUPePbb5g6IjQH0Il1YCuHqi0V/QiURuG9NDsIQrqvDKvn56/QGU U4cm2jppvCACCPMuMD0EK7iicAQBAOD9PQCdWa/mB+ohIV80g2r8dCBvjHUC ANK6EUWpmHfPXefQPXppQtxQR7etb4IzZcAxA6dbMWige1T2EFr4B6aHYI3X zqFKUyLZo6SdAAAQk+j+ldXCKibVVDcEZjBzo8LCuvq62MGBEw8GmR4CAAD4 hi40Ln8OKf4YUa/xrz+ppjs/AABAR8dO/24AgJzRviWldnzKQo1i6evmZkFI FJ8VbnjZE2x4/6Fh4TpFxkpcbdYjVB33c6tpTxCxiIGg+WEzxPv77+rpGdRC Ypr68Vr1tr5bn8MGAnhnsGEC8C9q+WoWBks/X0wHEWNCppea1Zg5zKQ+5Upj S4M8Eh/UNBj8CxJTuTU1FvtDderUDCMjW4fy5n4Gr+7FMZkBADK6G/QAaq9I BQAAZ889dE8mckHNjAQYGwOBNEI5Arru7gdePBvOos1xzHsQJvBfMHn5zGCx Tqud0yZ+AAAA94/Etbn5+koHEBom+kIGmCEAAOjHli1sSltfdxY81DE1tCW4 MKwC0D7qEEnEYtcXS9vme8dyOePjrRMILzGQ8ogxAjjBXGdcA8NDyG8dZ/T6 8zKYX+uGMv6NRoP4Ev0htKeJw4w2pj2x5Qnd5DshAyvb2RDq9w9zU1GsJYAG SRpEEhi9tYNJFzaX0Pxa6q/yWTxbpwDgIMiBRw5twp2XaLxc3pLVmePp6eXl 6SVQ0eGG1unCWgJwmU/K+cK3+B5tF5OHhDwc5MeEe3mplMqJDhVNSam8WUsA DpLIB3K4cYS+tGYhCuWOvlhFq1JJK/PZSwAe8xIA4PqRKbpUQSFPB3oTPqQ9 g4YPaxeBzK8BAEB9ucqbniuJvQdAV0/7+y+iMzgUH1iwBgCA0Ufl9MjICEas TjtKf8baKYANawAAaPIooT69sSA5eaKRfDe4IS35OYe1BOCygwDwqCIfmfnV NtwTUzuvMKJ0lE3RXy8AM9gxBQDA7aNqKk3Tsvikpt8zkz0tNnaSzQRgiQQA w43Dk5RFBnlny2t+y5DVK6W84zG9TqG4IJV1MT2EJWiV5Qqq/FjjjRfHmdru JOqf6uivGYQZPLZIAIDujhCqupYxmILYexKAxQRgyS5gcSxUdOoJAODNoNuL zyTA1hqAIfCiY9tUABwZc3UoRL4THGAxAThs2QVQgODo2C6FTzuAZJo5fefJ eg4AiwnAY1Fi6QV/lGqaoFJd+++1gSmS8TpvppYA/IIC1WIVZNbuAuR89mRo H0rlIVTUFLbdGdWDZsDgm86ZZEQBxNv3huHqkscdWxeBPilsmgLuFcuQ9eXn 2wwAANNttzuTmIk8Nz6Yb15efLBUAmSVPGRDZMgy5hbSkcWI5vauqJXGRtTM BMqmBawUQGAlAdzK3M8z65FribHwQDWaRHFe+d+u7m8YCpQOfPvXK4tPNhIg 6GDzI3bEhq5iyGe7uwbFunTbMJPRRot48+bqz4uFBChMucmwQ7YNGAYb3bfL Zslv23PrGS9BmS59svoH67aBPoWazyjXAUncDLhrF+jqG1OO/GYBwEXiKnF1 1U1OEorSl6GM7CGGubUJO9lGANHxq1T+/GVufm4ebm4LM15dtbjzFRnDxktc Ja4wO6udnRUmyISTk+uCh7gC51O6l5p5DaeWzQTgaKh8/ymRGs1o14zGAIL0 Ey11eMNsv8lWzWq1K9Zbkcx7b/czLQC4eXp5eXl6fOqcAFIWFKDs1/mu8pZ1 BKAyZYkg+8Lyneuf1qWdftqCcz5+tu6v+aGh1uxTdRJvqU41rRpQbcNQyZVJ 898K5iTsJYCZSs1UZtsaia1/1pp+6nm9nlSP+kcN0X1NqkWhoPZ17jji00Hh /WHF2pStrNMEUjggr7S6dX/P3P9E8no2yX3QTF33+NKkoHF33lyGKscfGWjX FD9gGwFMFE4BGQ8sNa/qO594HAuw2bhkH+4KNxrnZ/Dd2UAANkuABS06rbsF ggNsaJdnbtbsXyzBXBi85rD7Ps7wYbyZbceDnHoOyVhROW9t4DnrFEHuHlQ5 yZQ22nz6U4ptQa5SN5+Q4AIZT7O4SQve2/hseGaXAV+lS4OquM2Jfd9HpKDo 7vDAL3TVssE6AhjTmqnpON7noe0v5lr93GXyoOqaZm70drlwTgdJZXc6AKY7 cvzwVWZQimIdbmJ987K72FA9VRq3KguZSBTpGKfPKynp99QdDBWgeWFhobMj 8huLQ+Bsd72OzwRUNVFj97uQBL/GBjZEPEL4Kx+s3jHTg7GCpyslXviZeiwp v8zTinql+81lq08Pd/cULoXhSJnSTvugitD2WyPscHJwKVxVaLCPADOFZgoq 9Eh2XcMaf6EZWH1NE9M7AA8f9dPb220ZMpPLfZ8/YsUCEAAAKlZrYLCPAHOd JUb0BpP8IWIqZlVPhgyP/VYlSLRKiC9Mr4QHdagSPCOAfu+Dlc9s2wYCgPZC UhbqPv0iawieqTrLO+KBo31NqGV2q8zvCi99y55fPwDAGscG9kkAAJ1eiqow 3jKK2olvLvsEFUolxrZiybyXeZ2uJ6yKc7WTbUVn0kZWRsRGAkD6KOI5ICL8 NomzRydKeQ7445oCK2ZF332Rev92AADvQo9JE7gWJT17xr4CGQFeK/koWEmA bc2IfzIVj0nNwOqO9CCFne+Cc7ZP5RiXl62+0OgXHivl87ILe6SVQte9Q1fZ JfwXIUpbUQSwzRoIAMBD7TWTrCXph6c9V3z0pg1DrjAu3vzitqGhQnp38YC/ sr8fvINiw198qIPquOiv2WD9tUbHaysf2SgBlj3nkWH3PdJ+eH2cSpWlHScg e+d8bfWkCRZaYxL6jN7ZKdvCXqgA5kY70y/pAfQjHSwKb1oLXVbHshmcjRLA JEEbGSpdQKB/bZqqKNTOaOfmjVwOl8vlAjfIpe23Sy/YfD3nyMUUdZ+KBQ5f mNAbtawWYyMBxgfSkKbJDUISZDb8kdhVLBZ7cM1Gs9FkMJkfr03s9WT8pOm3 5JxL6ERPGpsJAE+PNqJ8lkFo3AxNDp3Ce86HbZz3Dx37lz+xUBEEoG5LR9ld IC05+IaZrm+ABxO65RfPJgKceT1m0TiZHOkmItnXWtBkgVsrH6a96LkmYfRG L31g0y5gTuJezJ80JlYKHjaiDA1L6qA9D1fAAjv3fyvwiljKwcWmNUBb/jl9 5hvTM7dRi2z6xdyUJ62XCx/DqznrLl36wCYCQGvcozs1IuSmQD39Yk4ZRevl irapx8Y+xXNG33IMA5vWANAaDzCD/P1LTfTfpJpeCTBw92cXCt7AdUrfkgMr qwigHElA3mfw3oM99Meaz3jRernBEpj/ccYpAQAEVWE7RRG5+D+bFoEAxqRW xD16lzTdHEaT2gHXjSRM0+kAospUzMP976QMCF8/GBPWheV+ufmLxVDYRQBl 5iBi7bmHP8W5vu1An4wspwwGmJVv1+vg/sybBd9cu+99bBKD6nvyyKKTNLsI APxQJGk4VuHlzUzK4YmUOSWNlxsde3tkCqabRnoB+tve6cKwCU0Y1QKwjgCa 0LS8AC+xCYkPhVcoT+stZSjbyHxmC52XG27Zufc+zI8DACx0H6h2bpQKkA4C sI4A+p7GRrUkJJNPPjwoLCdrLqXEY1bBzJ1MR+uVdF5P9cj7O8upvzS7PJ3r pQWZTQAs0wMAAIBuaAhCM54TPp/D50lcBJJEaPoWgOcnZ+o+aot76L1gdc7K xx+/1/7MWfPOMwDARgIAAPTvEhKdBE6563Q6vU73tA8AwDjMWDrugalUmqvP j7334+WPv6hwSoC5YfkosG4KWIYUCOrSQ7w+rn3R1NrRjTsDEHKoShpoDQSa fiB6bXjJa2n+QLvTOHRBRhuwlgCcUILyc2cd829+CVp/o5LWC5pbW4sPPl20 fY4nORU//ccecFlLgIWCOkLnhQYzs+23CbEv3TkhlU81pzkDAAARaqfbX7Ms eIBlquBVaGeIVexMeMr0yNdgbO0ClKZf2oO/cX9PDAAVGLj3MJu+ceGGuxuR GGF5Cn3lvp1jtrBhaTueUJ6T00mPQnq+XnrsxQIM5jpdBYI6YV7JVgkQntRJ 4CzXZCbqb9rHkP/Sh5RHf3icSddV3X6lBuhJeMN5y8c5bJUA3ntu4AwRl4bE puanLDxkRwD+EiR+Jk8PN4lflviRYSRPjXJ5Gu6ltPeV+t0hlRnuH5c5VQYN 7W40sC9DCADwjzRiDw1x8fb29vbWTE1NTGqYHrgF/HLMYDbref76jwGC875A 1nFGSSRMf11v59v0uNyLzQDv/ZtT0+p+j5usVAQZ+jFk3FuC8MjCxGT7JEPV Nx1j7MLSh4NJTTCgRacY8r0+Be7/4ed2rA11kbxmAPg01ykB7v/VTXZOASMl M0qMTXf33+gdm2FF6h37GK9sMYByexOqYSbDFOjaM+2IgFcLfwwAoDz+xJll dT5EwM5FoOFBgQBby0I1mzZ+9jBVnwcw3pCLqr/eYwAwEQKy+CVReTp59cuK 3GWNsPPrVeewUwKAMq8WU5RdRuBVpoeKCUMZ5kmYKFUg2gqOFD4FMFelHNhW nh/r3yF+OzUr30urAu8I41zmib9bavXowD1na+LxYlauAQDc5zH58YWnfM70 SDHi8U7Fgu5p4UVE3bkAAPxYoAcAz+8HeQb/GDhpf97nLQHQufzf5bdulL7z c2cMeMLKXQBAcOY5DK38Dl2kJeoLBfJEtwGONCBKFv6PP1nzR4j/4jQomQUA EK2RMu81ve+kIyFLp4AQHoYcncI99/Fl8mQSg3naadBUDqPZqqZo1xj71EvR z4vBqWtF5/0jfvWOOzKylAAxagx+jbsG2aX4cwxNQZNZPV3VjyT7zeBb2FRe 90/0Oam/x1ICpCice1UXm+9j6Ik1UMkCBmBatWsEhQxQwRvYbr5zpxPzKDu3 geBpx51BsLpoTfe5wfQo8eFhTAhA1+09/uS7ArimSsbUbiSs1HEDdkoATlG1 DQmXn5aXmeWm17i4+wRFxcdeZKX2zz5MytxmgKnxPcN4i1XZgLk38tD2VgzT yeNXHjlMXMFOAkhDbGlN82vrqhu4SWXJQd6C+clW5gvw4YQqUDAJoFZXomDA dPON8aKDwwIPL8dTimm7wKF/Ojv1AJ4quY1FoPvIAug6O/lsKyuLGTo+AECn ec9ZJMkDa2qi840mwyG3j/q93oW/M/ODjOPWvrQ/fu++I8sqOyWAklfmMW0p 4QVpiz4OGyQRlw0EGkcBAKZnqlDIAACYetHQ2HxzMO2Y36WeN4vCOV5HH1tP nfoIR6ZVdkoAaO1Mfe35s/W2Ezc0D41BGJeedgfsuYiwckhDg2geoHdICzD+ F39nxYDH791w4CHMTgkAYBpuDywzrZVdkqpOSipJ0Ai5YElxOTVRNYSyhrQB AKb0AKDQv/nEyuRYZHIQqMpWAgAs9I3Gp61Gdgv3dCFNHsgE/ETLrprquW3U hA72KN9psFwH3D9+077WiKV6AAAAGLnUsn35s2B3/4Z//6BfnXDbRD7UXKP6 N9+3OibaZr89mwkA0KRcMmlzd48+IdcVG2Bcs+LqCafmGr6J1sd+kWO/PbsJ AHfCYwEAYM8oiwI+CMOwhgDdkVRcwePwX2l+bHV0OjrY7hnsXQMAAIB5qmpu 2gQ7tQ/J98U8NGWdK/PzTOoI8joivF1non5sKzHynIfdSDuWEwBmhqLKRFna O0yPAwlMYp/V2oUSGXJfBrMq+/0lLRnnvaInaxQm4c32loFsJwDMdLeny75m ehSobqakfuVF6LPQG7O1gz8omjRywUv+7s3mXVUjy/t/3fGqWDv5t1nqEbQO BXMbfwOwhMqu1ZjNysCmRuQFJbxDQ2UigUat/hYgJjvv5tNlwr039GubngH0 EMDLn0z6t311GIq+bgwEF67J5ylNSaxvozSnsPfO/PeXL5Cd9r9tNaFnCsjz KxLPEvaIlcTTmXKNUqhj51YDxOZ7X/gW+81SqOGec0m/vvx5qOqOLSsaLQQI yPi6x7co0KAkdvpIqO+G8f10BnPs2qhX41A9pyBqgLpKE8PBRx4sC/miIVvp cmghQGnDxPxAnT4zyUzMhj9QNMfy7OuYMZk/tH73N9WYMEBh4Phz4emiJeex GYGtvSAdBIgKvgcAoGyZicvTKwlYc43Du7rpT/dKDUxhlp7MkRNUmjnbwi/z FoADAG/4mG04UdNAAJfS2qXtiKpjJLKcr8LvyqU1ZqBOIswUlFWWxTDCZpzm cyKB8H1XdH+ZUTY/muDfL45pt1IHUE8A+d6elTKVMKvo8K9wn8GtAxsN9ntJ dgJGiafFVBwICH0DrKB8GviDc798EXRANzzzRey2MdqdKqLejbM44pL5XfxR kvzvxNI9coogO21xwP1QKaGOMCMbAACC33IHyH+nwOJLyiVAUrPlHs447N+n xNuNaWjXS6INmgtxWa+R0bVGplK4EQBYtA7s7ekAGBjLCl4fnUa5NTDAhv4p AGf+FwAAOeqK8ozhbm6gxZFbg0cCqL5qIfceAED/712+G7T2ONUEEHpaO8B6 GQi4Q4W+NATQ3CuxfOo1d/YlEuoLM1zjl/wpTF+37Mlb8wXVU0Co2Doe1ldM IIX79vsb1hvcElOekQqLQ6q+PHeqVrnuOgBId11JoDekzghtX/mWagIU91sv cZOUq8diPd2EAi44jXQM8dpIgaBO0J9tVU1C254YraDE4Z37HfmgAYbyJld0 0FPNUVnTy+EkFBuD4qMvWB88Ur3itFDKE7jwhC4uLjqdbmHBpJu15/hVOEc8 gTz74Hf4c2vVZn7ITSqCnQ5rXb077plKPNa+ibLYalpSmXt/X2Z9kHsie/lj wtFlArqI3H2DIqJ37XWz2ZH4DWKpY9mK1IM2Dka8kyZFfSGP8lMAIQfPFEve kaw9HvfG3sUP1EqA/d1NNo6K9nQu0i9w71eWS8SkbbcVFoeCfH39hIK5zmYl pWOlFxUz1dYHAyIiF7pbyQcMBGf7mUxGowGELqC7MAkAAcVi/cS1tW2ElR43 RoBiAqQHX7B5XLRr7KkeQHD8qfUKMSLh0voDqZHj42NKkEZHG7raUEZTMAqX 71y3WcoiIDJS+4xc3pOoTHfFfb5I5CLga6ZWdK4J2ZKfr29XHln3jFoCuL/2 oR3Fo2BX4NTQVKjahqtvJqyf7OOyv16+Ce+Y+OnOTsylROTh0+1Y29KP8JIv 7CjEQ0r6HxHXCyUni7vuYmsaV9h9m9JdQEWTvZzlpvaaCZOf600bXyUNrEup 67/n0oqpZG6gbiFgp888thQbORkD0fFK1ooMpTDIztNRN/uWKYnmFc7LqruK VYBMtudzqYy2S99L5Kzj66ru8k5Zus/z4/Zj6ifiNT5A0vfyWev0aGUTWIOQ EztcCXVa8r0IPM2zTqOWAF7BK9sbWdVlIjb89blBolxqLL43CYOdOYhJJH7h ial3pgHGW8MKNEq0t4gKc7FKu6JM3eRXPj8BAOCF6xnuDryCS500nChCS4Ck 3TLx8h5/Zw0h1VZ8/9pbLm60koXuAQ5ndtHRwrhAN8NkywAAgEExmes3haQM JXII/RxU9RgczAyZXJDmFcrmsRtw97t/hXPqMKQhJcBO38tN6fLFKSjJg2Aw V9yaYje+Cda5sNwD+xwphYsmL9S29gxNLP+6NM2+lQZVsC/7fMrmch3pYmZb xXtdi/uvcnMTOeOY+uMf53yOV10+kYySALJtn88b2mKjes0A/pXniK1kxwrH 5N4+Em8/uVzuHzhmHeik8Sgcte9QEpB2xVKhOqRIKBZ5F4q0yJ3wyWEhbsrh j3u8x/Nan2myyT05TeJAmGYH8rUmAJAcnv0G/yDMCNdInNcfdABA6XzgtzOh VbeI1HwBAMjwNZtNJqNRYDbPQ7OtVXx45XWDyWA0GUwGo6V72bHHduSqR0zS 5NCMdmaOPbnFsgUYhWRajPaSna8yE2HB1cUwNzMV23cTW2/rgZAAueFLQQ+Z Cfd2X6EyiWtQHI/P4/P4fL7+2/XiMSXoiv3TYrzcJBJ3k0ZRzw4SxId8i7Gl 4Pj0ZVvH4zKgpgMA/OUyr3FiaTPREYBzum55TksouEhTNpeEoutrfZ2D9n3u dK4XSeKSamrZUFuoshdzwIv4+NBKhEdwlptWo5wam0lK49U5LxLtBOgIkJ7w h5XPUir9XNchpKL+OQCAxH1BFxEZsPABlpO8sgKeYy9KRBU4b/0O+/ZEtq/v NgAAyAtlff1SDzdXoVnfgiBpBjoCvNrKiMHWK9vneTuElmlEor7u/jf/HZtN PSBL9LQXljOsM4PwVCwp8Zfhv7ulGiAyw7OpZmmp7+E8nTIGICNATN6HSB8P doRum+/Purw45+xrxupsFJoz88KUFtbTSnd51xWUTuMyyQfsaZhJFTeirpCD bBu4o42pQu2qFmH4haU5xxSGtei0qtlUEtl5XZiWxuNKXF1FQqELh97SU3l1 uPalM5q8gNZLQ3hOwQJUiSL93etQDw07GlfcxYZLip5inVi7uwGgsVGWEMrh cnkcLkei6R/vpo8ELjgVlJ18NkdHHaxgegSLEBW+mUb4ZL/Uyh30DfUtjIXR NgR83nYj3wmioVRlkTj7YDpd4+S8S9eVHAJRXEDaIGsS+U5g05zbwe2sYDKn 44CQHSYqNARwjWRRGkczmXtS3S6haZh4lwAUAc0iMHWIRdUbzKR2Nl1p/mi1 mK5CH6lbRz8AgLef76q3lpAd+mgkBOAkXSDfCTKQNHB1JqIlQJFEo5rIKWoX +fiph9cM7WUiQCGWIm+0gdQUANDyvXvYDNlltzE164i6DvDCL1bTptInrskQ 7cIOAqBYA3CiWBW2RZIAekUMlma8cv9Dlse4HC6Pzxe4uAjXHBwKAwAYu18/ oQeeCSJ2iRePs2QRiEICZOmpyX1PEJMJ5M5vr/JsD5D29Tk0GAYX994stjy4 J9hsNpvBbAZjV8eyUFyYCl5x5+Amuvm2nLxqDKwFcGGDQRINAaJZ9f5hyBxA Sivd82FSgVKTtru/2b5aOTPtbpdYZHHQz30l9MIzerupc7GqW6p4dYdc3xR+ Z264xPMKAPRk3mb6SaHCjn1Mj2A9UstR9CKI+H6Qna989+wRAvC/Z3F457pa jv4lZ/aF8yH1VS/L03mLcY6HQ5h+UABojEH6NJ8RKlOc4IVyZyOCXAIm5fQO m4V+Y4oS2h4aAUy5613WvbPXOfjM9NbpYnd4Rl5SWnZgngOA/B2eYXVMPylA QwD1ZHi+j5I9LpdGdy6SunzTyvIey6W6KLlK3Hx3UdmY0rKOZ1ndlplvlF0v OM/sOP/zG7kiNqydkZiDVa29odt0BBL/UAT9NjT+PsqAhfWuTcF5xaqH9cvH 4rt0AAD+WVkiwYxZUnHLWu4Yp+yJxqDtE9+yoQIiInPwxMWo8rku8v2gwaA5 GE2+lWHZnAufx+W58xdhbr255iUvCAAgLkFU3xiUsWdwvAFXEE/GN7S5zTkE ssKRXR5lM6xRB7XGoSGAOsXHoDcaDPN6g9GgNxjWK7zn+eKYtOnaXoDJFwJ5 AK6U5h5c9kyZqFD2uoR8J2ggfBd5qg0bKM9/t9ST6Mm5lTQ+DwdAGBmk8KKg CAoxGOcKOqlPKhagvYk9W4ElBpNNrIhWQ0gATga3lR3aLYBxcXIH+V6coH+U DMkm9iuQlw0jAHQE4Bwzf8GW9w8wJI/B6h7KEOZmstjg44eMAC7HF84yfTNr 0ZfghdyDFi0mXpoM6AAA7qcI5QOhEILjKUwPwQlE7zA9AkDmE+h3uP8i07di Af237E0PswhXJKE9JIGGAEF7+zFmpqIRyv5wpofgGGhiu0gCCQGiqxpvMX0j NtAXzvQIHMOTDYozFARI2I48Yg0J+sNxpcyiHcNZQeQ7IQsEu4CY0ifsTOS8 0J8To2FNvII1Zqd29DGuEEZAgAj36+Q7oQRzrbxi6TibXBXWQzlX2ElvQKo1 EBBgOMWLtdU8Jhr8KvVsmGptY9ItQsHwEFAoghaympjmsV2YB7tjc+dZoXW3 hUEF0yNAQYCpsCDWuAJYY75blRkvXmBp5dG9AwxXwkGiCnaVs8sv2ALqlknf /FiJjg22Fwvk8ZhOaI7EIcREefE5khgfrw4IrzQqHjM9EAtERH/O9BDQeASx XOcKADA8/MgvDWM0F02QRSTfYjw8CMkUIPdpIt8J9ZjtTvRmj/0ta698to75 4aCZApi+C6y4sT+eDTZ4AAAQPq1f+ezmMzvL0AoFCQFQZhymFAsXX09rHxth xabVZXlvKkpLixuQuAtmZ36hpHsQgn1ICNATd+oya7fa6yCevxO+zX9spJZx FexKhpDTWfXPzgMAwAlvJc1jiD3ZimQNoG/xKRpng23TKRIn2gdaa5RyT6aS Gq4ZSr8GAOCQ5286l+wVAdpBeoeQ+dZnzYjiAm5BHmMZN3GAm/4xAJiHZw7V kO6LLEQ6AIDtkasZlidlVS4ufPr2qqU7/3UWWfXwWxLi6fnoQ2LvoujXTDDv LiZcAABp6prfzZSINzfefyiTpgHsy/vpLEKnUF1mPflOqAY3cSlyYSrDXxGS VpIb6DnB1Ipw23MjwPyT7IQVXaBqcGR8StnwvSFaoixP+n/KAYQEmMhPJx4l QRdmYuYWqyrNtfpXysaePtMGmOgM0QuYAYgyLgAAt1i0+IOpDy6vXSeG9c3v 9CAJbnYIl++ZF4uQICNAuWAiD5hfWjkBJ2rZbDXQWT8yb1QJgxWEOnIr5c/g lB3xZ44mjEx6/nFxRWqkvGD+4lIQRZvkZMu6IPSF9jOdSoqfg/c7g/cWP6Ei QGLyhRciH+qjcUiCH7miCFp86IYcYjNXgSaoLEA0j0fm7a+/aPZtDw7+zZMu vVf2L5bev+dpnw8tkhBoFafbqN1Thb3ztG7pI6JFYHDB4yno8yHfEcVws/QQ U+o9iPQTElRz5f0m2aFjmDKKAQCAb3wXtMWDbBJgTvF8Wcin/cWAda2v4XNv y6l8Cil/cmUlgQIiCVDa+xxAndPGXverRYSYLbXvUjF2hyGXZaHvUVI7AaBU 1E8XibHu3XcMDIKh/Gnc3AgAyD3mJgEADmz/VGGjrUp5uIk6VVX+iQ9W7xkR AYLEXQAQP0v96oUcgn0tZylTLOZ5K3Cv67AZwCW+IKNzqVjTTEdsBJfjzNlE ogfgvP4lB0BqDBlQA4BKXLhP1CH/Hvcz2+dOLeyjrLRZZfG/r7E7ICKAr3cr AAQJqSwWhwLDke4WiWDnysYxlltNK37gXjQ+A69C852VsENDt0dAcnG4VMy1 a8xJ2/PK+DAU8DsBYPBtTo0eALjjTU/Tync9sKv0GedU1lKzQT0a/eFa4x0i AriFNQCAJJT5WlxOMLDfQgdo0qYlwWLSJ7mn0dEUllLb3T9TIh2Iv7+OQ6M9 Tc/HuX4Jwb12YqPLJ65998Xsidtzu4Tjhv68W8uWsy71HQdZiUddtz+j4gG8 7np23d+ICMBNrAOA+Sx2BgisgbfU0h483jITXmLW6uJKYryzMoOlrjyD7Z9e 8jTMKZuDi82dlmLbPDve2xKQM2p72h49fWF253RidYpfao9W9WDVcqpy6A44 JC2k4Gl+b/bG+gOICCCPqAWAheRBFvrdrUOo2dpmoeru861IETbea69vneL4 xhXEB0hFXKvJmRuSXGQY7lMmPLTZc//C7hGbKeG0buEv/MIaOXveV7z2CIfh vD8wuw717Z/k3LY4gogAlQMKAIAoFvvgLyKrz5bmb26osfeFEgAMMxP9bc87 da6BicVBC8p1jSa6m5oLjRMqe7/L6bEdXJsyffDMA0X+zTc/7h83VuJJoqOI TCJdGHQ9Dvpa1SBGQ4DoyMXNrE8Qaxxu7KCwxvbi2rhWfBuUQ91NtQtJOTr1 +tnAMFDMs09xdWd6oMLG8QV+TG/TO3cfAyhCoxSAHd2pC0jNAhUJX1gdQ0IA zu62xd1wuAhXrjT64RGHOYzVPN0+GlUuHls3US/0FjhggL4tLMNFYD159J2s yffx6JsJKUl2NeIpaeTlgrUOJhYUFv/O+iASAmzzXqrZnTxGs0sDXoS44Alh me1p9k5Yf4KuP5/vYO3ey/cIA6tfrXFh56XnC2+lZo9ersdV0ooXhqu4qGOk H37fxj6FLAH2eQ4Bp+LB0sSaPkiqZBf1CMCpqTAMJa7TFPLzFL15JgcFksb6 OSHWHOuLlQ5PPe54PIQzi9Zc3kN8JzhA7Dvv27JckLQFhPtHv5FVqF0WVDzW R4j44T3jVu5atXyM5BTvQrpD35dhfxsHLxcAGPAXqtb54z7FHkK+92ubkfLk 3hgnu+YPDyNjHiz//TQD2YCpwbg3XpE3c69ozV8JrQ0xs70OWaTRRVsfzCVY Vm8alVFIduac7fUkOQLkudRC++c3VhSjXXOFiAZMEczjvnhPaRlfvSe5sL8j Ji70tsMTHuRb14QNJ+g0PYJIBIjOPLCTNpEUAYSJ1QAAa7IDPItFM2CqEMfF XxPuUdhKaQ/3edDot19xbPPsby2yOvbFYWKLreEANPd9pt2ekp7UInC31tKY oYyWK9AMmRKI99zS4D7JNFLVsfTKp+RhvU3PnBn/hnIiZiwuMzOzk1D0XNkT JPbVU9pH9r4iQ4CI1G+stNmarHr25Iu1QkkfET2F1iVuxZEs14ihRmrTXOY2 7np1QX+sjEDiUsN2JNHDmRnn7H5HhgBKaZqVYFGHhShYy4Bkv9uEzhsMCVcs fjJrMbkPqNunUy1SJrSf6Mbr4lE+FjOLIupW/PYX9l3XSK0BbvB2WR1TyNjr GBZRTfDE2x7pS5/GMO4jZ6QyAABhSFxcCA8AYO6LYwDgheOir+reTUGSf/9Q rQOPB3KKIHV6q+UkYEiZYq0yKLmHqJ9VT8lStRB9cgeWnC4eh4bM3Ijk3GxP oYd3vkoFAGPSBL9Xs5uwpqox/Nnty00ZZxHcdXqOo8rO5AiwvUdheUibuaDA 3xE9SCYcuWAcqxxbdNQN1GJxIFpoLovwNfe8qG7r6emZKRUNAkBrxdT0GOaf 9MmHd0DzGMFsKnzrK0ekI0WAxAgbGaLjxGx1CxInthMOXZkd26mYBwDwlGJK 6mCqqW3sVS1aHpVN8cmj8wD1sZ6YM2qXas8DAIrV1LERh6sWUmuAjFobB6e9 WaoPFu/pIOFtP/Csgg8AwMGq0lljSDZ823UyBaA86BzGcyEs+BNEd50a49ic QCY6WORqy19hzF2GYadEP1x2K0iFBDd45N0HAC9CoWRNA2UBvLiPMbc//AtE WynBwa8dNyDza/WzKVD7R1m5DRDu6SQZEv4kyhcAPIklH1ZdjYrE/v733UTl B3Cw1YkmiQwBhDYJYHBnPvuGNfh7hsma1vVP8wBAgt+mJ88LBzg8/BXmE+KE VxHddlLifWcPhkTvvjbJFRjCqtzREndXV7Grq+sg+bwLzVHxrVxXnBIgJSdc 31Mqbgz/APMp3EN/h+jmuQe/cdaEDAF8bK6pIkaoCmkhhAz57Mzs8OwcihxG T6s6cL7/8CrPu4/5Mm2vp9M3sYqjX6KKsj7Y7dSnkAwBhDbXQ/I6RKNHAq+E 36ObkUZ6Dvji8XnzqYq7Jj4yNjllkuJQ6aVp7iEab0j6z522IaMHkKVFL1j9 sAJDa/HPktQhrxOll+Lo9G3sEfCiPa81Nu0wfnRtIjarD7sVmn/yl6gSW1d1 OffSJ5fhrzgMPrHQjBYKbyIaPgr47f49Uylgyque9BfNf9sRXhpzB49K/1it s3UbVoj+5ifOG5HLEnavq9JSMx7CfP6tVYhTnzP0/nMqBz7LirpWF/1m8J1r eE5M0KN6/1CAJbSMZJo4P0tpJXZjT+kAeVJME+LQGowIPgRXYg9euw/vet+5 hOtM9/0YfrUYkfcphkYkCWCVXSFKxZbEwTGJkpoHzKSt2rXjkvur1/6HCaA1 BN/vIeTgb5EVvE3BlH6YJAH8LS0j4ewo2CtKTJhqpF0WeU/CP5+/4XnS+LeJ PWm3TADb8ieNWJfZ5lJOT5nmI3SWtIJHWFqRJIDEcifox4oAce7h4W9pSba3 HgdbHqkTM90fPjmc+KOGYx+lVBjPKjG+f25p2u3tsvMIDakBIZhMT+QIwBFZ bG+CTcxLAC8/P7++B+T7IXDl/BjDF3KDx3s3J4rO/rf/ariBXaHDD//v831I qy7kYxIA5BRBGeEmC8VYFMNJw8PkvvL5sfFO/M7fKOD1T0W9MFrl9TOF919e dXHHs5yL4nkPIn3/Lvn/iKkdYT0ALz1SOjZYa7ELPNXIaMLY1JDx0XHGclRw /+lsKwDAn8/8tufUCB5pvi+25jni6lGlYd9iakdUExhxVDJwtXHIcsmfrGUy abhf+cUeFXOZ6sxtKfsWRgAeRe5wT67DYzTY+VcNqPOrnbiOzSRDdAoQz9jc ZD4vrGZwG1j4kNlKwT09/sU/fC7nfTpeeRuPPUc8jlxdJZZjfBREJUCYh00R NxkRRNyVITidXJa5JFds6x4KMdNcbX4y+abqBXanKB/vgAAzcn1VnCfGXC1E CRAosR1jo8xpJ2gNziv1W1iOvyAE352XWGCIXhhSDTZU+mC9kbQTEg+ebgJ5 8eXcKYzrYKJTgIcdCTOsF+DraBn53lcnoKr0DvF75k2xpVT8kB7jLzqn5MlP qcmwHonVp4ioS5jI3hcmYpRK970yAXDVBXvyZSuoCaV9pgKyIGxLuiOcH31K zfvnRmB1gCZKAF5ouYvNL0yEJpW02GsGAIChECJnL0LLJSh8kANrdrdIymqt RWDejBElwPk7nq/vltr4wkxEAiSkXF00K/YHkbhrlZTEySgRic0KIR6izFYV iXkpTtgruPvrc3JbLmFEpoCQ/EtL8VZqPXGf8iwjC1yRBACYHz9GnhBBRA/W liRUwXE2XQL1Xrg7Ctl7fkUUDgYRjSopE57HErZJJQpTg2c/7QJRy9tfYQkg DKNOaxpxHmtLEnEBATbnGQPuidht14VVv72BYIKj4cdeXX7/LgS7II2GyL+7 +m4ZzH9y5SSm5pTVrgvGHgNHQgIMpbRZy9wUrwt4+xGo1zgVDFTyiGnFJEt7 QP/gIH/O+OTEEANmqYzuyKGfnAz5VCfC5Dra/wqfIpklx+4JS4IA97gHrYKd OJlPcN+TYO0Z+sGAAVepq9xrYX52DEdxdc8dHq/MzmqkQerB54Nc90D3/b8l fmdEkfPtKPA+z/uTf0usw9S+JZUi7wkO9shCMm7hvZEplvrgKvNd3N14Bq7V WrpHFaR4mif6ZvT8YqXTQEyOwAQA4J5X0nqtd3JeMPKofkBtNs2Pj6TVkXmC hOCaxhkEgAH+zmls+0BdIkWrgCBvOhaBAM2WWQEjQr/E38t6OTg4Nr2s0Bve /4Fj3e62ZKHmyiSIMtJqf6eDhXV0QVQUGQ+0P3ur8hoA1PIxZqRWHOUy7kFJ KpY/zsLgwMlrJTDz8tcZcEf6VxS6w8MO82QGHHf75Bd1+7zST/M+fGzJFMyu eChh+PnYnwEAYM5I3pVMzUBwTAGkgkMDLMqPZOuJBDUJ7K4aRnztK7RccwOq uwGaNMc6P7Ox5zKbBUw4BlxON+NxselORZgNfA1oIkCmYn3YnTQdRwTkmiHY JcBwto1Fkm9MgFgzo0lpXFyB9p21rTgY8GfENWVe7DSuq+j5ks9SYnkjrpgR 7KCHAJyQy+sP+M0S8sTl2/2pDvuvnyNlYXJXSdd0t9bNzf2b5fduR3E0EMwI AeacEqB85hinphEgekf3z1hQXYEoAZKlnpIpi/F3lRYQSW9vXwIYRwKWLuEb LHeVdInn2rRaDQBg0HMMVDLiHTInctLAR/7RN5GZ/7GvZm7gd5h6JAIca0uC BIjOH9MMWdo8DV/uOHUDvz+ufQkAI/6DfkFyV4lYNdCm1c7gyZszJSCQy4M8 nC4+g1sBuru/yMhMYGCfYgPERpGcX21rpzv5WXH+17g749sP4B9IPzM72KbV zhLYLSnkKOvtYIXJ2bYqcnGdXFvrQW2BcKwgRICI/Psttr/pjMLfm8rb7lcj jaOE0zv0YTeJIoTZiQTI0yyv+6l8/zimACJ6gOiKWjvvH0YJGGKmvex+ZVAQ T+8xEkH4VBIwOd4FBsejyv/nEFp3zE1xEUBQKgeA0LJau1VtTQbcRXlgmho/ jgUlE/4hjqcAziuf0OK3isOmimcKiMuf2Kcb6SyvdpDwYs4b91ZQyxFTklhu OJBQTkfikEZFRnpddtTCbKQnlf6YGHNT7ATglIc+aoXo+LJWRwlPZr3wD3da SgkBBjKRBts5R2Jy6yMnttCvD/8TLUMZ8FJibImZACFls5/NAnR2Om6mIuCa Oy2lJKR4cA9B1wKieJAn8p9yHJlYTsBYRgT9IUqMLbGuAcJ31X2FZV89tWb5 IU+rKnBzfkpiPDVae9M89qUQGlzjx+99z1GD9J4WrH2Rw0DMMYwtsRrNKvuw Vb7jLJu4XXeW+Ot6JTs8VE50o4XRF3E4fuBAmLQOYW9CDNJkrOOFIrTdQcNT v6QpZ83w010N2LaCGKeABCHGkJ3RZVVo4fjNeYDu58mHBl44yFYn2b7wFUV+ UdGdpE7nurq6urq6iiWL/2Cgu7vHacmxQ0VXHSzzY+qx+IqiweNcbNnGsBGA k4U5elG3d7JFBRAmWQxPX6ipTapot28g3znwDGvPOMGLIlp3NzI+Uuzq6qLV zs3Ozc5NabVzsxwASUT+qemerh5HQT8V/v9g55s8g4+3N/yGonu1gcd/hZIA uaY6rBe+mKLfP9U6smNlO2RqGDhab/dnYXCey5IgovuJ7C0k8QkJ0y1Xtevl JwcAZhsbgR+ZfMDQ1dM9AiA+xtHcUq4/OXKn3cRsWfyeR3V0GieUL7BJQCzu Cx6h2+7gUqvGJrl0rLXkl2rtioDdLQqKnsCeDuxZXZcQEp8Q0tLqxJ3OHBEZ KezqKXs+6J3zm/X20FwfH6k9ChjTUt0b6snNSrjwX85i4pszCeCa6uvDmxvG p1Zvbw9ct7F7YV8EGKiyiYmCLuNqL4hPSNS1fKt02pCjUNyEoIgvJmEoymKP 8xjgwB+rAy/YSp3Aa2w0px6SNDTgpiUx+HlgkzdOnn9EyfRYLYFF+vqN/bRe QDsBIjpwKN080+MTW1vuYvfmGhwEAHAJVOV1rNc2nWsyzJyW2Z5+OQ0NnNTX H2EuHEUKKRhXbY6ff1bmC/J1FgAA7G5JjFQRIBF7wXbfkuLqx+cIJMz6srJi NP9f1pO9C0BlP92Dub5+9w/ep8MekISxbIej58/Jj3uIRptqX91ElQQomsLq ERZQmnWXYIUO42XIVlppMQWpVxyddLloL36fCdzwCsBoCLH//DkpcS6XUK3R 7UoAAzU15mIDMepcQ0sT72BLqGcbWb+yOpQ6L3So7rn/g1oFJTe9FpiLztoj gCwjVN+KbItu30uVmilAUnoWkxkgqjT8DrkKR0+yrCRNDf/tnzvSbUmnFFTc 83okY02Vavv5JybK+vDt/BzDPgEMQipuv+gZpvrFxfsu4dsqWKPDunw2POa+ /QsHethSZAUB7EMYdRZjS5sESE/tOod0oTIVYo9NlEiAIA9MKZKOhf8LafN8 gC0n2EfinBd6uxausRA0C2tHwG5ysPX8k7MuIU5d9CLVHgH0FEgADqbfmOer sx+Rv9Zc0k0buuHh2L0CAV8g5F+xsYh++P0XiNPC2gBmpaMFASLcX0Bg7jPU qau60oLsxECMbkN/8zkDGNLuRZ96jCI0d+DuGz+1FpYtS1Zf+e7cq9Y3fn4f mWUnNsxi9YVYbw6W7fIoCI7vRS+idKl2SD+f0Y18V5zxwjn/C777JRq17IS4 yH6VpNm6ye1xfZaTgTrUl/JaFhn9GGeBdQQQHB64OMg33cB2Kh5MJ80rbX/j v4Dcc8/P5DRN36Gs91G5ZQ+GJNnfcpWeqL9hvRhofRNXKmkiSJokkiu4zHQV ZgcpYedMpp3ctW5S5DF8bt5OunR7w+VLdN6ZPekB9uZ06Zmf2tKjc6dzqS6s EqvDGKi/Tg8TSJ2hYsDNjpP2SADya035OmnwR4NXMHWEEWcTyux8k/fY9kzc bD8WBhFmJBgbriWAVIgxwzQRDNlJ9jDmgzy/57STXIPHhhD//j6olNk8Lquy E6BKffoKrSvGhmsJEEtltNKgHQKYHacBIYKFeU9HX2fH3EZ8QV5Tks3je+2p I7iUB4b6Y023uJYAQVQWfBqx954HkBMAxr0cfCk5gaWeIj402U71Mm13K0a5 BIjCupJbIQDXxUNKZZlNlYcdlc+wP/JrjcscfCkbQx+GMhpztCDS+vCU3ame agkgc8UaHsoHAOCkZ/LABDpKA5ZH/G3XA5lETwCto5TTMioySP4iILzyC6vd oL8dY2rQHqor2mJPQ8z3DZmbz3K710l5vrJBuW0C6GZ9iKYHtocpR8m3vFGX ZwIAUKvbXhztshAt7kE2vSm4e90+bcPSKQlgngGAf2DIQ2r6HaqK9Q4wlGP7 uNQTXz8YMObpINxUislQiBOGV3rG16Yy8AkKCgoSDtoSqdnFF0mURcGIKMyF M/mf6F9tIpLcDTcmfW1XBCp4gloAAAwF2v8FyKj49e1u42TcXP6D+0dhc4OD j20SLWhfy187DS8hDakb5qhI/uyBcVrePxjOV3nYyIqXxK1Ff62uIAcEoGAN IA1Zk/DJ5Z3Bs/Ya+u34mHpLIEAOdo0eN16GrcIkeYyfT7A2/XmXEI3fcYTR yFC733lTEKK/e41mUfRuX7XdhgfP0/H+gyqxazp5+6opi82xxEJHttRSNV7R iDmtMZ4rqSoMdhJVSIqwuwtjRl9l4tCSBVLybpd9T7q86dsU3K0VXruPfaHL naZQ/WsJ3XlZ2fojyTpq8mX3fB6fZfsbSnaBqo9731vczXp+v9W+dRji8edS J4ASLg4xw6UoWaltGC7yKtf+7UvFzxEAANRnvXbZVLdRQgCApsFF3c6JhjoH rfiU5y/m8EWyA+dwnMCnJDuLfdwo21dvNpvN5jkVAOTdpiyNj/5G3tEbNiQh n3hVKodYdm50mP2V6gTW+8p5eqPhOp54e/42zLnN0eB2TipwOByOx3zPiJ+B yvmneurINWvdU01sXjWBzpwCU4SLgNqyVjlpP8F9Dj/R/Sbuk0hhWeh7BWXK qU2a167d87710ct/Tg0BFq3aY5GO1l/UTgHhpwhkoOJ+4rGPykHZh7Lp3IcU q0QGbBldwpAXagYA4C6lpXyY76gVpRLA9bWPCCSg4S54Uq2Xtg/KFdALNiyQ XpSkaTh6btEReLwj2kErE5VpAvfUEnGu4wLQnUuLRtgiQE0mBRcK5C6HIjwK +Q+n7FbA1odi7I8I4gipVHk+orgX9KSvZAAxKt36WTc5OqQnJLCXYHf24ZfQ vuSFO9FwM0Brb6s5fTSQCkNwll4LIM+pIXIu79TCfXbkLacCkrBtKXJ3rnaZ 4QWF/IDvdPjLFagvNK083r7ih51olwCTzw7fQP9r25G5vTzqeZaOEK/5fZjC 6DYoagA8/H0j/UcHlaNKN4l85wc6uJzh5+/7Oeor7b04IeLzeXweP7TA6MC1 risduUN4dPm/Auc1iCMkAABriTvmkKVRq7XET1er24HrJw/NdtfOerfrAKC2 NvAQ8lHeyDhoNBoNRoNx8pyjZIB1KAkQn2kaGupc0ALwDVVxuGv2AgCBtCj0 wicvd0Dg4e6i0ag13a2k5iquKaf4YzIdoMF7/5UEmy260j03BIaoQ+41gvhw ZwcxpSqrCZCUF1O9FKco8PCIjZtsbSdhTXV77wMmqghZYFcrqmJWe7yvA8Dp B+TyOLCXAOK8XF31eluxOC4usL31KcF8u6/OsGG6++GPiDtARc2OeLzV/XDR 0B30n/6BcEdrwFYChOfmPXts6wcbEJ9+6xYRDVL6LgbqiVsh33SWwFkx+10b Gt2KPUUjnC6TdFGB/m4zkkA+dhGA5+Hh6eHh6eHpoau2u2E2lWXfuo17InX5 fz6hL1OzXZj+y//APw0J96ecH0lJWbg/BPL4O/Bn/zAGYbGxHDSlB1hEAJ/j QRKVWq1Wq1VqxzpzY1nOrds4H+QJAyOFJC1QPoF/15194MU6H99UqTBW3d6G yJDOHgJsO3atGXvrorwP6/D0nnzoV3iaU4X8Sdwu4bK//JWFzayiDWFEPRNV 1m3iROaHeEIT+168alJgb859+xtkuy8y8HYZxmsRPm1l4+lGqbplCwH+XHMO 35PRVe+WY3cnOaSiqVaLE3ik7x7H6YRb+ohSJwKWECAh7jzeU7gNaal1GNvG 7aSpWJMzTDUM5uJMv+nrSanbNjWZWnEjn0h0wEXPfIwt+Sy5TQAYkONzSozM otaNjB1Pxj+KkDrr/AEMRckAAJpaDjN9i8swmPEpgorO4VgaEwA7CJBPbIs2 9/AAtob7jTQk6MYE1+/ipLrSi9oBsYIAwgJipkx4Fp6SjqVdB/6KxtQg8827 H+JpH38wHn3w9DqwYhFYPE/UoNF/OnYQQxjU9EnM4dKUosDz33FFwmXsGr6j oHZIrJAABYQDRCf/+ZuDGJrFUFOaEjeyruDzg959uZ7qSpNsIEDqDAklzdDY bueNcikJBcCPJz/AXtYbAHYOURPHthZsmAKOPCYTH9D1ilNHEemRS0zf4yIG ZUKMWtz9x6P9Jcc+oF5TzzwB/N6aJOcpO17qzM5fPjyArfgv5VDlY4qGjX9r 4QutR3zDCJbG5MA4AVLfeUBwC7CM6Rh3jeMQ19kT3bOpJ5roKNblBNqyJudW TO6hnZcaODPD7aiz9tsC4wTw979Ntou2gh37HKY50XDjt/OuvM34TmCbXusn crrhSXpH9SXVycRXQXnOUicI34Egadd5t9cPnXXU4CoADYFozsArCAkbcJaQ x+Vg/Fn0WbPsg2EC7Cr7CoFtO+D132Jwtmaa6/Bncy63nJm80w43/JLWQTHq ECL9rgZ/1a5Xblhl/ymcvIXhxH/EHzyPGp5pqWMvGhz8wF8J+0ZJ75AY1QPE qPC/f5NXxeF1qdATBNARg+FEDgtUHqq7/3pN/sNYu98f8PitkuYhMSoXYzpA FogzS0hq/Zd5pxTiOO79uzwAQ0FRXeUDVTSGE7FWUaIYyjttr/yTHcVFeQz9 kStM7gI4Ry9z3ffzE3ExoOLu1MAd386P7ge84jqy/WDfx08eJye8j2HV7FLG EnXgjCnetoU3d8cH9I+GSQngr8/1G1b/vPQ7eBL4u3cAwHUA9aXrxScffGQE 0PyBiyXTNY9axwocUJTYPJx09KcMDIZJAqglCTf8x8ATj7te6mq2Wd2N5fJm GN5/tr8/O6YAABi3bZxOP8cERZkkgBa8ngNAAp5UUakEPTtiJ55eYIsLPF9q cz2SQHO2rkUwuTQ2awcAwFeMQ0XLFRMs99gSPsaW9w+wYMsmGKFkZCyM7o3+ +t8AoBJrpXuAdTMAPjQnMnmja2EC0NhasybQmLN3DRjfHFd44VHRpxElwEIr +tpUBBDy6p+/B+k2E9Xh2wwhA9MESC75CkdrHp9wMHwTK0RA7vX/pnot1dYt y2TMJC9gWEGedxCXj2Qqcc+B5ios6mJqEZjsVQ9/EzBn61UnMBS6xCgBfA+4 /wKXz1vaHwhfa3KU7qzISwg8NTUxOTExGZEc39j0FYCdZF4593H2iwhMEqDs 4MUmXCe4cBXEr/aLP4LAhOq7lFdHs0Tc1x3eMu9In8HHv3Kwz98/xZDfKnN7 o+BDhm9wnpLGJ5YJaxEuR1uaS3N+Tfd9/tG/YjDvJ7zyb3SPawlMLQK5u/60 Hu/7h6FAMpfU/aFO962ZdpHXXYyh0QhjyVqZMQbxdpyZ+wi/y7N2+22yV5b6 jtAs9LrLkyTzzlb487se0zuqFTBBAMGOM/NfYC5tuRYFRDOErUBflpXVR2+u iHp1+K5CP45j17eUbsqrydgG/QRw2XFG+3k3MZEn8iOoCV7BtNsNTZSC3jue 6a1pcM/LnFQ6aBNuVGLsDTHoJoCw4szMpz1EZ7z+I6Q3811qr1CyLMIP83iT 7rhPv32rh48fwrw/eEAvAUQVZ9SfKIifb/YTk39O3uF0FG+0wvRT/9MGe/ce evpbhpyW6SSAeOeZ6U96Sa3BJnaQLzQqi2ZG6w6Dj17ptb3y9X73G9rKd1qA vm2g657/5f4Pd0leb9qYTK4DANAzp/z62naiEuEbdykpZIQFdEkASdWZsY8G yNNNjTfHkjUyOQqabtp69L4yW9d+q4+a+qlYQI8EcNv/v0T/cB/FL6/PP4xs F0zGivNseQIUzVBVPxUD6BCHHmXb7/89qs4eFJKs+JOkZTBGzNUGAeLjGFmU LoEGAlTtvvt/0PXW+BeXyWXJzWPw5wZuVgTILBLfJ5fxnxyoJ4Cw7Mcola/y fnLv3yeayWQR3D3XFAAQvGz6KyrW3GVq/b8I6hXjlT5IPTFO3qojdf4+AaPx ITEVreORkdOy1tbWwsK+gJ77FBdPdQrKJYBgO8pwB5d4cx25HnKZsrsuoqMj XfLoHEBw3F7Nv0iVzFdspFwC7PRD6O5eGdjSQrLS7X4+SyLEAFM8E/WjoLh/ AXkL7ipSXP/2LNlKx/cLKL5jzGDF+6ecAKXN6O7TtxhBufkDpBVJLxcoJgC/ 7Da6znRD5MukHOXfpfaONxooJkBpG8KQTL2AdBdVwSzJGMgaULwLyPgMYWdk CSCJi4uk3SeU7aCWABwZSkenV8jFTuwra2u7yXQkFOtALQHESHeZbVzxHInT 4/9Ny3gkHPtA7RPxRprs+LHrj46HED1ZmiVlRd0wtoFaf4AIKZLypstQPPQs IBjflfe6sJbO/IsbBtROAT6on7nWl+CJaV8ya3NhLaidAmQStP0llb1P7ES3 mK33bxvUTgHN+V6DCLvLTPn3MWJnbtMxaXNnMyheFv8mUoSwt/ALGOoD2UR6 HbX3uXFBMQH0QpT+V4T9eT1DthaAdkD1xliIsjMZ0QkrcmsCsAeKCbAX5ZM/ 9Zyo93RjPLW3uYFBLQHCiq8j7Mx0jeipiTQU39mgoJYAB3DXBHeA3sk9RE+t ukrpbW5kUEqA8gWkc++14khiJ5ZNE9w9bgJQSQD5ftxJYByjHWOtaAuItwSA fVBJgAPnkfYu+tORfyZ04q4aIkZpL8ZrDNECCu8yX4w29+E80T1A0q+InFXq zWtuUiJ+JuwDdRLA4xDiCQBu75IRGogbkbPcZD/6pbLiP+8ibH/eIKDOFnCi BaUZAAAAZpPwZBZfRrQnEVfyymf9mu4njyDxQLD5ZVYjUiYB/CNrkfdZm0Bg GxC2m1A4ccgDAICZJx/88Hp4FfI7YQ8oI0DabBD6TmsjcJ9S+qf36whcSbzq y9Tya81x9LfCFlBGAD0gnwEAAnGXU44r+T+EBID3WrH/ZetbjNdYpgpUESCn 5CMKehXitC2lFwT3phC6kmydN+O1b9+i4G5YAYoIkHjiAyq6vboLV/Oc4lC3 IWJ1GNYTAKon/Ah1w35QI9qqjvyOkrj3uQAvBY7mr19qGxxXErqSxMdCiZGB 1L2VPaBCAgT9Scg/UbRzurvLFXvj0jEl4Qu1WhiQn8US64f1oEATuP3A11T9 WsrivsTq3B/0g3keiTgwo4t8nRup6Ukac6ncqATyKSD8jPSXSMNBVpH0etuv MWeKzZk+W0OmEqdErFj3t3bPc2ruimGgngJ2/WnD11RlHZF/fgH7K41vA1L5 VyzngF6NN0W3xSzQTgGBR+En1KW90XhibyuKwVOOzgYkSosDygCiLsmsBlIC 7Nh3lcKJcq8Jh2ohjmxCaG+L153s/3I6FaAkwJHwfyFb0MPBQE81n8PRPJ40 ASxqlE6/pIHFCG+rMPkP1L1/SK7H8/558WSTSVlogmDQnbp7YxLoCBB1HEEK J/vg4Eo2dbyZ7PUsCQAKHypvjzGgI8DJrylNesnFs7jMCyFdh7Ol0uKAIpzK 22MMyAjgL6A25zUXR7apgOOfk77ezciEdX9H5jNU1otiICOAH0Xan2XUp2Fv W1KNYDFybvu6P7tniXgjsR/ICCCn2G9KZ87B2tQ7HkUhZnXsa3Fr/x7OpvYG GQIyVXDh6Dj5ThxBPq/A2DLWHYkt4gF/R6loNbKl/c0H1N4gM0AlAWSZ+AqB 40dGc9VpbC0DEfkidfz+s8STK3+lUH2DzACVBNgzTLKSi1MYX52C/Hosm8Ht w6jWI/rmqOK2xfXEf3etV1N8h4wAEQGEp/9AdeL5wYe9CtnOJp3zlgOHFpAt SHqER8KCpCKjeedLmmQYEQEqdLSUYx3g72934hCQNrcwG8kZwNYfBoy2zElC Mysrhf/fy6kLRvO75fzP39KUhTEuyKFFSPY9vf9Z9ziSlkBbSH1Bvg82worW EiJELxqjKwtnW6LDr72M//JC/DjYF/2v9SV9/xbWwIS4qCDugnZOq53Vzsxq ZjUz2NRfhfQlYR968wuV3S89vZTczL/WnM2RfUjbgDY4VgkQkhAdMdF5bQKA I5G4SSRBErFE7Grs72x1muUhykSxDmANHlSe/NbOgFx2VjT0Zbar4fHwD2gb z0bH4hpAlhAdZeput7WTC4iNkvR0tDpMspO8A2VZAIc4LL7WYOer8ooXdwAO +E2MT+z/QkHXgDY6OOCSEBfl1d3R6sDaFhUTpetub7W7D87KxmOrJ4U3/tbe N3+mvq4HADD5+Hi/IBYNshnB/9PInq4vnRhyu7pAEL/txHhne4tNr0wxhs05 IngG2VHziUN/sviBO0WxWerlAk93pWkIw/szjbU86pXlHPZW2ZADsSIFXeNV Z9dKbSYfTZCQ9QHanODhUATMDzY8DDgUq7NKvB3tR1v964nivVl3LI4JxHqA oiH6FqIvE3g4Xd0GHsP2Ir5i/cHug220TQItDxIHlesPmf7fDJ/c7CsI65Nt IuAlAMBE/UjGEY/xddV7BOmmcNyh+8RgBG7ARIV53Tyfdt5z4uLW+ycETgCR swSF2xoerBH7nP+p0sHn1FciBwAA/mnpg4DVEtAuxUXD98lXlNyswC8BAABM ioeSXRnm1QU55+MnVb5d9AzZtHB+MEu7pJiQVr4xe64FZU76TQZiEgAAAMLy vB7fWbEBZJ3+uYpwV7ghr/h7AIDg4qz7j+m76ssIYhIAAABUTe2Jr8iUS9tC j1Q6izLOBge1A8Scaf2GgkxEmwokCACg63zov7wtnBTtdpslU9cRHzoPJ/vy dt+kxQvhpQaJKWAJcXlQXacEgNgEOcrs8M7gGRVpOEvj9V5SkCcAgG9hKGdi fGQ4bq6G6dvZAl6gIAAAgJe/3M/38y1l3IYDKgJsYYPi5fR03AJmbBFgk2OL AJscWwTY5NgiwCbHFgE2ObYIsMmxRYBNji0CbHJsEWCTY4sAmxxbBFjBtpcz E6QTbI4CuViQGBIYrv4N06OgHVsEWMaB/z0Of+zzMhcJtYmtKWAZ+mkIjyJT Y2RjgpRP4EuFZKPmnbNjTI+Cdmw5hCzD9EPjtZczFaBDbBFgBUGb0sN8iwCb HFuLwE2OLQJscmwRYJNjiwCbHFsE2OTYIsAmxxYBNjm2CLDJsUWATY4tAmxy bBFgk2OLAJscWwTY5NgiwCbHFgE2ObZcwlbxlzyqi1+yEFsEWIW3R+rLWSHc EbamgFV4XQ5kegj0YysuYBlZyXouXdUPWYQtAiwhR/yJ4sQmrDqzNQUswiXv vAK+CPNnehy0Y4sAi9h3cRbgcM8I+Z42GLYIsAi/+wCpSXfId7TRsEUAAAAQ qAGg/Gumh8EAtggAAAAeagBw3Yy5rrcIAAAAnmoAkNBU9YpV2CIAAAAk9wJw 3JgeBRPYIgAAgKtvNYDrJlQDbSmCAABgm88dAJBsypLjWwQAAN/mRwDgtikJ sDUFAIBYDQDgOsP0OJjAFgEAoCkZYLNOAVsEAICmJAAAty0JsFnB00TBZt0F bBEAYEkEbEmATQj54n+10v8UDgndTI+GCWxun0DjO/kjSgCArtnv+nKbmR4O E9jcBDj54NrOoC4AAOW93bxapofDBDZ1mrgiwcfA++G1IabHwSQ28xogKrTp OJxo39Tvf1NLAO5/5M8Lxj5iehjMYjMTAGRu+vJN/v43NwG2APD/A1/1R1Br xPucAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAzLTEzVDAzOjM3OjUwKzAw OjAwmJjk+AAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMy0xM1QwMzozNzo1 MCswMDowMOnFXEQAAAAodEVYdGljYzpjb3B5cmlnaHQAQ29weXJpZ2h0IEFw cGxlIEluYy4sIDIwMjF9ve4mAAAAGnRFWHRpY2M6ZGVzY3JpcHRpb24ATEcg RlVMTCBIRFcsD7EAAAAASUVORK5CYII=" ###### class Time def round_off(seconds = 60) Time.at((self.to_f / seconds).round * seconds) end def floor(seconds = 60) Time.at((self.to_f / seconds).floor * seconds) end end $dt = Time.now $dymd = $dt.strftime("%Y%m%d") $dy = $dt.strftime("%j").to_i $dw = Time.now.strftime("%-d") $d0 = Time.parse("#{$dymd} 00:00:00 +0900") $d24 = $d0 + 24.hours $d48 = $d0 + 48.hours $dtame = "#{($dt-20.minutes).floor(10.minutes).strftime('%Y%m%d')}_#{($dt-20.minutes).floor(3.hours).strftime('%H')}" $dh = ($dt-6.minutes).strftime("%H").to_i $dtr = $dt.floor(5.minutes).utc.strftime("%Y%m%d%H%M") $dts = ($dt.utc-8.minutes).floor(10.minutes).utc.strftime("%Y%m%d%H%M") $dtm = $dt.floor(3.hours).utc.strftime("%Y%m%d%H%M") begin $koyomi = Nokogiri::HTML(URI.open("http://eco.mtk.nao.ac.jp/cgi-bin/koyomi/sunmoon.cgi")) $sunrise = Time.parse($koyomi.css('td').select{|text| text['class'] != 'left'}[0].text.strip) $sunset = Time.parse($koyomi.css('td').select{|text| text['class'] != 'left'}[2].text.strip) moonrisetext = $koyomi.css('td').select{|text| text['class'] != 'left'}[3].text.strip if not moonrisetext['-'] $moonrise = moonrisetext else $moonrise = Time.parse('0:00') end moonsettext = $koyomi.css('td').select{|text| text['class'] != 'left'}[5].text.strip if not moonsettext['-'] $moonrise = moonsettext else $moonrise = Time.parse('0:00') end rescue $koyomi = Nokogiri::HTML(URI.open("http://www.hinode-hinoiri.com/131130.html")) $sunrise = Time.parse($koyomi.css('td').select{|text| text['table_line'] != 'left'}[1].text.sub('時',':').sub('分','').strip) $sunset = Time.parse($koyomi.css('td').select{|text| text['table_line'] != 'left'}[3].text.sub('時',':').sub('分','').strip) # TODO moonrise/set moonrisetext = $koyomi.css('td').select{|text| text['class'] != 'left'}[3].text.strip if not moonrisetext['-'] $moonrise = moonrisetext else $moonrise = Time.parse('0:00') end moonsettext = $koyomi.css('td').select{|text| text['class'] != 'left'}[5].text.strip if not moonsettext['-'] $moonrise = moonsettext else $moonrise = Time.parse('0:00') end end def isnight if $dt > $sunset return true else return false end end def isdarkmode if ENV["SWIFTBAR"] if ENV["OS_APPEARANCE"] == "Light" return false else return true end # elsif ENV["?"] # if ENV["XBARDarkMode"] # return true # else # return false # end else if `/usr/bin/defaults read -g AppleInterfaceStyleSwitchesAutomatically 2> /dev/null`['1'] if $dt < ($sunrise + 1.hours) or $dt > ($sunset - 1.hours) return true else return false end else return true end end end SATMAP = Magick::Image.from_blob(Base64::decode64(satmap64)) do |img| img.format = 'PNG' img.background_color = 'transparent' end.first RADMAP = Magick::Image.from_blob(Base64::decode64(radmap64)) do |img| img.format = 'PNG' img.background_color = 'transparent' end.first RADMAP.image_type = Magick::TrueColorType RADMAP.negate unless isdarkmode # https://ja.ojit.com/so/ruby-on-rails/3795657 CLOUDICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAA90lEQVQ4y+WSIWtCYRSG3zt0sCDK0pjsB0wUBmZBm3nBoGHNINtPUK/d tqIri1rWlgRhhhVRJsYFMWkzujDYs6Bc7vc5r+u+7ZzvPHzveTnSEcoxS0JK KKEPfTocZAlTY81Gc9qcB4/HGAFv3JOlRAcYEg0CnoEHPIvk+OKdJJd/j2eA J6t3t7W3oIxjAy4Q39lpQBOXKfBoA68sA8LoArlNdfKP5L9V0Y9uTWCkC9uS D1lpppQJ9CXV95o61ZVmB2I13hpAcfub14yqr7QGetFEa990XAWV1FN+51gI U/VOw1SLiLePBYV0rRud+VoLjZ29kR+JfgH7AZboXs3ksQAAACV0RVh0ZGF0 ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVY dGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAA JnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea 8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet 2kcAAAAASUVORK5CYII=" SUN = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAA3klEQVQ4y8WTMU7DUBBE32LalPgMdKaBAilVDkOAO8AhjBTS5Q5whsiC guQMKSzZZ0CPAhAOMbGjgNhud3b0Z3b/wj+FmdluhMKiHTnYR8bM3OS7JBNz Z+2EXL3aqF6r+VceDSjhgkXMTbnkFHhmEpXnnDCN15+FjazV0lKtHHX5SK1d OQRw6MrKo+2EW31v/6DoTVtbZmFhBj5aNuph6UMT77sH95W0adowepnuHuv6 4sa8xNyUMWfAE5Ootyyu39c4bGAD7ph+DhpiCcA9xwy65/gn97D2wq4n+ovx Bv6ToSCm/AsBAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2VDA1OjQx OjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0yNlQw NTo0MToxMyswMDowMCaG9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQATm8gY29w eXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAAACF0RVh0aWNjOmRlc2NyaXB0aW9u AHNSR0IgSUVDNjE5NjYtMi4xV63aRwAAAABJRU5ErkJggg==" MOON0 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAiUlEQVQ4y+2RwQ2CQBBF39qDhEb0JCea0Sa0DArRKqQMDpssRTwPmqho WDDxxr/N5L/J/BlY9AeFz5YFBzZASxNSdoK1vRqNarLO2Qt7OysAKzuT63Hg pHf7A9HjOHAxvlTB6PndsZp7pSHQUj5XYkdJOym0wTApdP6s3x+3ZwtcaUI/ N+Oin3QDmkJTk/raVNEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMjZU MDU6NDE6MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAy LTI2VDA1OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHlyaWdodABO byBjb3B5cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6ZGVzY3Jp cHRpb24Ac1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" MOON1 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAjklEQVQ4y+2RsRGCUBAF91uDBBYCESZUo0VoG3RiBUbQhYEOaGZktCYQ iA4fnTFjo5ub927u3cHMHwjvLRO2pEBNGa7RCRa26tmH2ljE5ImtJ3Mw9642 LscNezXv6oOqu1fFYuBIuYRjV9cAZOOGKENDzapfiRSAamro9aTQ8bN+ftyG DKgow+3bjDM/8QTmH1LDDnvKmQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0w Mi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVYdGRhdGU6bW9kaWZ5ADIw MjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAAJnRFWHRpY2M6Y29weXJp Z2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea8IIAAAAhdEVYdGljYzpk ZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet2kcAAAAASUVORK5CYII=" MOON2 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAgElEQVQ4y+2Ryw2CUBAA51mDHCxET5yoRorQNiiELqALDyQgPYwHkUQ0 vGDkxpw2m53NfmBjBcJnyoScI1BThHu0g5md2qjamsXKEztvpqCDsp8XrmoK o6CXeaG0GaIX5XvFbumVpkLN4TnSSPXfpeNn/f64Myegogj90h03fuIB3oFg 2/9mWpYAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMjZUMDU6NDE6MTMr MDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTI2VDA1OjQx OjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHlyaWdodABObyBjb3B5cmln aHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6ZGVzY3JpcHRpb24Ac1JH QiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" MOON3 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAfklEQVQ4y+2RvQqDQBCE5470Et/DNzDxnlwErcQmaQ4fw1rwS2Mg5ufu AknnVw4zy+6OtPMHzKuE1UlOUqPWLNEJ5IzAhQHw5DG7ZaQn40gDgMeGAxWQ YZm5c946nvNOVzOp0OFBCQa+Jb7S56PrpKPjb31fXLkW1yUUt/MLbuw3ZODd jP3HAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAw OjAwV9tMTAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0yNlQwNTo0MTox MyswMDowMCaG9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQATm8gY29weXJpZ2h0 LCB1c2UgZnJlZWx5p5rwggAAACF0RVh0aWNjOmRlc2NyaXB0aW9uAHNSR0Ig SUVDNjE5NjYtMi4xV63aRwAAAABJRU5ErkJggg==" MOON4 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAbUlEQVQ4y+2RwQ2AIBAEB2tQOsEGaEYqsifpBIo4H8ZExXDBxB/z47LD ZQE6P2DKkVgCDoisJqk3iJcsJ0m8FreX+KFM98TwMALj7TwR6oIrlrq6oPIU YpGIVV8v3fys7x+3MAMbq8mtHTuf2AFUpUuEYu+AZwAAACV0RVh0ZGF0ZTpj cmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVYdGRh dGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAAJnRF WHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea8IIA AAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet2kcA AAAASUVORK5CYII=" MOON5 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAlElEQVQ4y+2RwQ2CUBBE50sLEkMhcPqJhiKsQYqANujBOjxBCXrSRANS AuR5kkQ0fDTxxjtNNjObzK408wfM+whfiUJJpXJzd24gpgYuXIGK2GX3qQE4 scFypmI5Hsh40rLCAumrYzFIhL3ytDUH3RSNB5wMA2WvOu2xClRMK31kPam0 +6yfH7dTJKlQbppvO878xAPtrVzCEFDlVgAAACV0RVh0ZGF0ZTpjcmVhdGUA MjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVYdGRhdGU6bW9k aWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAAJnRFWHRpY2M6 Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea8IIAAAAhdEVY dGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet2kcAAAAASUVO RK5CYII=" MOON6 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAg0lEQVQ4y+2RsQ3CQBAE5+kBi0ZMZEduBpqAMtwQES6DAPQuYkgg+Dfw gOTMm91q56S9g0UzKEwtK/bUwEAfYnGDnaOqVzXaleLVIy7YejG6/gwc9QmA rXpIE6uMqJPpxC1zJkBROTAkU8Mmc96XNnxVunzW14/bsQXO9GH8teOiv3QH XX9VzO0wTyMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMjZUMDU6NDE6 MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTI2VDA1 OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHlyaWdodABObyBjb3B5 cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6ZGVzY3JpcHRpb24A c1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" MOON7 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAj0lEQVQ4y+2Ruw3CQBAF5+gBi0ZMxJG4GWgCynAnVECEyyAAnVMikiGx EJjPARKZJ9vVe7t6uzDwB8Jjy4IlJdBQh5SdYGWrnj2oySonL2zVkxGM7k2O 3xvWqm66Kqqre8Wo5ygBaLpqy7HrvDRk6Ruamz0wY3Ldlgk9N3wUOn/W549b MAV21KH9NuPAT1wABsVS4mTqqMsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEt MDItMjZUMDU6NDE6MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAy MDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHly aWdodABObyBjb3B5cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6 ZGVzY3JpcHRpb24Ac1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" ONCERAINSUN = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABh0lEQVQ4y5XTvUvVcRgF8HNTLgUapVCaBA4J0WIUBRGUUFBbQ9Agd4jo L2jpBWoqSLDGIAhBKGiwiLAXbAmHaoqgaOhNehGHcmkRJP00XFO89/7KzvR8 z3POeR54+CYF0K2S/4HrNOJXFTpmMvPv1Eue27hQt9q9UJVcNNHYcNScW3Xs PjwsmtHnQKLNBWNeG7Y30eW41Y3l7fbbZrsPeOeFGfPON5aWnPDJEo4kiQ3G cLBevsYo3rqs4rRxjFWX0Gba43rDIAaUF6dVzLlvnebEPd8Tm73yzcmqYId5 d5SWRZwCP41642ui1bAH+qrNc9hSt+RHt90wifHahe76sTy/uliSWOuJeTuX t54W3LHa7cK1pfeqJO/TbX2RoTSZiWxdnrELA4UT2v0yWEs+MutYgeEs9tSS m7w0q11ZzyLXo5y4imfqP4EWlcSQaR1JosO0ocRNV/4ctNH4LziUJA7jc6Il f4Ne/ZqUlTXp15uVwYiRFUqTRCfoLOo31xKlKWeS0lSR4Td5GRZA6NT9bwAA ACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfb TEwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6 MDAmhvTwAAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNl IGZyZWVseaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYx OTY2LTIuMVet2kcAAAAASUVORK5CYII=" ONCERAINMOON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABVElEQVQ4y8WRTSjDARjGn/+soWElsbVy9JGDw8pBKSfihBJOHFzdFOXi LLm5aEmjxD8lo9yknVwkDj52m62RCzKHYT+X0f77pJTn9j7v87zv2/NK/whq GMUkTMXP5HVEgRegMZO35QgNnBI2BeTSsDqU0njhqQMccA9EOQFmJIlV3pim KldczQbwwC5LBEkAc5KEm20gRSzb4AeWvybhZR+YSFc+Ytxa5b2A38KUE+Kd MCYj+Phgx2pYJ4Era2cXcMYNAEnarM1LQnny8kgSPTxynd18ZbPITyaBZusf Imot8sU9SYNWw5ba6Sxo8Ep6si6t5ZlzGgqctEAKdzY5RpILDBw4vjmnRBmL wGG+Od1MSZiY6bqJO/qxEWeFykJ5eADScc4DQYmWTIXdajDizEpGHMNAa6pX QDKuVArYMRkqKcsw9AGR/D17XvZUxzr6xYY/xSczUtz31H9DsAAAACV0RVh0 ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAl dEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTw AAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVs eaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIu MVet2kcAAAAASUVORK5CYII=" SOMERAINSUN = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABr0lEQVQ4y5WTP2hTURTGfy+Vp1SjCBYKokKJohCoIA4FhRKIdCmii+gi InQVrJRiuziqdCzSQungH3BQcBVBB9EpS8VBKDj4B4VUpQ7ahvTnkJfbPPNi 8Cz3nO9855zvHu6FDuaoD/kfc16z8FyKtM8DIVjiTcB3eDS76x1/WQ7R9uTc 6Wvr2QUH/eijxM87YATgKfVKJ+W95sGSFevqD2+6zdjdm4woUCOKHKdIlZhp vnKfb5Qp8ZLhqF2Q/T6x1U40mnhdHW+nD1i17oxD5j3sNb/7xUMA5nznKwAL HjMmAV+45smWBvv95AcvWrbXB/4Ej1hTbzfSZ9TJv2aWEmlrrvoWwLOO2ddI 3tLWPSTqZzzvqPfUJXPp5DPf/+OJ3FAvpKEFV+3pWBD727ubcQ54Sp4igCOJ SsA+RwCidT7Tn+6xxw2f2wPWnA3orDUA96pXUxOiKpMMcy5Qp5wK/lbmgMft Sscda0qyYqUpyctuOJ19uV3BW3Qx+Kctdd4gTjgIxsbgoBN0M3U+FHT4oltS 0QpVYA64RJWV7hMKxuHSsYWuE6JlaC4xWmc5q+APB+sRuh6kmHAAAAAldEVY dGRhdGU6Y3JlYXRlADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDBX20xMAAAA JXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwJob0 8AAAACZ0RVh0aWNjOmNvcHlyaWdodABObyBjb3B5cmlnaHQsIHVzZSBmcmVl bHmnmvCCAAAAIXRFWHRpY2M6ZGVzY3JpcHRpb24Ac1JHQiBJRUM2MTk2Ni0y LjFXrdpHAAAAAElFTkSuQmCC" SOMERAINMOON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABc0lEQVQ4y63TP2jTURTF8ZNWY61iFQniIC5dzBIhgxaETuokaLsUcSmI Il0UxE0QQfAPLm5OLkJxVQfbRbpVpUXExUUoKHaoCLVgocLHIRr9xSRE8E7v vvs97953Hi/5v2FAVX+v8DHTvmKwN/ywDaue42A3bNCI0+r2WvJOxZDPHutr D+/0wHe/41SSuIw5E0Za8aoPmDZp1FlzmFVO9DvjPdaL+GaL1hxv5iVTeKhq IFG25G1RcBHnW3reA6umXcfdYvGJZaUWwX4vXHPbRyzbUSx+8rSjc7u8tmJT I/tl2Lf0dRKUvuRSdudIUTCbQ42RHPjj7IpKkmQxydFi2zFcSAzbMNbcnTGT JEZxsijY7pV1+9RxLnFFLbFgIbHFM2u2/v3OL9WV3VFJcD8xbjxxC1fbuTFk T3O94kZzfdOJToZEyYRyYlg5UVNLuv4HNUw2s3nzrUQH9z3qMkbbkRqXrqn1 JEu8MdUj+lOw7Z/wbvED2dsEwF0biqsAAAAldEVYdGRhdGU6Y3JlYXRlADIw MjEtMDItMjZUMDU6NDE6MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlm eQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNv cHlyaWdodABObyBjb3B5cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRp Y2M6ZGVzY3JpcHRpb24Ac1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSu QmCC" RAINICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABO0lEQVQ4y82SsS/DURSFv5b8SBujRARTGRhMFgwWIaGLgaRCWIxispgs VoMYmA0kNlsXMZKUhFjaf6GVSGhJNJ+BVn+quvZM5553z73vvDxoOURqC2Ms MsYwd6S5ipSaeB03q36Y9UUtOtesvWzelDEw6pS3vjnduD1m1rx9NUrcG4se uOu87fWGNTX1Sxu2gmsHfxsO/TBWN2bZIXvdsuijneGjS3P/5FtRN8PSvu92 NDREffb0i0e/tTQBkwBuO1ptHHX7i/BC+MLGLZmzC9RjMDAAjxXAEXU9tCHy ygL9LAEF8sARR0CeAtjDOWUu6m866yqYMAAzZsDABLjhk8m/w7VV2Y47VT7h QOMXxLOfaSY9oxm+Q3fb/RO6FvW/5IF74ASY4Z6H5hviUAldqf7dEHkFYC9U tTY+AeOQu7GwX+MjAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2VDA1 OjQxOjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0y NlQwNTo0MToxMyswMDowMCaG9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQATm8g Y29weXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAAACF0RVh0aWNjOmRlc2NyaXB0 aW9uAHNSR0IgSUVDNjE5NjYtMi4xV63aRwAAAABJRU5ErkJggg==" HEAVYRAINICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABNklEQVQ4y82SvS9DYRjFz3tJyW2siDAyVKIWixgsJiIYSCoS/gExdbdY DWJo/wKDzUC6iMFA0kradGk3cxdJe0vS5me4et2P1o2tZ3rOeZ9znvdLGjoY P8HWvlaU0psKejLtGC+r1IAONZqAw2Zce5cGGWwJi3VKfLIxuN2mRoNZn5Lk FYcrztliNGo4BjIhLUUPL8yHDdd0sCMxhywwwxkOVcaDS4/U/zjfEXAalC75 YmygweKDG7e2frSCElqTJLKkvcY0WbdQU8ENk6RNnQkJyEskSEjkQZJYBE4C E0xLe5rTgZeQU84Lm9aturpzmXfD5p4dTXqGJd/4bU1p1zRCBsk8MNL3zFUt m/ceCbyh6fbrN89+ZumfiBoqKnt1WZXYBJKSRJHiL/Mj8g9NS5J0EWDDjW9m BJvAkwsd1gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MTox MyswMDowMFfbTEwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6 NDE6MTMrMDA6MDAmhvTwAAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHly aWdodCwgdXNlIGZyZWVseaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBz UkdCIElFQzYxOTY2LTIuMVet2kcAAAAASUVORK5CYII=" RAINSNOWICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABOElEQVQ4y82RPS9DYRiGr3NINUesiOiGoQsGC4vFRMRHghSJSWIRf8Ev MIjBPzBIDAZiIGwMTJbazI1EUlVCLkM5eipVo3t67vt5rvd537zw7xRUGyPm GCLLDSecB88NWIfNq2/mLaolxxuNv1swZwSGjnpt2bH645F5C3ZXJa1eWXLb TSds/gmsqLmaLOuXLu2tBXZ8M/pxzKJ9drlhyVvTydaZd7+8b1ldT0ZbvthS Fwh9dK9Sh5/ZCSlGAFwzEw9mXKsUFImSwAVldm0DBmkH06aBdgYByNLFQQII npghw3x8iyWW4j2d7PPOYXIDwRFTvMZA+N1hkg6mg0LFVH1JcGwT8EAxjoo8 ALcMBPf8Lldd5W+yxxTYbz+YsqcxcOpCXC942hhoBXDW2W9XrbA2CJ7qu3+q DxuOmtouvECeAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2VDA1OjQx OjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0yNlQw NTo0MToxMyswMDowMCaG9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQATm8gY29w eXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAAACF0RVh0aWNjOmRlc2NyaXB0aW9u AHNSR0IgSUVDNjE5NjYtMi4xV63aRwAAAABJRU5ErkJggg==" ONCESNOWSUN = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABhElEQVQ4y5WTzStEYRjFzx1cmsnOlCRKk/KtNBZY2NDMYqKUNCIkLCzY KDt7yUL8BbKQnR1FWVLERs2kLGQ1KRrDMPpZzHWZj2s4i7f3Oec9z/O8X5Ij 8Og/YJ6rfLzL0dGqloIGugnawb52bb6OqfxtrANzOWw1dzx/RcUZ2oqa1S5J lMmvSl3oxkB1etPgb9s1WCRJGofUZKqGvcytYfnVrJhKFNKJtvSgPi3qXi3G U27WLiJAighxAAYsPgSs5Vv+QYwwbgkXvZyTpN/STrmUMBhiFq/VDBFiVP9I 4OGUBBss08kOcYkG3oHVtDwBhLNqNvKNM0nCRwdmWtwkhTunzVHqqWKBBLeU ZUrHRH855DFgIft+k5Q6Glw8svcduyQdyFSPcwnFZWYyHl6IUp79A9IRTcBk dpIgr0xLHDFicyMcSVRyTYqK3LoBxiV8mBJttEmY+CRmeCCUv9Uie7bEkj3v ptb5BMU2gR81t1UIBPBKeK0xUNBg2VatN/NXYGI6q58a4PEHz2BrWwAAACV0 RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwA AAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAm hvTwAAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZy ZWVseaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2 LTIuMVet2kcAAAAASUVORK5CYII=" SOMESNOWSUN = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABhElEQVQ4y5WTzStEYRjFzx1cmsnOlCRKk/KtNBZY2NDMYqKUNCIkLCzY KDt7yUL8BbKQnR1FWVLERs2kLGQ1KRrDMPpZzHWZj2s4i7f3Oec9z/O8X5Ij 8Og/YJ6rfLzL0dGqloIGugnawb52bb6OqfxtrANzOWw1dzx/RcUZ2oqa1S5J lMmvSl3oxkB1etPgb9s1WCRJGofUZKqGvcytYfnVrJhKFNKJtvSgPi3qXi3G U27WLiJAighxAAYsPgSs5Vv+QYwwbgkXvZyTpN/STrmUMBhiFq/VDBFiVP9I 4OGUBBss08kOcYkG3oHVtDwBhLNqNvKNM0nCRwdmWtwkhTunzVHqqWKBBLeU ZUrHRH855DFgIft+k5Q6Glw8svcduyQdyFSPcwnFZWYyHl6IUp79A9IRTcBk dpIgr0xLHDFicyMcSVRyTYqK3LoBxiV8mBJttEmY+CRmeCCUv9Uie7bEkj3v ptb5BMU2gR81t1UIBPBKeK0xUNBg2VatN/NXYGI6q58a4PEHz2BrWwAAACV0 RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwA AAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAm hvTwAAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZy ZWVseaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2 LTIuMVet2kcAAAAASUVORK5CYII=" SNOWICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABJElEQVQ4y9WQoUsDYRjGnztRZlhYGGhZ8BicIlwYBpmC2Bd0aRb/A5MM jFZhQRRhxTKxq0GYBuOC4IpZQVQ2DgwDUWT7GRR397E7V/ek73ve9/c+3/tJ oy8reMHSvBY0pztdWe1/WaY4o69bZuPbZ/DpUmGRJC7bvPFCNrrd5oZPlgNO hmee2KTA9CBgDdgxvNW/51VJmsAekDI8iwolCpwAdaxw8ZqHmP12gY2wdc47 iUhggg+Of872r3evSeUjI77UlhOekaVLvZ9BkWKg6gFVM3YfuCQhsUSJJk1K 5CWJFBfAigmMcQrkJA4BHx84kMjwChwNWs1iHUfCoUWZMi0ciRyPbBmfaoA1 enTo0KMmMY6teJHGpUEDl7SGFR7e0M0jp2+a/8uALB476wAAACV0RVh0ZGF0 ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVY dGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAA JnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea 8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet 2kcAAAAASUVORK5CYII=" HEAVYSNOWICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABG0lEQVQ4y92RMUtCYRSG33tJw6KxsKlB0YjCIRqFoKAlCHERf4CDDm0t Qf6IfkBT0Q9oChIEiWizpUG3CMOhW4IFRTwNV+ze+3G9s73Td97vPN855zvS PxMpTrljQJNjNqOSLaoM+eGBC5p8AEeTgTJwz9ooSnIOHIanJ3nlkYTHsbkE brmizpIJ1IDtgLfMNw49oM9OEDjjkxnjmQ1iEnme6LPov7qhM2G+PHDinu2R 11KKhVCipRdt+YFrWToIBWzN6t1fNMYzDisSaeJjN05aktgFCsE+V+lRkWhQ GnslGhLrOLwxZ46WZV9iXpIoUpTciAptMuE/YgIZ70JNwGgpQu7Q5Mj9De2V sV2rK0nak9S2vtSNrDCF+gUmfKcgrLCpAwAAACV0RVh0ZGF0ZTpjcmVhdGUA MjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVYdGRhdGU6bW9k aWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAAJnRFWHRpY2M6 Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea8IIAAAAhdEVY dGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet2kcAAAAASUVO RK5CYII=" FOGICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAt0lEQVQ4y2NgGAWEASMy5z8ngxWDE4MFw1WGfQwHGD/g1fCfkSGYQQhF 7i/DYsZf6BqY4CwRNOUMDMwMyjht+C/LsB7VeVDwneExw2aG7Yzv0TUoMAjj 9es7xvsQBgtUwIvBAK+GT/8rGX8j+4GfQGjyMYSjOkmdgYeAlo+Md5CdZMGQ R0DDBYZkJA2MC/9/ZhDBq+EYhELEwxMCNrxF8QMDAwPDfxWGSQziWBV/Zkhl vM0wCogGAHCtJNE21N/PAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2 VDA1OjQxOjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0w Mi0yNlQwNTo0MToxMyswMDowMCaG9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQA Tm8gY29weXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAAACF0RVh0aWNjOmRlc2Ny aXB0aW9uAHNSR0IgSUVDNjE5NjYtMi4xV63aRwAAAABJRU5ErkJggg==" THENICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAbUlEQVQ4y+2PuwmAQBAF3x2nXRjZglUIVmZgB4IKgg0IdmIxyhj4yQ7P RBCcZNnHG9iVfj4BBQPRE6EBWmy4EDMCFWbfj0Gq0esYJVqVmVmS3BUvXsGK Jz/ETEB5nnQvdEAdXJfI6XHB9Z+X2ACstyj09mmqeAAAACV0RVh0ZGF0ZTpj cmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfbTEwAAAAldEVYdGRh dGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6MDAmhvTwAAAAJnRF WHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNlIGZyZWVseaea8IIA AAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYxOTY2LTIuMVet2kcA AAAASUVORK5CYII=" SOMEICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAk0lEQVQ4y+2QMQrCYAyFX35ab+EBunsHoeDJHDq4F1tRFEeh4B08gLvn qJ+DFcXkAB36lpDHy8tLpAkjAzlnSseuOJFH8sQeqB3fAC3pnzY2wMV7MaMD KuzdD4VCNyU9RLDcNFevhd0lKftaCfV6BgMpsMGogC6MdAXWn0i/R7fA1g3s gNrJh7ceWTq25ECmCSPDCzT5UOOUFiG1AAAAJXRFWHRkYXRlOmNyZWF0ZQAy MDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0ZTptb2Rp ZnkAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMCaG9PAAAAAmdEVYdGljYzpj b3B5cmlnaHQATm8gY29weXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAAACF0RVh0 aWNjOmRlc2NyaXB0aW9uAHNSR0IgSUVDNjE5NjYtMi4xV63aRwAAAABJRU5E rkJggg==" ONCEICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAe0lEQVQ4y2NgGAVUAP+l/8eSpmHn//8kafkv///J/z//fWF8RpwKe+FM YYZwhp8MOoxP8JuMDjQh4iw4dSih8Z+iaPjPxiBHWlho/icE0Jz0meEDAwfD coY3OM18h26H1//f/5/+VyDFWdH////fTZpPYv7LkKRhFOAAAADaWYo0h5nS AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAw V9tMTAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0yNlQwNTo0MToxMysw MDowMCaG9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQATm8gY29weXJpZ2h0LCB1 c2UgZnJlZWx5p5rwggAAACF0RVh0aWNjOmRlc2NyaXB0aW9uAHNSR0IgSUVD NjE5NjYtMi4xV63aRwAAAABJRU5ErkJggg==" TWICEICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAgklEQVQ4y2NgGAVUAP+l/8eSpmHn//8kafkv///J/z//fWF8RqiwOEMZ Tj3CDOEMPxl0GJ8gm6P5nxDQRLWBjUGagNueMv5iYGBgYIELMJMWFkQ7CWbD Z4YPDBwMyxne4DTzHbodXv9//3/6X4EUZ0X///9/N2k+ifkvQ5KGUYADAAAB jWt4wkFIGwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MTox MyswMDowMFfbTEwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6 NDE6MTMrMDA6MDAmhvTwAAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHly aWdodCwgdXNlIGZyZWVseaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBz UkdCIElFQzYxOTY2LTIuMVet2kcAAAAASUVORK5CYII=" MOUNTAINICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAAAbElEQVQ4y2NgGAX0A/9T/udhl2HBoUObgR+7BBOG2df/azMwMPxn+A/l 6RLQwCCHIiZH0AZCgPYasIVSyH8rBl0Gzv/p2ORZsBhhy/CJ4SvDVwZPBgYG ZpwBDwP/0/4LIvES/4uT6uhRQAYAAAHIFuQn0EGBAAAAJXRFWHRkYXRlOmNy ZWF0ZQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0 ZTptb2RpZnkAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMCaG9PAAAAAmdEVY dGljYzpjb3B5cmlnaHQATm8gY29weXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAA ACF0RVh0aWNjOmRlc2NyaXB0aW9uAHNSR0IgSUVDNjE5NjYtMi4xV63aRwAA AABJRU5ErkJggg==" COASTICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwSh1RzH Zqma+19gBMKZJJX3kUcVBW4k0bTa6W+t6c8cUjTBHCisVUmbETAtSJt2Y0NB NkYEOaTIiQDWZWYA5OKr7/Pln1fSrGRkwFkc5Qq/GMNvnYzyDTTC9mD25Uhf xl/KKkYaF2U4Aqae/GFgaBwHvA+4mrlCcOMlVg9wvofLsfFzx2NNLueSMQ7a /4QXvB8P3J7xUW98FmLvTFDFWy4ET/iM/xCfJlVDbfF9a4zsjagm+i76nVlV tIvIPkfcYmenODsl7g+XBhiXVYDdK+xemQxIfXohKnvvE50Azdie4nAZ8AsG d9gkwN2zR4qbZwEdzsr35jeag+qrOqAu9Owe1og1CfYqO1dnfApgzfm88vgV nsZ0LrwZ172kRPQIDGdXDkBaH3MQyvq2st4wWoGFX2dea9s3UVvtr6A9fWKg I2xCx4iCru+nOOQygvLBq2uXVvpYEIutczvWmo/mgGa3S34Ko/tpqDoF88J4 WoggZSVS2AtOuV789to6OKnZYiRs11y51hVYSllvw30BDKnpHy3n/5Md+xJz AAAA00lEQVQ4y+2RP0tCcRSGn59GdsE2aW1u6Dv4CcJJKFpdXFybg6Ah24SW +hYhKE79GRIdGlriYoFLcIeWEiR53AINrkK4+UznwPuec14OrFk9Zq0ZmbXl 3nKGyKYdD9WBsbFvns8qwpwhsEWJKy7pArDBY0jSNlS889iOiU9+eu8fcWau v6FHnhZ9Grxwxs/CLBY8cujQVz989svr1AzgBWN2eWCTHdqcUgyTtPk5E4sO HDny27ETT9IPOjA2763bYN13q4sSBPd/67Jlo6Xet+a/TAEgDGwkeiDQowAA ACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMFfb TEwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMjZUMDU6NDE6MTMrMDA6 MDAmhvTwAAAAJnRFWHRpY2M6Y29weXJpZ2h0AE5vIGNvcHlyaWdodCwgdXNl IGZyZWVseaea8IIAAAAhdEVYdGljYzpkZXNjcmlwdGlvbgBzUkdCIElFQzYx OTY2LTIuMVet2kcAAAAASUVORK5CYII=" FINECLOUDSUN = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABUElEQVQ4y8WRzyuDARjHv+9rwgqhlSmH1UR+lIMkcVeOzooTF1Ir7eDk D1AOcmKO3Kx2We2w01iLGy4ypyVJI1kzbx8HP98fU1I8t+f7PJ/n+T490r8F ASZ+BmxQ8dLN39mIsk+z2xILHHoDs8C2Sx0BMu+Z72vJiPEgS8KvaY0pqGPt GKe6VlSb3ht8dNNGJ0dAkTxQZr6a/0kylACwgBlMiTBZLIbdzbVsAQViRFjj DNilRpIIck/cDawAMZo+jEWAGKYkkaAgESDJEVOvDb08kcL2EVaBEnm2yHHl BJaBPsfORm5Jk+AOSDsN7VHEcNmse7shS4UeeynFxTffDwPrn7kp6VghWqoB xrkuNWAHDiTNVd1gqlXXDok0ZcarANPAolMMcYPFGqP4bXoHSzxyQr17Tjtx vCNLl+2mD8RQv4Y0qIYv1YJyShrP+tN4AbCn9qMuTlMhAAAAJXRFWHRkYXRl OmNyZWF0ZQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwV9tMTAAAACV0RVh0 ZGF0ZTptb2RpZnkAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMCaG9PAAAAAm dEVYdGljYzpjb3B5cmlnaHQATm8gY29weXJpZ2h0LCB1c2UgZnJlZWx5p5rw ggAAACF0RVh0aWNjOmRlc2NyaXB0aW9uAHNSR0IgSUVDNjE5NjYtMi4xV63a RwAAAABJRU5ErkJggg==" FINECLOUDMOON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABKUlEQVQ4y92RsUrDYBSFT0JEHVRcRDpZ3czgIiK41ElEEHFysC9gl/oO rt3q4tCxgnTTJ5AqlaBIhhbUWR0VNabU5nNoG0mKoXPv9N9zz7mce35pqAuL dYpUGRuMPkED+AFSg9ANyvjkWAXy/9PWKOHyyS3nwJEkcYZHFrOfPEqBAJ8a ZRzaQE6SmOMSeOcuLigAFWa6nc0Vbba79jZ55SVuJqCCETn6nm+qFMmQpslF VFDC720PsQ3gmTegRcBKdOhS67vKZAkDk3086vHhB6cJIeeBdBSq4SYIUsBh 593L15HNYuJfTkV3LPCFw3iCpa04eAA0yDDSd3oWjxusTh8mj6E9HWtaTT2o FfJNzWtSde0YjzGBJDGrXS3LlvUH6UnXOjF8DVH9AmTXx8AXLtT3AAAAJXRF WHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwV9tMTAAA ACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMCaG 9PAAAAAmdEVYdGljYzpjb3B5cmlnaHQATm8gY29weXJpZ2h0LCB1c2UgZnJl ZWx5p5rwggAAACF0RVh0aWNjOmRlc2NyaXB0aW9uAHNSR0IgSUVDNjE5NjYt Mi4xV63aRwAAAABJRU5ErkJggg==" LIGHTNINGICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABN0lEQVQ4y82RPyiEcRzGn9/LoFfOIgPKIIZ3VFxJsVwKxYROyigDhltu NKIsspAymm9RstzIcDeI4Z2JdHUlvXflro/l9XZ3L/e6iWd7vr/n+f55ftK/ g6kl2FrSmBzldaWsKUV4mcAFKri8Ax5zUfIqBZLYEhbT5CiT+Flu41JgoKbS yS0eR+wyT3vYsA4kG2oOX7hhuNFwTAU71GaVEfrYweOejvqnLG6T+9aArbpY udCsug0+MzpQTNKHUqYkYamoS7NS2yMNTAVs1N993+cWL1zXD+3hjRy9PjsE 4JGuIHLYa9xzE3hlQaKNZwCWg7TuKOOET9umAvSTAKDMKSfEmcSjysb3aQyR ljgP8s8wSJ4M44oCi8ADsUhhYDijGPrdJnKLJ2Z+LZeIk2pBLuFgWjL8LT4B HQvf4Jmq4YoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMjZUMDU6NDE6 MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTI2VDA1 OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHlyaWdodABObyBjb3B5 cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6ZGVzY3JpcHRpb24A c1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" WINDICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABSklEQVQ4y7WRvUtCURyG3yNhCKXUFAUKoVQSCUHQUkMQjRVREAi1Ba0O +Q+0SFJ/QdFqEAgtfQxRY0M2uEVCYYMIRYMgZE+DmN3bveZQz3TOe96X38eR /hvTOGA0qglFdatzU/o1Rx9Zmtww0to+SJkaaSbpZpgELzwTcbd7uKTK1Dcl SJEn1lgg6BRYBJI2bearvX0C9kAK6LFphjSrrJABTumwPl5QcG3XsE2NZauY 5RUjVxhgunnzSMoroKiL2UtSXebKKkZ45wKPYyBMmQ9m7fIecIDfMRKjwhFh Cuzgl+rzJ9Srdc1xppwqtkRMPj3oUTklVNRucxvz3OPMNUGJfiqcNCrIoCxx hbShO1uFqvIaI6OQfDqub6nBm1pRUFyH1vHGKbm31MDyYXRpSZ0/ht5Uymyp Xeprbd/u/HEtAl6SDLVt/xM+AYOo54FM/HzxAAAAJXRFWHRkYXRlOmNyZWF0 ZQAyMDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwV9tMTAAAACV0RVh0ZGF0ZTpt b2RpZnkAMjAyMS0wMi0yNlQwNTo0MToxMyswMDowMCaG9PAAAAAmdEVYdGlj Yzpjb3B5cmlnaHQATm8gY29weXJpZ2h0LCB1c2UgZnJlZWx5p5rwggAAACF0 RVh0aWNjOmRlc2NyaXB0aW9uAHNSR0IgSUVDNjE5NjYtMi4xV63aRwAAAABJ RU5ErkJggg==" ICEICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABcElEQVQ4y72RMUiVYRSGn+/e0LiSGi7qUGJCXloMFVxyMBJp0DGRWt0D FycJlyAnhxRFHHRxbyhIQQgT805BomgugmBgkDrcEp+W6+3/vf7S5Bm+j3PO +77n/c4H1x7WW/O/0Drn3VfPXHPUe5ejQhHeyyzVvOcLp7TRywGdYStZvdET t+0o5i3uumdjMmHJvA9ilfsem/fEVYe8cRF+Wx0vEZn0uwtuqp+8GW89Vp+V EMoNYMph9XW81ad2J9oNLvrTwnpSACxzRjuAKauiVgGCfKSahrjKihtmwBYn ItXPhXvCX5ZHJ8AMzbwB0qQBfAFAGYC19PEu5C86HTIHtjoFYC5ybvjHJ+e4 4obDmF8TXp3hUVgtIUD4kEB4GA7/JakE0O+I0CGA/ZZdRXha/IMB52y2gkHu RCzZQy78sLJkwjRHvGInSFfc0nNagTHeApjlmxUAHIWXYTtIUjhSWGsWwHWb uDq85d1IljVw7fEXVWawSSVzipoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEt MDItMjZUMDU6NDE6MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAy MDIxLTAyLTI2VDA1OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHly aWdodABObyBjb3B5cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6 ZGVzY3JpcHRpb24Ac1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" UNKNOWNICON = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAAmJLR0QAAKqN IzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflAhoFKQ3zBeW3AAAB uXpUWHRSYXcgcHJvZmlsZSB0eXBlIGljYwAAOI2dU1my4yAM/OcUcwShFY5j s1TN/S8wAtmZJJX3kUcVBW4k0bTa6W9r6c8aUjXBGiisTUm7ETBtSLsOY0NB NkYEKVLlQAAbsjIAcvXV92gAVJJmJSMDzuIoN/jFmH7rYpRvoBP2B7MvR/oy /lRWMdK4KEMJmEbyh4GhcRzwdcDNzBWCG6+xeoDzLS7HhR9XPLbkcm4Z46D/ T3jBR3ng9ozPduOrEHtngireciF4wmf8h/i0qBpqj+9bY2RvRDPRd9HvzKai Q0Suc8RL7OwUV6fE/eHSAOO2CrB7hd0riwGpTy9E9dr7RCdAK3akONwG/ILB HbYI8PDsmeLmVUCns/K9+Y3moPqqDqgLvbqHLWJNgr3KlasrPgWw53peffwK T2M5F96M615SInoEhrMbByB9zDUIZX9b3W+YvcLGzyPvtV83Ud/tb6AjfWKg M2xCZUZB1/dTHHKdQbnw7tqpjT4WxGr73Mpec+kOaHa75Kcwup+GqkswL4yH hQhSdyKFveCQ88Vvr62Dg7ptRsJ2rpVb24G11v02vC6AKS39A7wt/0A6usWN AAABDUlEQVQ4y+2QsS5DYRiGn9OiGlNJMGGphEUMjaSIgTB06SZhYCgmowsQ 1yAkvQODWUKsEoK4A9HBYDLU0IbH4FRzek56AeJZ/vz/97z5vu+Hf34wHZ6B ewbgJhi4myz3eOybT5YBHHMZXALXHE0OlDwz47Y1q2bAEXMOmnM4WV/0xnMn waxVH50CU2DaCUsW4oFrVY/CW9maV16409qqU9+wruqzc+FLrwuu2OeAeWei cr8ntml6YBBWpsEiOB75J/ftpABg1lWwYqpznMtY4BDAdXPQ6tbWB2zEAndJ e7YaNfiK1epdAkGTh1jtlm5Y9DMy0KtDdMeKH7/6i/PJVmR782wxS517ToN3 /izfjC3cDxA6lqoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMjZUMDU6 NDE6MTMrMDA6MDBX20xMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTI2 VDA1OjQxOjEzKzAwOjAwJob08AAAACZ0RVh0aWNjOmNvcHlyaWdodABObyBj b3B5cmlnaHQsIHVzZSBmcmVlbHmnmvCCAAAAIXRFWHRpY2M6ZGVzY3JpcHRp b24Ac1JHQiBJRUM2MTk2Ni0yLjFXrdpHAAAAAElFTkSuQmCC" CLOUD = [CLOUDICON] FINE = [SUN,MOON0,MOON1,MOON2,MOON3,MOON4,MOON5,MOON6,MOON7] ONCERAIN = [ONCERAINSUN,ONCERAINMOON] SOMERAIN = [SOMERAINSUN,SOMERAINMOON] RAIN = [RAINICON] HEAVYRAIN = [HEAVYRAINICON] RAINSNOW = [RAINSNOWICON] ONCESNOW = [ONCESNOWSUN] SOMESNOW = [SOMESNOWSUN] SNOW = [SNOWICON] HEAVYSNOW = [HEAVYSNOWICON] FOG = [FOGICON] THEN = [THENICON] SOME = [SOMEICON] ONCE = [ONCEICON] TWICE = [TWICEICON] MOUNTAIN = [MOUNTAINICON] COAST = [COASTICON] FINECLOUD = [FINECLOUDSUN,FINECLOUDMOON] LIGHTNINGRAIN = [LIGHTNINGICON] LIGHTNINGSNOW = [LIGHTNINGICON] WINDRAIN = [WINDICON] WINDSNOW = [WINDICON] ICE = [ICEICON] UNKNOWN = [UNKNOWNICON] WEEK = { "1" => "月", "2" => "火", "3" => "水", "4" => "木", "5" => "金", "6" => "土", "7" => "日" } WEATHERCODE = { 100 => "晴", 101 => "晴時々曇", 102 => "晴一時雨", 103 => "晴時々雨", 104 => "晴一時雪", 105 => "晴時々雪", 106 => "晴一時雨か雪", 107 => "晴時々雨か雪", 108 => "晴一時雨か雷雨", 110 => "晴後時々曇", 111 => "晴後曇", 112 => "晴後一時雨", 113 => "晴後時々雨", 114 => "晴後雨", 115 => "晴後一時雪", 116 => "晴後時々雪", 117 => "晴後雪", 118 => "晴後雨か雪", 119 => "晴後雨か雷雨", 120 => "晴朝夕一時雨", 121 => "晴朝の内一時雨", 122 => "晴夕方一時雨", 123 => "晴山沿い雷雨", 124 => "晴山沿い雪", 125 => "晴午後は雷雨", 126 => "晴昼頃から雨", 127 => "晴夕方から雨", 128 => "晴夜は雨", 130 => "朝の内霧後晴", 131 => "晴明け方霧", 132 => "晴朝夕曇", 140 => "晴時々雨で雷を伴う", 160 => "晴一時雪か雨", 170 => "晴時々雪か雨", 181 => "晴後雪か雨", 200 => "曇", 201 => "曇時々晴", 202 => "曇一時雨", 203 => "曇時々雨", 204 => "曇一時雪", 205 => "曇時々雪", 206 => "曇一時雨か雪", 207 => "曇時々雨か雪", 208 => "曇一時雨か雷雨", 209 => "霧", 210 => "曇後時々晴", 211 => "曇後晴", 212 => "曇後一時雨", 213 => "曇後時々雨", 214 => "曇後雨", 215 => "曇後一時雪", 216 => "曇後時々雪", 217 => "曇後雪", 218 => "曇後雨か雪", 219 => "曇後雨か雷雨", 220 => "曇朝夕一時雨", 221 => "曇朝の内一時雨", 222 => "曇夕方一時雨", 223 => "曇日中時々晴", 224 => "曇昼頃から雨", 225 => "曇夕方から雨", 226 => "曇夜は雨", 228 => "曇昼頃から雪", 229 => "曇夕方から雪", 230 => "曇夜は雪", 231 => "曇海上海岸は霧か霧雨", 240 => "曇時々雨で雷を伴う", 250 => "曇時々雪で雷を伴う", 260 => "曇一時雪か雨", 270 => "曇時々雪か雨", 281 => "曇後雪か雨", 300 => "雨", 301 => "雨時々晴", 302 => "雨時々止む", 303 => "雨時々雪", 304 => "雨か雪", 306 => "大雨", 308 => "雨で暴風を伴う", 309 => "雨一時雪", 311 => "雨後晴", 313 => "雨後曇", 314 => "雨後時々雪", 315 => "雨後雪", 316 => "雨か雪後晴", 317 => "雨か雪後曇", 320 => "朝の内雨後晴", 321 => "朝の内雨後曇", 322 => "雨朝晩一時雪", 323 => "雨昼頃から晴", 324 => "雨夕方から晴", 325 => "雨夜は晴", 326 => "雨夕方から雪", 327 => "雨夜は雪", 328 => "雨一時強く降る", 329 => "雨一時霙", 340 => "雪か雨", 350 => "雨で雷を伴う", 361 => "雪か雨後晴", 371 => "雪か雨後曇", 400 => "雪", 401 => "雪時々晴", 402 => "雪時々止む", 403 => "雪時々雨", 405 => "大雪", 406 => "風雪強い", 407 => "暴風雪", 409 => "雪一時雨", 411 => "雪後晴", 413 => "雪後曇", 414 => "雪後雨", 420 => "朝の内雪後晴", 421 => "朝の内雪後曇", 422 => "雪昼頃から雨", 423 => "雪夕方から雨", 425 => "雪一時強く降る", 426 => "雪後霙", 427 => "雪一時霙", 450 => "雪で雷を伴う" } WARNINGCODE = { "02" => {:name => "暴風雪", :type => "警報"}, "03" => {:name => "大雨", :type => "警報"}, "04" => {:name => "洪水", :type => "警報"}, "05" => {:name => "暴風", :type => "警報"}, "06" => {:name => "大雪", :type => "警報"}, "07" => {:name => "波浪", :type => "警報"}, "08" => {:name => "高潮", :type => "警報"}, "10" => {:name => "大雨", :type => "注意報"}, "12" => {:name => "大雪", :type => "注意報"}, "13" => {:name => "風雪", :type => "注意報"}, "14" => {:name => "雷", :type => "注意報"}, "15" => {:name => "強風", :type => "注意報"}, "16" => {:name => "波浪", :type => "注意報"}, "17" => {:name => "融雪", :type => "注意報"}, "18" => {:name => "洪水", :type => "注意報"}, "19" => {:name => "高潮", :type => "注意報"}, "20" => {:name => "濃霧", :type => "注意報"}, "21" => {:name => "乾燥", :type => "注意報"}, "22" => {:name => "雪崩", :type => "注意報"}, "23" => {:name => "低温", :type => "注意報"}, "24" => {:name => "霜", :type => "注意報"}, "25" => {:name => "着氷", :type => "注意報"}, "26" => {:name => "着雪", :type => "注意報"}, "32" => {:name => "暴風雪", :type => "特別警報"}, "33" => {:name => "大雨", :type => "特別警報"}, "35" => {:name => "暴風", :type => "特別警報"}, "36" => {:name => "大雪", :type => "特別警報"}, "37" => {:name => "波浪", :type => "特別警報"}, "38" => {:name => "高潮", :type => "特別警報"} } WEATHERCODEICON = { 100 => [FINE], # 晴 101 => [FINE,SOME,CLOUD], # 晴時々曇 102 => [FINE,ONCE,RAIN], # 晴一時雨 103 => [FINE,ONCE,CLOUD], # 晴時々雨 104 => [FINE,ONCE,SNOW], # 晴一時雪 105 => [FINE,SOME,SNOW], # 晴時々雪 106 => [FINE,ONCE,RAIN], # 晴一時雨か雪 107 => [FINE,SOME,RAIN], # 晴時々雨か雪 108 => [FINE,ONCE,LIGHTNINGRAIN], # 晴一時雨か雷雨 110 => [FINE,THEN,FINECLOUD], # 晴後時々曇 111 => [FINE,THEN,CLOUD], # 晴後曇 112 => [FINE,THEN,ONCERAIN], # 晴後一時雨 113 => [FINE,THEN,SOMERAIN], # 晴後時々雨 114 => [FINE,THEN,RAIN], # 晴後雨 115 => [FINE,ONCE,ONCESNOW], # 晴後一時雪 116 => [FINE,THEN,SOMESNOW], # 晴後時々雪 117 => [FINE,THEN,SNOW], # 晴後雪 118 => [FINE,THEN,RAINSNOW], # 晴後雨か雪 119 => [FINE,THEN,LIGHTNINGRAIN], # 晴後雨か雷雨 120 => [FINE,TWICE,RAIN], # 晴朝夕一時雨 121 => [RAIN,THEN,FINE], # 晴朝の内一時雨 122 => [FINE,ONCE,RAIN], # 晴夕方一時雨 123 => [FINE,MOUNTAIN,LIGHTNINGRAIN], # 晴山沿い雷雨 124 => [FINE,MOUNTAIN,SNOW], # 晴山沿い雪 125 => [FINE,ONCE,LIGHTNINGRAIN], # 晴午後は雷雨 126 => [FINE,THEN,RAIN], # 晴昼頃から雨 127 => [FINE,THEN,RAIN], # 晴夕方から雨 128 => [FINE,THEN,RAIN], # 晴夜は雨 130 => [FOG,THEN,FINE], # 朝の内霧後晴 131 => [FOG,THEN,FINE], # 晴明け方霧 132 => [FINE,TWICE,CLOUD], # 晴朝夕曇 140 => [FINE,SOME,LIGHTNINGRAIN], # 晴時々雨で雷を伴う 160 => [FINE,ONCE,RAINSNOW], # 晴一時雪か雨 170 => [FINE,SOME,RAINSNOW], # 晴時々雪か雨 181 => [FINE,THEN,RAINSNOW], # 晴後雪か雨 200 => [CLOUD], # 曇 201 => [CLOUD,SOME,FINE], # 曇時々晴 202 => [CLOUD,ONCE,RAIN], # 曇一時雨 203 => [CLOUD,SOME,RAIN], # 曇時々雨 204 => [CLOUD,ONCE,SNOW], # 曇一時雪 205 => [CLOUD,SOME,RAIN], # 曇時々雪 206 => [CLOUD,ONCE,RAINSNOW], # 曇一時雨か雪 207 => [CLOUD,SOME,RAINSNOW], # 曇時々雨か雪 208 => [CLOUD,ONCE,LIGHTNINGRAIN], # 曇一時雨か雷雨 209 => [FOG], # 霧 210 => [CLOUD,THEN,FINECLOUD], # 曇後時々晴 211 => [CLOUD,THEN,FINE], # 曇後晴 212 => [CLOUD,THEN,ONCERAIN], # 曇後一時雨 213 => [CLOUD,THEN,RAIN], # 曇後時々雨 214 => [CLOUD,THEN,RAIN], # 曇後雨 215 => [CLOUD,THEN,ONCERAIN], # 曇後一時雪 216 => [CLOUD,THEN,SNOW], # 曇後時々雪 217 => [CLOUD,THEN,SNOW], # 曇後雪 218 => [CLOUD,THEN,RAINSNOW], # 曇後雨か雪 219 => [CLOUD,THEN,LIGHTNINGRAIN], # 曇後雨か雷雨 220 => [CLOUD,TWICE,ONCERAIN], # 曇朝夕一時雨 221 => [ONCERAIN,THEN,CLOUD], # 曇朝の内一時雨 222 => [CLOUD,ONCE,RAIN], # 曇夕方一時雨 223 => [CLOUD,SOME,RAIN], # 曇日中時々晴 224 => [CLOUD,THEN,RAIN], # 曇昼頃から雨 225 => [CLOUD,THEN,RAIN], # 曇夕方から雨 226 => [CLOUD,THEN,RAIN], # 曇夜は雨 228 => [CLOUD,THEN,SNOW], # 曇昼頃から雪 229 => [CLOUD,THEN,SNOW], # 曇夕方から雪 230 => [CLOUD,THEN,SNOW], # 曇夜は雪 231 => [CLOUD,COAST,FOG], # 曇海上海岸は霧か霧雨 240 => [CLOUD,SOME,LIGHTNINGRAIN], # 曇時々雨で雷を伴う 250 => [CLOUD,SOME,LIGHTNINGSNOW], # 曇時々雪で雷を伴う 260 => [CLOUD,ONCE,RAINSNOW], # 曇一時雪か雨 270 => [CLOUD,SOME,RAINSNOW], # 曇時々雪か雨 281 => [CLOUD,THEN,RAINSNOW], # 曇後雪か雨 300 => [RAIN], # 雨 301 => [RAIN,SOME,FINE], # 雨時々晴 302 => [RAIN,SOME,CLOUD], # 雨時々止む 303 => [RAIN,SOME,SNOW], # 雨時々雪 304 => [RAINSNOW], # 雨か雪 306 => [HEAVYRAIN], # 大雨 308 => [WINDRAIN], # 雨で暴風を伴う 309 => [RAIN,ONCE,SNOW], # 雨一時雪 311 => [RAIN,THEN,FINE], # 雨後晴 313 => [RAIN,THEN,CLOUD], # 雨後曇 314 => [RAIN,SOME,SNOW], # 雨後時々雪 315 => [RAIN,THEN,SNOW], # 雨後雪 316 => [RAINSNOW,THEN,FINE], # 雨か雪後晴 317 => [RAINSNOW,THEN,CLOUD], # 雨か雪後曇 320 => [RAIN,THEN,FINE], # 朝の内雨後晴 321 => [RAIN,THEN,CLOUD], # 朝の内雨後曇 322 => [RAIN,TWICE,SNOW], # 雨朝晩一時雪 323 => [RAIN,THEN,FINE], # 雨昼頃から晴 324 => [RAIN,THEN,FINE], # 雨夕方から晴 325 => [RAIN,THEN,FINE], # 雨夜は晴 326 => [RAIN,THEN,SNOW], # 雨夕方から雪 327 => [RAIN,THEN,SNOW], # 雨夜は雪 328 => [RAIN,ONCE,HEAVYRAIN], # 雨一時強く降る 329 => [RAIN,ONCE,ICE], # 雨一時霙 340 => [RAINSNOW], # 雪か雨 350 => [LIGHTNINGRAIN], # 雨で雷を伴う 361 => [SNOW], # 雪か雨後晴 371 => [RAINSNOW,THEN,CLOUD], # 雪か雨後曇 400 => [SNOW], # 雪 401 => [SNOW,SOME,FINE], # 雪時々晴 402 => [SNOW,SOME,CLOUD], # 雪時々止む 403 => [SNOW,SOME,RAIN], # 雪時々雨 405 => [HEAVYSNOW], # 大雪 406 => [WINDSNOW], # 風雪強い 407 => [WINDSNOW], # 暴風雪 409 => [SNOW,ONCE,RAIN], # 雪一時雨 411 => [SNOW,THEN,FINE], # 雪後晴 413 => [SNOW,THEN,CLOUD], # 雪後曇 414 => [SNOW,THEN,RAIN], # 雪後雨 420 => [SNOW,THEN,FINE], # 朝の内雪後晴 421 => [SNOW,THEN,CLOUD], # 朝の内雪後曇 422 => [SNOW,THEN,RAIN], # 雪昼頃から雨 423 => [SNOW,THEN,RAIN], # 雪夕方から雨 425 => [SNOW,ONCE,HEAVYSNOW], # 雪一時強く降る 426 => [SNOW,THEN,ICE], # 雪後霙 427 => [SNOW,ONCE,ICE], # 雪一時霙 450 => [LIGHTNINGSNOW], # 雪で雷を伴う } $forecast = JSON.parse(URI.open("https://www.jma.go.jp/bosai/forecast/data/forecast/#{$pref}.json", :read_timeout => 120).read) def forecastdays fcarea = $forecast.first['timeSeries'][0]['areas'].select {|x| x['area']['code'] == $area }.first fctime = $forecast.first['timeSeries'][0]['timeDefines'] tmarea = $forecast.first['timeSeries'][2]['areas'].select {|x| x['area']['code'] == $amedas }.first['temps'] tmtime = $forecast.first['timeSeries'][2]['timeDefines'] pparea = $forecast.first['timeSeries'][1]['areas'].select {|x| x['area']['code'] == $area }.first['pops'] pptime = $forecast.first['timeSeries'][1]['timeDefines'] temp = [[],[],[]] pop = [[],[],[]] tmtime.each_with_index do |t,i| if Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") < 24.hours temp[0].push(tmarea[i]) elsif Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") >= 24.hours and Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") <= 48.hours temp[1].push(tmarea[i]) elsif Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") > 48.hours temp[2].push(tmarea[i]) end end pptime.each_with_index do |t,i| if Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") < 24.hours pop[0].push(pparea[i]) elsif Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") >= 24.hours and Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") <= 48.hours pop[1].push(pparea[i]) elsif Time.parse(t) - Time.parse("#{$dymd}T00:00:00+09:00") > 48.hours pop[2].push(pparea[i]) end end out = [] for i in 0..fctime.length-1 out.push({ :date => Time.parse(fctime[i]), :weather => fcarea['weatherCodes'][i].to_i, :wind => fcarea['winds'][i], :wave => fcarea['waves'][i], :temp => temp[i], :pop => pop[i] }) end return out end def forecastweek fcarea = $forecast.last['timeSeries'][0]['areas'].select {|x| x['area']['code'] == $area }.first fctime = $forecast.last['timeSeries'][0]['timeDefines'] tmarea = $forecast.last['timeSeries'][1]['areas'].select {|x| x['area']['code'] == $amedas }.first tmtime = $forecast.last['timeSeries'][1]['timeDefines'] temp = [] tmtime.each_with_index do |t,i| temp.push({ :min => tmarea['tempsMin'][i], :minup => tmarea['tempsMinUpper'][i], :minlo => tmarea['tempsMinLower'][i], :max => tmarea['tempsMax'][i], :maxup => tmarea['tempsMaxUpper'][i], :maxlo => tmarea['tempsMaxLower'][i] }) end out = [] for i in 0..fctime.length-1 out.push({ :date => Time.parse(fctime[i]), :weather => fcarea['weatherCodes'][i].to_i, :temp => temp[i], :pop => fcarea['pops'][i] }) end return out end def overviewdays json = JSON.parse(URI.open("https://www.jma.go.jp/bosai/forecast/data/overview_forecast/#{$pref}.json", :read_timeout => 120).read) text = "#{json['headlineText']}\n\n" text += "#{json['text']}" text.gsub!(/([\p{Hiragana}\p{Katakana}])\s*$\n/, '\1') text.gsub!(/^ /, '') text.gsub!(/[>】)]/, '\0\n') text.gsub!(/[(]/, '\n\0') text.gsub!(/[^線][しりが]、/, '\0\n') text.gsub!(/[^の]ため、/, '\0\n') text.gsub!(/時間降水量は、/, '\0\n') text.gsub!(/。/, '\0\n') text.gsub!(/\\n/, "\n") text.gsub!(/\n^ +$\n/, "\n") text.gsub!(/\n\s*\n\s*\n/, "\n\n") text = "#{Time.parse(json['reportDatetime']).strftime("%Y年%m月%d日 %H時%M分")}\n\n" + text text = NKF.nkf('-X -w', text).tr('0-9.', '0-9.') return text end def overviewweek json = JSON.parse(URI.open("https://www.jma.go.jp/bosai/forecast/data/overview_week/#{$pref}.json", :read_timeout => 120).read) text = "#{json['headTitle']}\n\n" text += "#{json['text']}" date = "#{text.lines.first.strip} #{Time.parse(json['reportDatetime']).strftime("%Y年%m月%d日 %H時%M分")}" a = [date] + text.lines[1..-1] text = a.join("\n\n") text.gsub!(/([\p{Hiragana}\p{Katakana}])\s*$\n/, '\1') text.gsub!(/^ /, '') text.gsub!(/[>】)]/, '\0\n') text.gsub!(/[(]/, '\n\0') text.gsub!(/[^線][くしりが]、/, '\0\n') text.gsub!(/[^の]ため、/, '\0\n') text.gsub!(/まで /, '\0\n\n') text.gsub!(/。/, '\0\n\n') text.gsub!(/\\n/, "\n") text.gsub!(/\n^ +$\n/, "\n") text.gsub!(/\n\s*\n\s*\n/, "\n\n") text = NKF.nkf('-X -w', text).tr('0-9.', '0-9.') return text.strip end def minmax(list,st,et) a = list.map {|date,val| val if date >= st and date <= et }.reject(&:blank?) if a.min == a.max return "#{a.min}" else return "#{a.min}〜#{a.max}" end end def windwarn(a) if a.length > 1 dir = a.map {|d,s| d}.uniq spd = a.map {|d,s| s}.uniq min = a.map{|d,s|s}.min max = a.map{|d,s|s}.max if dir.length > 1 and spd.length == 1 return "#{dir.join('〜')} #{spd.join('')} m/s" elsif dir.length > 1 and spd.length > 2 return "#{dir.join('〜')} #{min}〜#{max} m/s" elsif dir.length > 1 and spd.length > 1 return "#{dir.join('〜')} #{spd.join('〜')} m/s" else return "#{dir.join('')} #{min}〜#{max} m/s" end else return "#{a[0][0]} #{a[0][1]} m/s" end end def warningtimes(t,l,s,i) v = l.select {|x| x['type'] == s }.map {|x| x['localAreas'][i]['values'] } return Hash[t.zip(v.flatten)] end def warning json = JSON.parse(URI.open("https://www.jma.go.jp/bosai/warning/data/warning/#{$pref}.json", :read_timeout => 120).read) warnings = json['areaTypes'].last['areas'].select {|x| x['warnings'] if x['code'] == $local and not x['warnings'].any?{|y| y['status'] == "解除" or y['status'] == "発表警報・注意報はなし"}}.map {|x| x['warnings']}.flatten times = [] json['timeSeries'].each do |x| h = { :times => x['timeDefines'], :warnings => x['areaTypes'].last['areas'].select {|y| y['code'] == $local}.map {|x| x['warnings']}.flatten } times.push(h) end sort = Hash.new{|h,k| h[k] = [] } warnings.each do |x| name = WARNINGCODE[x['code']][:name] time = times.select {|y| y[:warnings].any? {|z| z['code'] == x['code']}}.first warning = time[:warnings].select {|y| y['code'] == x['code']}.first # ['status'] 発表 継続 発表警報・注意報はなし sort[name].push({:time=>time[:times],:warning=>warning}) unless x['status'].match? /なし$/ end listtoday = [] listtomorrow = [] sort.each do |k,v| today = [] tomorrow = [] v.each do |t| times = t[:time].map {|x| Time.parse(x)} if local = t[:warning]['levels'][0]['localAreas'].find_index {|x| x['localAreaName'] == "陸上"} else local = 0 end data = t[:warning]['levels'][0]['localAreas'] unless data.first['localAreaName'].nil? areas = data.map {|x| "#{x['localAreaName']} "} else areas = [""] end i = data[0]['values'].each_index.select {|y| data[0]['values'][y] != "" and data[0]['values'][y] != "00"}[0] j = data[0]['values'].each_index.select {|y| data[0]['values'][y] != "" and data[0]['values'][y] != "00"}[-1] warning = WARNINGCODE[t[:warning]['code']][:type] if k == "雷" or k == "融雪" or k == "霜" or k == "雪崩" or k == "着氷" or k == "着雪" unless data[0]['additions'].nil? details = " #{data[0]['additions'].join(' ')}" else details = '' end if times[i] >= $d24 tomorrow.push("#{warning} #{details}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning} #{details}") tomorrow.push("#{warning} #{details}") else today.push("#{warning} #{details}") end elsif k == "乾燥" effhum = warningtimes(times,t[:warning]['properties'],"実効湿度",local) minhum = warningtimes(times,t[:warning]['properties'],"最小湿度",local) if times[i] >= $d24 tomorrow.push("#{warning} 実効湿度 #{minmax(effhum,$d24,$d48)}% 最小湿度 #{minmax(minhum,$d24,$d48)}%") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning} 実効湿度 #{minmax(effhum,$d0,$d24)}% 最小湿度 #{minmax(minhum,$d24,$d48)}%") tomorrow.push("#{warning} 実効湿度 #{minmax(effhum,$d24,$d48)}% 最小湿度 #{minmax(minhum,$d24,$d48)}%") else today.push("#{warning} 実効湿度 #{minmax(effhum,$d0,$d24)}% 最小湿度 #{minmax(minhum,$d0,$d24)}%") end elsif k == "低温" if times[i] >= $d24 tomorrow.push("#{warning}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning}") tomorrow.push("#{warning}") else today.push("#{warning}") end elsif k == "大雨" if times[i] >= $d24 tomorrow.push("#{warning}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning}") tomorrow.push("#{warning}") else today.push("#{warning}") end elsif k == "大雪" if times[i] >= $d24 tomorrow.push("#{warning}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning}") tomorrow.push("#{warning}") else today.push("#{warning}") end elsif k == "強風" or k == "暴風" or k == "風雪" or k == "暴風雪" todayarea = [] tomorrowarea = [] for area in 0..data.length-1 speed = [] dir = [] t[:warning]['properties'].select {|x| x['type'] == "最大風速" }.each do |x| speed += x['localAreas'][area]['values'] end t[:warning]['properties'].select {|x| x['type'] == "風向" }.each do |x| dir += x['localAreas'][area]['windDirections'].map {|y| y['value']} end h = Hash[times.zip(dir.zip(speed))] if times[i] >= $d24 a = h.select {|k,v| k >= $d24 and not v.join('').empty? }.map {|k,v| v}.uniq tomorrowarea.push("#{areas[area]}#{windwarn(a)}") elsif times[i] < $d24 and times[j] >= $d24 a = h.select {|k,v| k >= $d0 and k <= $d24 and not v.join('').empty? }.map {|k,v| v}.uniq todayarea.push("#{areas[area]}#{windwarn(a)}") b = h.select {|k,v| k >= $d24 and not v.join('').empty? }.map {|k,v| v}.uniq tomorrowarea.push("#{areas[area]}#{windwarn(b)}") else a = h.select {|k,v| k >= $d0 and k <= $d24 and not v.join('').empty? }.map {|k,v| v}.uniq todayarea.push("#{areas[area]}#{windwarn(a)}") end end today.push("#{warning} #{todayarea.join(' ')}") if todayarea.length > 0 tomorrow.push("#{warning} #{tomorrowarea.join(' ')}") if tomorrowarea.length > 0 elsif k == "波浪" wave = warningtimes(times,t[:warning]['properties'],"波高",0) if times[i] >= $d24 tomorrow.push("#{warning} #{minmax(wave,$d24,$d48)}m") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning} #{minmax(wave,$d0,$d24)}m") tomorrow.push("#{warning} #{minmax(wave,$d24,$d48)}m") else today.push("#{warning} #{minmax(wave,$d0,$d24)}m") end elsif k == "洪水" if times[i] >= $d24 tomorrow.push("#{warning}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning}") tomorrow.push("#{warning}") else today.push("#{warning}") end elsif k == "濃霧" if times[i] >= $d24 tomorrow.push("#{warning}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning}") tomorrow.push("#{warning}") else today.push("#{warning}") end elsif k == "高潮" if times[i] >= $d24 tomorrow.push("#{warning}") elsif times[i] < $d24 and times[j] >= $d24 today.push("#{warning}") tomorrow.push("#{warning}") else today.push("#{warning}") end end unless k == "乾燥" or k == "雪崩" or k == "低温" or k == "霜" if times[i] >= $d24 and times[j] >= $d48 tomorrow[-1] += "  #{times[i].strftime('%k').strip}〜" elsif times[i] >= $d24 tomorrow[-1] += "  #{times[i].strftime('%k').strip}〜#{(times[j]+3.hours).strftime('%k').strip}時" elsif times[j] >= $d48 today[-1] += " #{times[i].strftime('%k').strip}時〜" tomorrow[-1] += " 今後も" elsif times[j] >= $d24 today[-1] += " #{times[i].strftime('%k').strip}時〜" tomorrow[-1] += " 〜#{(times[j]+3.hours).strftime('%k').strip}時" else today[-1] += " #{times[i].strftime('%k').strip}〜#{(times[j]+3.hours).strftime('%k').strip}時" end end end today.map! {|x| x.gsub(/〜0時/,'〜24時')} tomorrow.map! {|x| x.gsub(/〜0時/,'〜24時')} listtoday.push("#{k} #{today.join(' ')}") if today.length > 0 listtomorrow.push("#{k} #{tomorrow.join(' ')}") if tomorrow.length > 0 end return listtoday,listtomorrow end def typhoonimg begin img = Magick::Image::from_blob(URI.open('http://www.jma.go.jp/jp/typh/images/wide/all-00.png').read).first.crop(0,0,480,335,true) return "| refresh=true image=#{Base64.encode64(img.to_blob).gsub(/\n/, '')}" rescue return '❌' end end def typhooninfo(list) info = [] list.each do |id| data = JSON.parse(URI.open("https://www.jma.go.jp/bosai/typhoon/data/#{id}/specifications.json").read) analysis = data.select{|x|x['part']['jp']=='実況'}.first name = analysis['category']['jp'] if analysis['maximumWind']['sustained']['m/s'] >= 54 color = $wrncolor elsif analysis['maximumWind']['sustained']['m/s'] >= 44 color = $advcolor else color = $textcolor end if name == "台風" info.push("--#{name} 第#{id[-2..-1].to_i}号 #{analysis['location']} #{analysis['course']} #{analysis['speed']['km/h']} km/h #{analysis['pressure']} hPa #{analysis['maximumWind']['sustained']['m/s']} m/s (#{analysis['maximumWind']['gust']['m/s']} m/s) | color=#{color}") else info.push("--#{name} 第#{id[-2..-1].to_i}号 #{analysis['location']} #{analysis['course']} #{analysis['speed']['km/h']} km/h #{analysis['pressure']} hPa | color=#{color}") end end return info end def earthquake json = JSON.parse(URI.open("https://www.jma.go.jp/bosai/quake/data/list.json", :read_timeout => 120).read) list = json.select do |x| x['int'].any? {|y| y['city'].any? {|z| z['code'] == $quakearea and z['maxi'].to_i > 1} } end if list.length > 0 q = list.first mag = q['int'].select {|x| x['city'].any? {|y| y['code'] == $quakearea}}[0]['city'].select {|x| x['code'] == $quakearea}[0]['maxi'] dep = (q['acd'].to_i / 5)#.round(-1) return "#{q['mag']} (#{mag}) #{q['anm']} #{dep} km #{Time.parse(q['at']).strftime('%-m/%-d %H:%M')}" else return nil end end # 🌞🌝🌛🌜🌚🌕🌖🌗🌘🌑🌒🌓🌔🌙 def julian(year, month, day) a = (14 - month) / 12 y = year + 4800- a m = (12 * a) - 3 + month return day + (153 *m + 2) / 5 + (365 * y) + y/4 - y/100 + y/400 - 32045 end def forecasticon(forecast,istoday) icons = WEATHERCODEICON[forecast].clone $list = Magick::ImageList.new icons.each do |i| if i.length > 2 and $dt > $sunset and istoday p = (julian($dt.year, $dt.month, $dt.day) - julian(2000, 1, 6)) % 29.530588853 if p < 1.84566 j = 1 # new elsif p < 5.53699 j = 2 # waxing crescent elsif p < 9.22831 j = 3 # first quarter elsif p < 12.91963 j = 4 # waxing gibbous elsif p < 16.61096 j = 5 # full elsif p < 20.30228 j = 6 # waning gibbous elsif p < 23.99361 j = 7 # last quarter elsif p < 27.68493 j = 8 # waning crescent else j = 2 # new end elsif i.length > 1 and $dt > $sunset and istoday j = 1 else j = 0 end png = Magick::Image.from_blob(Base64.decode64(i[j])) do |img| img.format = 'PNG' img.background_color = 'transparent' end img = png[0].to_blob list = Magick::ImageList.new list.from_blob(img) $list += list end img = $list.append(false).resize_to_fit!(72,24) if img.properties['png:IHDR.width,height'] == "24, 24" newimg = ::Magick::Image.new(72,24){ |i| i.format = 'PNG' i.background_color = 'transparent' }.matte_floodfill(1,1) newimg.composite!(img, Magick::CenterGravity, Magick::OverCompositeOp) img = newimg end img = img.modulate(0.01,1.0,1.0) unless isdarkmode return Base64.encode64(img.to_blob).gsub(/\n/, '') end def moon(thedate) p = (julian(thedate.year, thedate.month, thedate.day) - julian(2000, 1, 6)) % 29.530588853 if p < 1.84566 return "🌑" # new elsif p < 5.53699 return "🌒" # waxing crescent elsif p < 9.22831 return "🌓" # first quarter elsif p < 12.91963 return "🌔" # waxing gibbous elsif p < 16.61096 return "🌕" # full elsif p < 20.30228 return "🌖" # waning gibbous elsif p < 23.99361 return "🌗" # last quarter elsif p < 27.68493 return "🌘" # waning crescent else return "🌑" # new end end def currenticon(code) case code.to_s # darksky/visualcrossing when "clear-day" icon = "☀️" when "clear-night" # icon = "🌙" icon = moon($dt) when "rain" icon = "☔️" when "snow" icon = "❄️" when "sleet" icon = "💦" when "wind" icon = "💨" when "fog" icon = "🌫" when "cloudy" icon = "☁️" when "partly-cloudy-day" icon = "⛅️" when "partly-cloudy-night" icon = "⛅️" # climacell when "0" # Unknown icon = "❓" when "1000" # Clear isnight ? (icon = moon($dt)) : (icon = "☀️") when "1001" # Cloudy icon = "☁️" when "1100" # Mostly Clear isnight ? (icon = moon($dt)) : (icon = "⛅️") when "1101" # Partly Cloudy icon = "⛅️" when "1102" # Mostly Cloudy icon = "🌥" when "2000" # Fog icon = "🌫" when "2100" # Light Fog icon = "🌫" when "3000" # Light Wind icon = "💨" when "3001" # Wind icon = "💨" when "3002" # Strong Wind icon = "💨" when "4000" # Drizzle icon = "💧" when "4001" # Rain icon = "💧" when "4200" # Light Rain icon = "💧" when "4201" # Heavy Rain icon = "💦" when "5000" # Snow icon = "❄️" when "5001" # Flurries icon = "❄️" when "5100" # Light Snow icon = "❄️" when "5101" # Heavy Snow icon = "☃️" when "6000" # Freezing Drizzle icon = "💧" when "6001" # Freezing Rain icon = "🧊" when "6200" # Light Freezing Rain icon = "🧊" when "6201" # Heavy Freezing Rain icon = "🧊" when "7000" # Ice Pellets icon = "🧊" when "7101" # Heavy Ice Pellets icon = "🧊" when "7102" # Light Ice Pellets icon = "🧊" when "8000" # Thunderstorm icon = "⚡️" # 気象庁 when "晴" isnight ? (icon = moon($dt)) : (icon = "☀️") when "雨" icon = "☔️" when "雪" icon = "❄️" when "霧" icon = "🌫" when "曇" icon = "☁️" when /[一-龠々]/ icon = code else icon = "❓" end end def formatnum(s) return NKF.nkf('-X -w', s).tr('0-9.', '0-9.') end def formatdaysmenu(h) wind = h[:wind].split(' ')[0].gsub('メートル',' m/s') if i = h[:wind].split(' ').index{|x| x.match?(/では/)} wind += " " + h[:wind].split(' ')[1..i-2].join(' ').gsub('メートル',' m/s') wind += "\n" + h[:wind].split(' ')[i-1..-1].join(' ').gsub('メートル',' m/s') else wind += " " + h[:wind].split(' ')[1..-1].join(' ').gsub('メートル',' m/s') end wave = h[:wave].gsub('メートル',' m') text = "#{WEATHERCODE[h[:weather]]}\n#{wind}\n波 #{wave}" return formatnum(text) end def winddirconv(degree) cardinals = ['北', '北北東', '北東', '東北東', '東', '東南東', '南東', '南南東', '南', '南南西', '南西', '西南西', '西', '西北西', '北西', '北北西'] dir = ((degree * 16) / 360).round(0) cardinals[dir] end def subdot(i) if i > 3 '͔' elsif i > 2 '⃨' elsif i > 1 '̤' elsif i > 0 '̣' else '' end end def superdot(i) if i > 3 # '⃜' '͐' elsif i > 2 '⃛' elsif i > 1 '̈' elsif i > 0 '̇' else '' end end def temprange(lt,t,ht) l = t-lt h = ht-t if t > 9 return "#{t.to_s[0..-2]}#{subdot(l)}#{t.to_s[-1]}#{superdot(h)}" else return "#{t.to_s}#{subdot(l)}#{superdot(h)}" end end def percentbar(s) i = s.to_i if i > 90 return "▓" elsif i > 80 return "█" elsif i > 70 return "▇" elsif i > 60 return "▆" elsif i > 50 return "▅" elsif i > 40 return "▄" elsif i > 30 return "▃" elsif i > 20 return "▂" elsif i > 10 return "▁" elsif i > 0 return "_" else return " " end end mapurl = JSON.parse(URI.open("https://www.jma.go.jp/bosai/weather_map/data/list.json", :read_timeout => 120).read)['near']['now'][-1] png = Magick::Image.from_blob(URI.open("https://www.jma.go.jp/bosai/weather_map/data/png/#{mapurl}", :read_timeout => 120).read) do |img| img.format = 'PNG' img.background_color = 'transparent' end isdarkmode ? (map = png[0].negate.modulate(1.0,1.0,2.0)) : (map = png[0]) m64 = "| refresh=true image=#{Base64.encode64(map.to_blob).gsub(/\n/, '')}" begin $radimglist = Magick::ImageList.new for i in $radxa..$radxb ilist = Magick::ImageList.new for j in $radya..$radyb url = "https://www.jma.go.jp/bosai/jmatile/data/nowc/#{$dtr}00/none/#{$dtr}00/surf/hrpns/8/#{i}/#{j}.png" png = Magick::Image.from_blob(URI.open(url, :read_timeout => 120).read) do |image| image.format = 'PNG' image.background_color = 'transparent' end img = png[0].to_blob list = Magick::ImageList.new ilist.from_blob(img) ilist += list end row = ilist.append(true) rowlist = Magick::ImageList.new $radimglist.push(row) end radpng = $radimglist.append(false).modulate(0.9,2.0,1.0) if isdarkmode radimg = RADMAP.dissolve(radpng,0.3,1, Magick::CenterGravity).level(Magick::QuantumRange/30,Magick::QuantumRange*0.85) else radimg = RADMAP.negate.modulate(0.7,1.0,1.0).dissolve(radpng,0.7,1, Magick::CenterGravity).level(Magick::QuantumRange/30,Magick::QuantumRange*0.85) end r64 = "| refresh=true image=#{Base64.encode64(radimg.to_blob).gsub(/\n/, '')}" rescue r64 = '⚠️' end begin $satimglist = Magick::ImageList.new for i in $satxa..$satxb ilist = Magick::ImageList.new for j in $satya..$satyb url = "https://www.jma.go.jp/bosai/himawari/data/satimg/#{$dts}00/fd/#{$dts}00/#{$sattype}/5/#{i}/#{j}.jpg" jpg = Magick::Image.from_blob(URI.open(url, :read_timeout => 120).read) do |image| image.format = 'JPG' end img = jpg[0].to_blob list = Magick::ImageList.new ilist.from_blob(img) ilist += list end row = ilist.append(true) rowlist = Magick::ImageList.new $satimglist.push(row) end satjpg = $satimglist.append(false) satimg = Magick::Image.new(768,768){ |image| image.format = 'PNG' image.background_color = 'transparent' } satimg.composite!(satjpg, Magick::CenterGravity, Magick::OverCompositeOp) satimg.composite!(SATMAP, Magick::CenterGravity, Magick::OverCompositeOp) s64 = " | refresh=true image=#{Base64.encode64(satimg.to_blob).gsub(/\n/, '')}" rescue s64 = '⚠️' end if isdarkmode $textcolor = 'lightgray' $advcolor = 'yellow' $wrncolor = 'orange' # $textansi = '97' # SwiftBar不具合 $textansi = '33' $rainansi = '36' else $textcolor = 'darkslategray' $advcolor = 'orange red' $wrncolor = 'red' $textansi = '39' $rainansi = '1;36' end ### 観測データ if not $darkskyapi.nil? darksky = JSON.parse(URI.open("https://api.darksky.net/forecast/#{$darkskyapi}/#{$latlon}?units=si").read) temp = darksky['currently']['temperature'].to_f.round(1) apptemp = darksky['currently']['apparentTemperature'].to_f.round(1) dewpoint = darksky['currently']['dewPoint'].to_f.round(1) humidity = (darksky['currently']['humidity'].to_f * 100).round(0) # % pressure = darksky['currently']['pressure'] # hectopascal windspeed = darksky['currently']['windSpeed'].to_f.round(1) # m/s gust = darksky['currently']['windGust'].to_f.round(1) # m/s winddir = darksky['currently']['windBearing'].to_i # deg uv = darksky['currently']['uvIndex'] precip = darksky['currently']['precipIntensity'].to_f.round(1) # mm/h precipprob = darksky['currently']['precipProbability'].to_f.round(1) clouds = (darksky['currently']['cloudCover'] * 100).round(0) # % visibility = darksky['currently']['visibility'].to_f.round(0) # km icon = currenticon(darksky['currently']['icon']) elsif not $openweatherapi.nil? openweather = JSON.parse(URI.open("https://api.openweathermap.org/data/2.5/weather?id=#{$location}&units=metric&lang=ja&appid=#{$openweatherapi}").read) temp = openweather['main']['temp'].to_f.round(1) apptemp = openweather['main']['feels_like'].to_f.round(1) # dewpoint = humidity = (openweather['main']['humidity'].to_f * 100).round(0) # % pressure = openweather['main']['pressure'] # hectopascal windspeed = openweather['wind']['speed'].to_f.round(1) # m/s # gust = openweather['wind']['gust'].to_f.round(1) # m/s winddir = openweather['wind']['deg'].to_i # deg # uv = precip = openweather['rain']['rain.1h'].to_f.round(1) # mm 1hr snow = openweather['snow']['snow.1h'].to_f.round(1) # mm 1hr # precipprob = clouds = (openweather['clouds']['all']).round(0) # % visibility = openweather['visibility'].to_f.round(0) # km icon = openweather['weather']['main'] elsif not $visualcrossingapi.nil? visualcrossing = JSON.parse(URI.open("https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/#{$latlon}?unitGroup=metric&key=#{$visualcrossingapi}&include=alerts%2Ccurrent").read) temp = visualcrossing['currentConditions']['temp'].to_f.round(1) apptemp = visualcrossing['currentConditions']['feelslike'].to_f.round(1) dewpoint = visualcrossing['currentConditions']['dew'].to_f.round(1) humidity = visualcrossing['currentConditions']['humidity'].to_f.round(0) # % pressure = visualcrossing['currentConditions']['pressure'] # hectopascal windspeed = visualcrossing['currentConditions']['windspeed'].to_f.round(1) # m/s gust = visualcrossing['currentConditions']['windgust'].to_f.round(1) # m/s winddir = visualcrossing['currentConditions']['winddir'].to_i # deg if gust.nil? wind = "#{windspeed} m/s #{winddirconv(winddir)}" else wind = "#{windspeed} m/s (#{gust} m/s) #{winddirconv(winddir)}" end # 2021/02/02 wind, pressure, precip null unless visualcrossing['currentConditions']['cloudcover'].nil? cloudcover = visualcrossing['currentConditions']['cloudcover'].round(0) # % else cloudcover = 0 end clouds = "#{cloudcover}%" visibility = visualcrossing['currentConditions']['visibility'].to_f.round(0) # km icon = currenticon(visualcrossing['currentConditions']['icon']) elsif not $climacellapi.nil? climacell = JSON.parse(URI.open("https://data.climacell.co/v4/timelines?location=#{$latlon}×teps=1m&timezone=Asia/Tokyo&fields=temperature,temperatureApparent,dewPoint,humidity,windSpeed,windDirection,windGust,pressureSurfaceLevel,precipitationIntensity,precipitationProbability,precipitationType,visibility,cloudCover,cloudBase,cloudCeiling,weatherCode,epaIndex,epaPrimaryPollutant,epaHealthConcern", 'content-type' => 'application/json', 'apikey' => $climacellapi).read) cccurrent = climacell['data']['timelines'][0]['intervals'][0]['values'] temp = cccurrent['temperature'].to_f.round(1) apptemp = cccurrent['temperatureApparent'].to_f.round(1) dewpoint = cccurrent['dewPoint'].to_f.round(1) humidity = cccurrent['humidity'].to_f.round(0) # % pressure = cccurrent['pressureSurfaceLevel'] # hectopascal windspeed = cccurrent['windSpeed'].to_f.round(1) # m/s gust = cccurrent['windGust'].to_f.round(1) # m/s winddir = cccurrent['windDirection'].to_i # deg wind = "#{windspeed} m/s (#{gust} m/s) #{winddirconv(winddir)}" # uv = cccurrent['uvIndex'] precip = cccurrent['precipitationIntensity'].to_f.round(1) # mm/h precipprob = cccurrent['precipitationProbability'].to_f.round(1) preciptype = cccurrent['precipitationType'].to_i # 0: N/A, 1: Rain, 2: Snow, 3: Freezing Rain, 4: Ice Pellets cloudcover = (cccurrent['cloudCover']).round(0) # % cloudbase = cccurrent['cloudBase'] # km cloudceiling = cccurrent['cloudCeiling'] # km clouds = "#{cloudcover}% (#{cloudbase}→#{cloudceiling} km)" visibility = cccurrent['visibility'].to_f.round(0) # km icon = currenticon(cccurrent['weatherCode'].to_i) epaindex = cccurrent['epaIndex'].to_i epapol = cccurrent['epaPrimaryPollutant'].to_i # 0: PM2.5, 1: PM10, 2: O3, 3: NO2, 4: CO, 5: SO2 epaconcern = cccurrent['epaHealthConcern'].to_i # 0: Good (0-50), 1: Moderate (51-100), 2: Unhealthy for Sensitive Groups (101-150) # 3: Unhealthy (151-200), 4: Very Unhealthy (201-300), 5: Hazardous (>301) end jma = JSON.parse(URI.open("https://www.jma.go.jp/bosai/amedas/data/point/#{$amedas}/#{$dtame}.json", :read_timeout => 120).read) temp = humidity = pressure = precip = 0 wind = gust = mintemp = maxtemp = '' current = jma.map {|k,v| v}.last update = Time.parse(jma.map {|k,v| k}.last).strftime("%H:%M") temp = windspeed = humidity = pressure = snow = 0 winddir = '' temp = current['temp'].first wind = "#{winddirconv(current['windDirection'].first)} #{current['wind'].first} m/s" gust = "#{winddirconv(current['gustDirection'].first)} #{current['gust'].first} m/s #{(current['gustTime']['hour'].to_i+9)%24}時" humidity = current['humidity'].first pressure = current['normalPressure'].first precip = current['precipitation1h'].first precipday = current['precipitation24h'].first snow = current['snow1h'].first unless current['snow1h'].nil? snowday = current['snow24h'].first unless current['snow24h'].nil? mintemp = "#{current['minTemp'].first}° #{(current['minTempTime']['hour'].to_i+9)%24}時" maxtemp = "#{current['maxTemp'].first}° #{(current['maxTempTime']['hour'].to_i+9)%24}時" dewpoint = (temp - (100 - humidity)/5).round(1) capptemp = (temp + 0.33 * (6.105 * humidity/100 * Math::E**((17.27*temp)/(237.7+temp))) - 0.7 * windspeed - 4).round(1) # http://www.bom.gov.au/info/thermal_stress/#atapproximation efftemp = ( 37 - (37-temp)/(0.68-0.0014*humidity+1/(1.76+1.4*windspeed**0.75))-0.29*temp*(1-0.01*humidity)).round(1) # https://link.springer.com/article/10.1007/s00484-011-0453-2 lastquake = earthquake days = forecastdays week = forecastweek warnings = warning icon = (currenticon(WEATHERCODE[days[0][:weather]][0])) if icon.nil? ###### puts "#{icon} #{temp}°" puts "---" puts "#{$place} | color=lightslategray" puts "---" # puts "温 #{temp}° (#{apptemp}°) | color=#{$textcolor}" puts "\033[#{$textansi}m温 #{temp}° (#{efftemp}°) \033[34m#{mintemp} \033[31m#{maxtemp} | color=#{$textcolor} ansi=true" puts "湿 #{humidity}% (#{dewpoint}°) | color=#{$textcolor}" puts "圧 #{pressure} hPa | color=#{$textcolor}" unless pressure.nil? puts "風 #{wind} (#{gust})| color=#{$textcolor}" puts "雨 #{precip} mm/h (#{precipday} mm/d) | color=#{$textcolor}" if precip > 0 puts "雪 #{precip} mm/h (#{snowday} mm/d) | color=#{$textcolor}" if not snow.nil? and snow > 0 # puts "雨 #{precip} mm/h (#{precipprob} mm/h) #{rainfall if rainfall > 0} #{'mm' if rainfall > 0} | color=#{$textcolor}" if precip > 0 or precipprob > 0 puts "雲 #{clouds} | color=#{$textcolor}" unless clouds.nil? puts "視 #{visibility} km | color=#{$textcolor}" unless visibility.nil? puts "紫 #{uv} | color=#{$textcolor}" unless uv.nil? puts "時 #{update} | color=lightslategray" puts "震 #{lastquake} | color=#{$textcolor}" unless lastquake.nil? puts "---" # forecastdays today & tomorrow for i in 0..1 icon = '' case i when 0 date = "今日#{days[0][:date].strftime('%-d')}日" istoday = true when 1 date = "明日#{days[1][:date].strftime('%-d')}日" istoday = false end unless warnings[i].blank? if warnings[i].any? {|line| line.match("警報")} puts "#{date} | color=#{$wrncolor}" else puts "#{date} | color=#{$advcolor}" end "#{formatdaysmenu(days[i])}".each_line do |line| puts "--#{line.strip} | color=#{$textcolor}" end warnings[i].each do |line| if line.match("警報") puts "--#{line.strip} | color=#{$wrncolor}" else puts "--#{line.strip} | color=#{$advcolor}" end end else puts "#{date} | color=#{$textcolor}" "#{formatdaysmenu(days[i])}".each_line do |line| puts "--#{line.strip} | color=#{$textcolor}" end end rain = ['-','-','-','-'] days[i][:pop].reverse.each_with_index do |x,i| rain[3-i] = x end print "--雨 #{rain.join(' ')} | color=white\n" icon = forecasticon(days[i][:weather],istoday) print "\033[#{$textansi}m" if days[i][:temp].empty? print "\t\t\t" elsif days[i][:temp][0] == days[i][:temp][1] print "\t\t\033[31m#{days[i][:temp][1]}˚" else print "\t\033[34m#{days[i][:temp][0]}˚" print "\t\033[31m#{days[i][:temp][1]}˚" end # print "\033[#{$textansi}m ▕\033[#{$rainansi}m" print "\033[#{$textansi}m \033[#{$rainansi}m" print days[i][:pop].map {|x| "#{percentbar(x)}"}.join('').rjust(4,' ') # print "\033[#{$textansi}m▏" print "\033[#{$textansi}m " print "| image=#{icon} color=#{$textcolor} size=16 ansi=true trim=false\n" end # forecastweek week[1][:date] - $dt < 24.hours ? (w = 2) : (w = 1) for i in w..6 date = "#{week[i][:date].strftime('%-d')}日(#{WEEK[week[i][:date].strftime('%u')]})" puts "#{date} | color=#{$textcolor}" if i == 1 and not days[2].nil? "#{formatdaysmenu(days[2])}".each_line do |line| puts "--#{line.strip} | color=#{$textcolor}" end print "--雨 #{week[i][:pop]} | color=white\n" end icon = forecasticon(week[i][:weather],false) print "\033[34m" print "\t" print temprange(week[i][:temp][:minlo].to_i,week[i][:temp][:min].to_i,week[i][:temp][:minup].to_i) print "\033[31m" print "\t" print temprange(week[i][:temp][:maxlo].to_i,week[i][:temp][:max].to_i,week[i][:temp][:maxup].to_i) print "\033[#{$rainansi}m" print "\t\t " print percentbar(week[i][:pop]) print "| image=#{icon} color=#{$textcolor} size=16 ansi=true trim=false\n" end puts "---" puts "天気概況 | color=#{$textcolor}" overviewdays.each_line do |line| puts "--#{line.strip} | color=#{$textcolor}" end puts "二週間気温予報 | color=#{$textcolor}" overviewweek.each_line do |line| puts "--#{line.strip} | color=#{$textcolor}" end puts "---" puts "天気図 | color=#{$textcolor}" puts "--#{m64}" puts "レーダー | color=#{$textcolor}" puts "--#{r64}" puts "衛星 | color=#{$textcolor}" puts "--#{s64}" typhoontarget = JSON.parse(URI.open("https://www.jma.go.jp/bosai/typhoon/data/targetTc.json", :read_timeout => 120).read) if typhoontarget.length > 0 puts "台風情報 | ansi=false color=#{$textcolor} href=https://www.jma.go.jp/bosai/map.html#elem=root&typhoon=all&contents=typhoon" puts "--#{typhoonimg}" puts typhooninfo(typhoontarget.map {|x| x['tropicalCyclone']}) end puts "---" puts "更新 | refresh=true ansi=false color=limegreen" puts "気象庁…|href=https://www.jma.go.jp/forecast/#area_type=class20s&area_code=#{$local} ansi=false color=lightslategray" ================================================ FILE: pkg/plugins/testdata/token-too-long/jma.1h.sh.output ================================================ 💧 10.7° --- 東京都渋谷区 | color=lightslategray --- 温 10.7° (13.3°) 10.1° 5時 13.9° 13時 | color=lightgray ansi=true 湿 97% (10.7°) | color=lightgray 圧 1002.2 hPa | color=lightgray 風 北 5.8 m/s (北 15.6 m/s 14時)| color=lightgray 雨 2.0 mm/h (64.5 mm/d) | color=lightgray 雲 98% (1.89→1.93 km) | color=lightgray 視 4 km | color=lightgray 時 18:50 | color=lightslategray 震 1.5 (3) 日本海中部 181 km 3/11 14:25 | color=lightgray --- 今日13日 | color=lightgray --雨後曇 | color=lightgray --北西の風  | color=lightgray --23区西部 では はじめ 北西の風 強く | color=lightgray --波 2 m 後 1 m | color=lightgray --雨 - - - 30 | color=white      ▂ | image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAJLSURBVEjH7ZU9aFNRFMf/LzZRU6QiFqSKi/UriBXERSm0iPgZBweRFj8G6dBacepSlBYhUhQ6FEE7NKhFm0kQEepiKzhYjEKKiOniYrsEQahJpKk/hzQf77285InBIvR/l3f+57x7/vfcc++VVvCfwSg28OusDiigD3qpSSO1zNo4SBzIEGceSHJyueUskqANv4SHFt6T5kjVs9Swl3PsxKgU6CdOgi1FTC1TJBmin1PUVEWMlxskyeIL99lQLvgS0GbhAuTwlu1/LWc974AJrtBCO0+AKeqcw++SwW9j29lBA9dI8pE1LhMHieAtwYeB7sJW0UqKN+yhofQ0r5gpk+Q8cNWloAfAQzwWthkYtnAXl+o/S4etpxjkJ6sdk3j4zphLQT5eAIPmFPQBmy2RXia5Qx8xYMg6zXHgsCTRQ1OebaJHkjD4yjNT/DY+O444KebZZYp/zpzjEryMAa1ZK1fa10rrHuskDahLwodPUpcGJEkBNeipZZ6M41gU7qqZhbGgTv3SGXuN0lyWSBCSCBOWCJGQ2MQnMmx0vWXjwO3KW2byzzBhJ49xQaIRn0SUqISPRokOvhF0u14eASPWJi3V1KZFpBkp5ViV/+qlN/99iK3uN4ATPC51kVqPvcnXb78FC85IoRoEibiXUkFo3dLF2E0z+4vGaUaBccfHJFta6qmXGOaP2rOCJC/X80+HGdnjJEmyF3daMUmjko4qpunqCTIWdJNb2q19WltEzypqzJX5jVop19Q561/CViHjhyQpZLJWsAJn/AZEfcfma31vgwAAAABJRU5ErkJggg== color=lightgray size=16 ansi=true trim=false 明日14日 | color=lightgray --晴時々曇 | color=lightgray --北西の風 後 北の風 | color=lightgray --23区西部 では 北の風 やや強く | color=lightgray --波 1 m 後 1.5 m | color=lightgray --雨 10 10 10 10 | color=white  10˚ 19˚ ____ | image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAITSURBVEjH7Za9a1NRGMaf06SCQ6kIZrB/gBbEWxAcAgFLB6Xg4iBiB52kRAX/AZs6W6hwB61DGytqRZwaCgpCOyihKH5s1kUo9EIzqo1g48/hJtf7lQ9NMA55znLv+77nvM95P845Ug9/BSysbnMIEipS7I7nvm5vvQHIY5OofnspI4FNvmM+khzlHIcwrRjbQDYivQzYHSHTzxQ7uPjMHPubTUiQJS2RIkeBAjlSEmmytbi1RWcfr4FVrnCCCR4B6wy2MnGMEuDgANuMNdzxE8Yj0tM8pj/GegG4+jtVjFLmJUc42JhOihKbZCSJDJtsc6COZR8PgPmI/B6wSKhZyAB3Q7IL1fRtcaluTZEDl463zFSsncEGlqOxYA8rwGzQBdPAUCTCa8wwzYdIlWJRpIglUcAJuHVY9us9zTDfKLPBx5ixQZmvHA54CKwbIbYEjLp/rZ1DxMpQRbsxoxJrXxfmh7L6qTPtpmwWWIlN2TPgZvOUBfSfWI1XVIsag2la1IsQPTK5D8yHizSuqAOb+B5tj5ryT9p+iZMR6TgPScZYh9o+oLsBnHe/fQYkNKm35hUpTeq4pHXdNiXSGtGcqahNMKgXOqY1PdU77fgUQzqrCT3XKROuvH9wdVz3ro4g7jBQs/JHKK8vuubGAksy76txu6UBc7ETlCSSGtaI9vpEW3pjnOYTe++hJvjvnrA9ePgFY17C4d7AaVUAAAAASUVORK5CYII= color=lightgray size=16 ansi=true trim=false 15日(月) | color=lightgray --晴時々曇 | color=lightgray --北の風 後 南の風 | color=lightgray --波 0.5 m | color=lightgray --雨 10 | color=white  8⃨̇ 2̣0⃛ _| image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAITSURBVEjH7Za9a1NRGMaf06SCQ6kIZrB/gBbEWxAcAgFLB6Xg4iBiB52kRAX/AZs6W6hwB61DGytqRZwaCgpCOyihKH5s1kUo9EIzqo1g48/hJtf7lQ9NMA55znLv+77nvM95P845Ug9/BSysbnMIEipS7I7nvm5vvQHIY5OofnspI4FNvmM+khzlHIcwrRjbQDYivQzYHSHTzxQ7uPjMHPubTUiQJS2RIkeBAjlSEmmytbi1RWcfr4FVrnCCCR4B6wy2MnGMEuDgANuMNdzxE8Yj0tM8pj/GegG4+jtVjFLmJUc42JhOihKbZCSJDJtsc6COZR8PgPmI/B6wSKhZyAB3Q7IL1fRtcaluTZEDl463zFSsncEGlqOxYA8rwGzQBdPAUCTCa8wwzYdIlWJRpIglUcAJuHVY9us9zTDfKLPBx5ixQZmvHA54CKwbIbYEjLp/rZ1DxMpQRbsxoxJrXxfmh7L6qTPtpmwWWIlN2TPgZvOUBfSfWI1XVIsag2la1IsQPTK5D8yHizSuqAOb+B5tj5ryT9p+iZMR6TgPScZYh9o+oLsBnHe/fQYkNKm35hUpTeq4pHXdNiXSGtGcqahNMKgXOqY1PdU77fgUQzqrCT3XKROuvH9wdVz3ro4g7jBQs/JHKK8vuubGAksy76txu6UBc7ETlCSSGtaI9vpEW3pjnOYTe++hJvjvnrA9ePgFY17C4d7AaVUAAAAASUVORK5CYII= color=lightgray size=16 ansi=true trim=false 16日(火) | color=lightgray  6̣̈ 2̤2̈ ▁| image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAITSURBVEjH7Za9a1NRGMaf06SCQ6kIZrB/gBbEWxAcAgFLB6Xg4iBiB52kRAX/AZs6W6hwB61DGytqRZwaCgpCOyihKH5s1kUo9EIzqo1g48/hJtf7lQ9NMA55znLv+77nvM95P845Ug9/BSysbnMIEipS7I7nvm5vvQHIY5OofnspI4FNvmM+khzlHIcwrRjbQDYivQzYHSHTzxQ7uPjMHPubTUiQJS2RIkeBAjlSEmmytbi1RWcfr4FVrnCCCR4B6wy2MnGMEuDgANuMNdzxE8Yj0tM8pj/GegG4+jtVjFLmJUc42JhOihKbZCSJDJtsc6COZR8PgPmI/B6wSKhZyAB3Q7IL1fRtcaluTZEDl463zFSsncEGlqOxYA8rwGzQBdPAUCTCa8wwzYdIlWJRpIglUcAJuHVY9us9zTDfKLPBx5ixQZmvHA54CKwbIbYEjLp/rZ1DxMpQRbsxoxJrXxfmh7L6qTPtpmwWWIlN2TPgZvOUBfSfWI1XVIsag2la1IsQPTK5D8yHizSuqAOb+B5tj5ryT9p+iZMR6TgPScZYh9o+oLsBnHe/fQYkNKm35hUpTeq4pHXdNiXSGtGcqahNMKgXOqY1PdU77fgUQzqrCT3XKROuvH9wdVz3ro4g7jBQs/JHKK8vuubGAksy76txu6UBc7ETlCSSGtaI9vpEW3pjnOYTe++hJvjvnrA9ePgFY17C4d7AaVUAAAAASUVORK5CYII= color=lightgray size=16 ansi=true trim=false 17日(水) | color=lightgray  7̤̈ 1͔8̇ ▁| image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAITSURBVEjH7Za9a1NRGMaf06SCQ6kIZrB/gBbEWxAcAgFLB6Xg4iBiB52kRAX/AZs6W6hwB61DGytqRZwaCgpCOyihKH5s1kUo9EIzqo1g48/hJtf7lQ9NMA55znLv+77nvM95P845Ug9/BSysbnMIEipS7I7nvm5vvQHIY5OofnspI4FNvmM+khzlHIcwrRjbQDYivQzYHSHTzxQ7uPjMHPubTUiQJS2RIkeBAjlSEmmytbi1RWcfr4FVrnCCCR4B6wy2MnGMEuDgANuMNdzxE8Yj0tM8pj/GegG4+jtVjFLmJUc42JhOihKbZCSJDJtsc6COZR8PgPmI/B6wSKhZyAB3Q7IL1fRtcaluTZEDl463zFSsncEGlqOxYA8rwGzQBdPAUCTCa8wwzYdIlWJRpIglUcAJuHVY9us9zTDfKLPBx5ixQZmvHA54CKwbIbYEjLp/rZ1DxMpQRbsxoxJrXxfmh7L6qTPtpmwWWIlN2TPgZvOUBfSfWI1XVIsag2la1IsQPTK5D8yHizSuqAOb+B5tj5ryT9p+iZMR6TgPScZYh9o+oLsBnHe/fQYkNKm35hUpTeq4pHXdNiXSGtGcqahNMKgXOqY1PdU77fgUQzqrCT3XKROuvH9wdVz3ro4g7jBQs/JHKK8vuubGAksy76txu6UBc7ETlCSSGtaI9vpEW3pjnOYTe++hJvjvnrA9ePgFY17C4d7AaVUAAAAASUVORK5CYII= color=lightgray size=16 ansi=true trim=false 18日(木) | color=lightgray  7̤̈ 1̤8⃛ ▂| image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAITSURBVEjH7Za9a1NRGMaf06SCQ6kIZrB/gBbEWxAcAgFLB6Xg4iBiB52kRAX/AZs6W6hwB61DGytqRZwaCgpCOyihKH5s1kUo9EIzqo1g48/hJtf7lQ9NMA55znLv+77nvM95P845Ug9/BSysbnMIEipS7I7nvm5vvQHIY5OofnspI4FNvmM+khzlHIcwrRjbQDYivQzYHSHTzxQ7uPjMHPubTUiQJS2RIkeBAjlSEmmytbi1RWcfr4FVrnCCCR4B6wy2MnGMEuDgANuMNdzxE8Yj0tM8pj/GegG4+jtVjFLmJUc42JhOihKbZCSJDJtsc6COZR8PgPmI/B6wSKhZyAB3Q7IL1fRtcaluTZEDl463zFSsncEGlqOxYA8rwGzQBdPAUCTCa8wwzYdIlWJRpIglUcAJuHVY9us9zTDfKLPBx5ixQZmvHA54CKwbIbYEjLp/rZ1DxMpQRbsxoxJrXxfmh7L6qTPtpmwWWIlN2TPgZvOUBfSfWI1XVIsag2la1IsQPTK5D8yHizSuqAOb+B5tj5ryT9p+iZMR6TgPScZYh9o+oLsBnHe/fQYkNKm35hUpTeq4pHXdNiXSGtGcqahNMKgXOqY1PdU77fgUQzqrCT3XKROuvH9wdVz3ro4g7jBQs/JHKK8vuubGAksy76txu6UBc7ETlCSSGtaI9vpEW3pjnOYTe++hJvjvnrA9ePgFY17C4d7AaVUAAAAASUVORK5CYII= color=lightgray size=16 ansi=true trim=false 19日(金) | color=lightgray  1͔0̇ 1̤7͐ ▃| image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAEPSURBVEjH7ZUxS0JRGIbfG16hQYwmSfoBSoLQHOjm7NCgQ1uD2E+wbHdzKRfHWtycBEEHl1CMxgZp0s3RhqCnwZDrBYfOjXtB7jN+H+flOXzn8EkhIXuG5e04EaWV1lTvFkFfRdjcsmLNB48cB6tzxBgYUCVHmSfghXiQQm3ghs3IyfPJiDNOgtG5AFqu2tXv+OZc4/Ft/l2oDiRdNZshDeq8AU2/hbosdvZsnoG8WfLB/8taX6roW0V/hcZKuEfmUFpqpoy/Qn1Jd7uaRHWqmWGyKe5vv9W7B0pmucbfk7j6OtdQHb1q5Wgkdamyeir4vkywqW1WxzYPxExTvS/XlLI6dJTmmlgL48CQkP3jByvrluiynBvAAAAAAElFTkSuQmCC color=lightgray size=16 ansi=true trim=false 20日(土) | color=lightgray  7͔̇ 1̣2͐ ▄| image=iVBORw0KGgoAAAANSUhEUgAAAEgAAAAYCAQAAADzanM7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAJdSURBVEjH7ZVLSJRRGIbf32Ymm0EMyQrtCl0YCw0iCENQIsJKqU2Eo1QQrjRauZFSN66KFhaUi6ZFoG4jCItIiRZJUyBE4GwiUjdDGNWM0cjTYnRmzj83x4ZC8P0257zf5bzn8n+/tIZVButfLoZb53VEVXqvZxq3Iv9579QyBUSZ4gcQ5vRykhxUc4H9FPzkqGWBEC24JYqo5x3znMie4uQGYWL4xH3KVrRwJW1pWDdThNiWxHiYIMwAfZzBka7QRt4CY3RQj48hYILSFQgahVRJXAJabFwVS3jD3tRCfqAzcVU0EOE1B6nIU9BOvhClycbeJYo7JdbHPiq4RpgPFJuuOmDQFn5xUf8M7bnfFLfi9pAIc8nXI/GSYJbcNuCqSfUClbYwJ+PcpJdJYCCnIDu8hvc2v1ifMbeIbwyb1BNmM4Y7GQYacgjabTOX4W0EjksSXdTE2Rq6JAmLaR4vW5BEGQvmGeFiTw4zBXmIEKREij0NXLgkBkGSOABcznllhj/ImDH3kgteW4VG5rkiEaJfwo9fop+QxFY+EmVTLG6pA7xQj3rUnkGOS9v1yqC+a07FGlIo4x6+mlPrKWe1WdJRfZZULUnq1QNJzdqic5a9kv2zN3x9abrIKX4zzS7lAdbFR910x8fH2JEuuHSxMXZSx+Eka+YRMJoqFR/wPB9BksRIokfRxEi2UCfX478OE/coSZvRanabZQmKPepyyhOPOhm2feOQV4e0IYmaUcCaVcHApAatO4xK1kk61G5VF672ygR5JIkAgcQsGY58C/4trJ+SpH5jtobVjD+GFPlZHQ0leAAAAABJRU5ErkJggg== color=lightgray size=16 ansi=true trim=false --- 天気概況 | color=lightgray --2021年03月13日 18時21分 | color=lightgray -- | color=lightgray --東京地方では、低い土地の浸水に注意してください。 | color=lightgray --東京地方、伊豆諸島では、強風や高波に注意してください。 | color=lightgray --東京都では、竜巻などの激しい突風や落雷に注意してください。 | color=lightgray -- | color=lightgray --関東地方には前線を伴った低気圧があって、北東へ進んでいます。 | color=lightgray -- | color=lightgray --東京地方は、雨で雷を伴っている所があります。 | color=lightgray -- | color=lightgray --13日は、低気圧が関東地方を通過して三陸沖へ進むため、 | color=lightgray --雨で夜は曇りとなり、 | color=lightgray --雷を伴う所がある見込みです。 | color=lightgray --伊豆諸島では、雨や雷雨となる見込みです。 | color=lightgray -- | color=lightgray --14日は、低気圧が三陸沖を北東に進み、次第に高気圧に覆われますが、 | color=lightgray --上空の気圧の谷の影響を受けるでしょう。 | color=lightgray --このため、晴れ時々曇りとなる見込みです。 | color=lightgray -- | color=lightgray --<天気変化等の留意点> | color=lightgray -- | color=lightgray --伊豆諸島南部では、13日は、曇りで雨や雷雨となる所があるでしょう。 | color=lightgray -- | color=lightgray --14日は、曇り時々晴れとなる見込みです。 | color=lightgray -- | color=lightgray --(雨の予想) | color=lightgray -- | color=lightgray --13日18時から14日18時までに予想される24時間降水量は、 | color=lightgray --多い所で、伊豆諸島南部 30ミリの見込みです。 | color=lightgray -- | color=lightgray --【関東甲信地方】 | color=lightgray -- | color=lightgray --関東甲信地方は、雨や曇りで、雷を伴って激しい雨の降っている所があります。 | color=lightgray -- | color=lightgray --13日は、低気圧が関東地方を通過して三陸沖へ進むため、 | color=lightgray --雨で、雷を伴い激しく降る所がありますが、 | color=lightgray --次第に曇りとなるでしょう。 | color=lightgray --山沿いでは雪の降る所もある見込みです。 | color=lightgray -- | color=lightgray --14日は、低気圧が三陸沖を北東に進み、次第に高気圧に覆われますが、 | color=lightgray --上空の気圧の谷の影響を受けるでしょう。 | color=lightgray --このため、晴れや曇りで、長野県や関東地方北部では、寒気の影響で雪や雨の降る所がある見込みです。 | color=lightgray -- | color=lightgray --関東地方と伊豆諸島の海上では、13日から14日にかけて、うねりを伴い、しける見込みです。 | color=lightgray --船舶は、高波に注意してください。 | color=lightgray 二週間気温予報 | color=lightgray --関東甲信地方週間天気予報 2021年03月13日 16時35分 | color=lightgray -- | color=lightgray --予報期間 3月14日から3月20日まで  | color=lightgray -- | color=lightgray --向こう一週間は、高気圧に覆われて晴れる日が多いですが、 | color=lightgray --期間の終わりは低気圧や前線の影響で雨の降る日があるでしょう。 | color=lightgray -- | color=lightgray --最高気温と最低気温はともに、平年より高い日が多く、 | color=lightgray --平年よりかなり高い日もある見込みです。 | color=lightgray -- | color=lightgray --降水量は、平年並か平年より多いでしょう。 | color=lightgray --- 天気図 | color=lightgray --| refresh=true image=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 レーダー | color=lightgray --| refresh=true image=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 衛星 | color=lightgray -- | refresh=true image=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 ================================================ FILE: pkg/plugins/testdata/vars-test/plugin.sh ================================================ #!/bin/bash # string(XBAR_TEST_EXPLICIT_VAR): An explicit variable # string(XBAR_TEST_DEFAULT_VAR="default-value"): A default variable (from metadata) # string(XBAR_TEST_SET_IN_VARS_JSON): A variable set in the JSON echo "XBAR_TEST_EXPLICIT_VAR=${XBAR_TEST_EXPLICIT_VAR}" echo "XBAR_TEST_SET_IN_VARS_JSON=${XBAR_TEST_SET_IN_VARS_JSON}" echo "XBAR_TEST_DEFAULT_VAR=${XBAR_TEST_DEFAULT_VAR}" ================================================ FILE: pkg/plugins/testdata/vars-test/plugin.sh.vars.json ================================================ { "XBAR_TEST_SET_IN_VARS_JSON": "json" } ================================================ FILE: pkg/plugins/variables.go ================================================ package plugins import ( "encoding/json" "fmt" "io" "io/ioutil" "os" "path/filepath" "sync" "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" ) // variableJSONFileExt is the extension for the variable JSON payload. const variableJSONFileExt = ".vars.json" // SaveVariableValues saves the values for a plugin. func SaveVariableValues(pluginDir, installedPluginPath string, values map[string]interface{}) error { b, err := json.MarshalIndent(values, "", "\t") if err != nil { return errors.Wrap(err, "json.MarshalIndent") } filename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt) err = ioutil.WriteFile(filename, b, 0666) if err != nil { return errors.Wrap(err, "WriteFile") } return nil } // LoadVariableValues loads the variables for a plugin. func LoadVariableValues(pluginDir, installedPluginPath string) (map[string]interface{}, error) { filename := filepath.Join(pluginDir, installedPluginPath+variableJSONFileExt) f, err := os.Open(filename) if err != nil { if os.IsNotExist(err) { // no file - but not an error, just empty map return map[string]interface{}{}, nil } return nil, errors.Wrap(err, "Open") } defer f.Close() b, err := io.ReadAll(io.LimitReader(f, 1_000_000 /* ~1MB */)) if err != nil { return nil, errors.Wrap(err, "ReadAll") } var values map[string]interface{} err = json.Unmarshal(b, &values) if err != nil { return nil, errors.Wrap(err, "json.Unmarshal") } return values, nil } func (p *Plugin) loadVariablesAsEnvVars() ([]string, error) { vars, err := p.loadVariables() if err != nil { return nil, errors.Wrap(err, "loadVariables") } envvars := make([]string, 0, len(vars)) for k, v := range vars { envvars = append(envvars, fmt.Sprintf("%s=%v", k, v)) } return envvars, nil } func (p *Plugin) loadVariables() (map[string]interface{}, error) { var wg sync.WaitGroup var defaultVars, jsonFileVars map[string]interface{} var defaultVarsErr, jsonFileVarsErr error wg.Add(1) go func() { defaultVars, defaultVarsErr = p.loadVariablesFromPluginMetadata() wg.Done() }() wg.Add(1) go func() { jsonFileVars, jsonFileVarsErr = p.loadVariablesFromJSONFile() wg.Done() }() wg.Wait() if defaultVarsErr != nil { return nil, errors.Wrap(defaultVarsErr, "load default vars") } if jsonFileVarsErr != nil { return nil, errors.Wrap(jsonFileVarsErr, "load json file vars") } // add the json file vars to the defaults, // and return them. for k, v := range jsonFileVars { defaultVars[k] = v } return defaultVars, nil } // loadVariablesFromJSONFile gets a list of environment variable friendly // key=value pairs. func (p *Plugin) loadVariablesFromJSONFile() (map[string]interface{}, error) { variablesJSONFilename := p.Command + variableJSONFileExt f, err := os.Open(variablesJSONFilename) if err != nil && os.IsNotExist(err) { // no .vars.json file - no probs return nil, nil } else if err != nil { return nil, errors.Wrap(err, "open vars json file") } defer f.Close() b, err := io.ReadAll(io.LimitReader(f, 1_000_000)) if err != nil { return nil, err } var vars map[string]interface{} if err := json.Unmarshal(b, &vars); err != nil { return nil, errors.Wrap(err, "json.Unmarshal") } return vars, nil } func (p *Plugin) loadVariablesFromPluginMetadata() (map[string]interface{}, error) { // read the plugin metadata for default values pluginFile, err := os.Open(p.Command) if err != nil { return nil, errors.Wrap(err, "open plugin source") } defer pluginFile.Close() pluginFileB, err := io.ReadAll(io.LimitReader(pluginFile, 1_000_000)) if err != nil { return nil, errors.Wrap(err, "read plugin source") } pluginMetadata, err := metadata.Parse(metadata.DebugFunc(p.Debugf), p.CleanFilename(), string(pluginFileB)) if err != nil { return nil, errors.Wrap(err, "metadata.Parse") } vars := make(map[string]interface{}) for _, pluginVar := range pluginMetadata.Vars { if pluginVar.Default == "" { // skip values with no default continue } vars[pluginVar.Name] = pluginVar.DefaultValue() } return vars, nil } ================================================ FILE: pkg/plugins/variables_test.go ================================================ package plugins import ( "context" "io/ioutil" "os" "path/filepath" "testing" "time" "github.com/matryer/is" ) func TestEnvironmentVariables(t *testing.T) { is := is.New(t) err := os.Setenv("XBAR_TEST_EXPLICIT_VAR", "explicit") is.NoErr(err) t.Cleanup(func() { err := os.Unsetenv("XBAR_TEST_EXPLICIT_VAR") is.NoErr(err) }) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() p := &Plugin{ Command: filepath.Join("testdata", "vars-test", "plugin.sh"), Debugf: DebugfNoop, Timeout: 1 * time.Second, RefreshInterval: RefreshInterval{N: 250, Unit: "milliseconds"}, CycleInterval: 500 * time.Millisecond, } p.Run(ctx) is.Equal(len(p.Items.CycleItems), 3) is.Equal(p.Items.CycleItems[0].Text, `XBAR_TEST_EXPLICIT_VAR=explicit`) // inherited is.Equal(p.Items.CycleItems[1].Text, `XBAR_TEST_SET_IN_VARS_JSON=json`) // in vars.json file is.Equal(p.Items.CycleItems[2].Text, `XBAR_TEST_DEFAULT_VAR=default-value`) // from plugin metadata } func TestVariablesPersistence(t *testing.T) { is := is.New(t) installedPluginPath := "test-plugin.sh" pluginDir, err := ioutil.TempDir("", "xbar-variables-test") is.NoErr(err) t.Cleanup(func() { os.RemoveAll(pluginDir) }) loadedValues, err := LoadVariableValues(pluginDir, installedPluginPath) is.NoErr(err) is.True(loadedValues != nil) is.Equal(len(loadedValues), 0) // should be empty values := map[string]interface{}{ "VAR_NAME": "Mat", "VAR_CITY": "London", "VAR_COUNTRY": "UK", } err = SaveVariableValues(pluginDir, installedPluginPath, values) is.NoErr(err) loadedValues, err = LoadVariableValues(pluginDir, installedPluginPath) is.NoErr(err) is.Equal(loadedValues["VAR_NAME"], "Mat") is.Equal(loadedValues["VAR_CITY"], "London") is.Equal(loadedValues["VAR_COUNTRY"], "UK") } ================================================ FILE: pkg/update/README.md ================================================ # Update The update package provide auto-updates. It was designed to work with goreleaser, but is highly configurable. ================================================ FILE: pkg/update/go.mod ================================================ module github.com/matryer/xbar/pkg/update go 1.16 require ( github.com/Masterminds/semver/v3 v3.1.1 github.com/dsnet/compress v0.0.1 // indirect github.com/frankban/quicktest v1.13.0 // indirect github.com/golang/snappy v0.0.3 // indirect github.com/matryer/is v1.4.0 github.com/mholt/archiver v3.1.1+incompatible github.com/nwaples/rardecode v1.1.0 // indirect github.com/pierrec/lz4 v2.6.0+incompatible // indirect github.com/pkg/errors v0.9.1 github.com/ulikunitz/xz v0.5.10 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect ) ================================================ FILE: pkg/update/go.sum ================================================ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A= github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= ================================================ FILE: pkg/update/update.go ================================================ package update import ( "encoding/json" "io" "log" "net/http" "os" "os/exec" "path" "path/filepath" "strings" "syscall" "time" semver "github.com/Masterminds/semver/v3" "github.com/mholt/archiver" "github.com/pkg/errors" ) // Updater updates an app. type Updater struct { // CurrentVersion is the current install version. CurrentVersion string // LatestReleaseGitHubEndpoint is the URL of the API to get latest release data. // For example, https://api.github.com/repos/matryer/xbar/releases/latest. LatestReleaseGitHubEndpoint string // Client is the HTTP client to use to access the // API and download the assets. Client *http.Client // SelectAsset selects the Asset to install. SelectAsset SelectAssetFunc // DownloadBytesLimit is the maximum number of bytes to download. DownloadBytesLimit int64 // GetExecutable is the function that gets the current // executable. If nil, os.Executable will be used. GetExecutable func() (string, error) } // SelectAssetFunc selects the Asset to install. type SelectAssetFunc func(release Release, asset Asset) bool // Update checks and installs the update. // Returns nil, nil if no update is required. func (u *Updater) Update() (*Release, error) { if u.DownloadBytesLimit == 0 { return nil, errors.New("must set DownloadBytesLimit") } if u.SelectAsset == nil { return nil, errors.New("missing SelectAsset func") } if u.GetExecutable == nil { u.GetExecutable = os.Executable } latest, err := u.getLatestRelease() if err != nil { return nil, err } hasUpdate := hasUpdate(u.CurrentVersion, latest.TagName) if !hasUpdate { return nil, nil } var selectedAsset *Asset for _, asset := range latest.Assets { if u.SelectAsset(*latest, asset) { selectedAsset = &asset break } } if selectedAsset == nil { return nil, errors.New("no asset selected, use SelectAssetFunc to select an asset") } err = u.downloadAndReplaceApp(*selectedAsset) if err != nil { return nil, errors.Wrap(err, "download update") } return latest, nil } // Restart spawns the current executable again, and terminates // the running one. func (u *Updater) Restart() error { time.Sleep(1 * time.Second) thisExecuable, err := os.Executable() if err != nil { return errors.Wrap(err, "get executable") } log.Println("restarting", thisExecuable) cmd := exec.Command(thisExecuable) cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: false, } cmd.Dir = filepath.Dir(thisExecuable) cmd.Env = os.Environ() cmd.Env = append(cmd.Env, "XBAR_UPDATE_RESTART_COUNTER=1") cmd.Args = os.Args cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Start() if err != nil { if exitErr, ok := err.(*exec.ExitError); ok { return errors.Wrapf(err, "starting new app failed: exit code %d", exitErr.ExitCode()) } return errors.Wrap(err, "starting new app failed") } log.Println("waiting before terminating after update...") time.Sleep(1 * time.Second) log.Println("terminating after update.") os.Exit(0) return nil } // getLatestRelease gets the latest release. func (u *Updater) getLatestRelease() (*Release, error) { resp, err := u.Client.Get(u.LatestReleaseGitHubEndpoint) if err != nil { return nil, errors.Wrap(err, "get latest release") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, errors.Errorf("failed to check for updates: got %s", resp.Status) } b, err := io.ReadAll(io.LimitReader(resp.Body, u.DownloadBytesLimit)) if err != nil { return nil, errors.Wrap(err, "read body") } var latestRelease Release err = json.Unmarshal(b, &latestRelease) if err != nil { return nil, errors.Wrap(err, "marshal") } latestRelease.CreatedAt, err = time.Parse(time.RFC3339Nano, latestRelease.CreatedAtString) if err != nil { return nil, errors.Wrap(err, "time.Parse: created_at") } return &latestRelease, nil } // HasUpdate checks whether there's an update or not. func (u *Updater) HasUpdate() (*Release, bool, error) { latest, err := u.getLatestRelease() if err != nil { return nil, false, err } hasUpdate := hasUpdate(u.CurrentVersion, latest.TagName) return latest, hasUpdate, nil } // hasUpdate compares the current and latest version strings to // see if there is an update. // Returns false if the versions match. // Returns false is current is in front of latest. // If semver checking fails, direct string comparison is used. func hasUpdate(current, latest string) bool { semverValid := true currentV, err := semver.NewVersion(current) if err != nil { semverValid = false } latestV, err := semver.NewVersion(latest) if err != nil { semverValid = false } if semverValid { if currentV.Equal(latestV) { return false // up-to-date } if currentV.GreaterThan(latestV) { return false // local version is higher } } else { // semver failed - just check tags if latest == current { return false } } return true } func (u *Updater) downloadAndReplaceApp(asset Asset) error { filename := path.Base(asset.BrowserDownloadURL) switch { case strings.HasSuffix(filename, ".zip"): // fine default: return errors.Errorf("file not supported: %s", filename) } resp, err := u.Client.Get(asset.BrowserDownloadURL) if err != nil { return errors.Wrap(err, "download asset") } defer resp.Body.Close() const defaultTempDir = "" f, err := os.CreateTemp(defaultTempDir, "*-"+filename) if err != nil { return errors.Wrap(err, "create temp file") } _, err = io.Copy(f, io.LimitReader(resp.Body, u.DownloadBytesLimit)) if err != nil { f.Close() return errors.Wrap(err, "download asset") } f.Close() executable, err := u.GetExecutable() if err != nil { return errors.Wrap(err, "get executable") } appPath, err := appPathFromExecutable(executable) if err != nil { return errors.Wrap(err, "find app path") } appPathDir := filepath.Dir(appPath) appPreviousPath := appPath + ".previous" err = os.Rename(appPath, appPreviousPath) if err != nil { _, statErr := os.Stat(appPath) // not exist is ok, just ignore it if !os.IsNotExist(statErr) { return errors.Wrap(err, "rename existing app") } } err = archiver.Unarchive(f.Name(), appPathDir) if err != nil { return errors.Wrap(err, "unarchive") } err = os.RemoveAll(appPreviousPath) if err != nil { return errors.Wrap(err, "remove previous") } return nil } // Release is a GitHub release. type Release struct { TagName string `json:"tag_name"` Assets []Asset `json:"assets"` Body string `json:"body"` CreatedAtString string `json:"created_at"` CreatedAt time.Time `json:"created_at_time"` } // Asset is a file within a Release on GitHub. type Asset struct { Name string `json:"name"` BrowserDownloadURL string `json:"browser_download_url"` } // appPathFromExecutable gets the .app path from the currently // running executable. func appPathFromExecutable(p string) (string, error) { if !strings.HasSuffix(p, "/Contents/MacOS/xbar") { return "", errors.New("executable not where it should be") } return strings.TrimSuffix(p, "/Contents/MacOS/xbar"), nil } ================================================ FILE: pkg/update/update_test.go ================================================ package update import ( "archive/zip" "encoding/json" "net/http" "net/http/httptest" "os" "path" "path/filepath" "testing" "time" "github.com/matryer/is" ) func TestForReals(t *testing.T) { is := is.New(t) t.Cleanup(func() { err := os.RemoveAll(filepath.Join("testreal-testarea")) is.NoErr(err) }) u := &Updater{ CurrentVersion: "v0.0.1", LatestReleaseGitHubEndpoint: "https://api.github.com/repos/matryer/updatetest/releases/latest", Client: &http.Client{Timeout: 10 * time.Minute}, SelectAsset: func(release Release, asset Asset) bool { // look for the zip file return filepath.Ext(asset.Name) == ".zip" //return asset.Name == "xbar."+release.TagName+".zip" }, DownloadBytesLimit: 10_741_824, // 10MB GetExecutable: func() (string, error) { return "./testreal-testarea/xbar.app/Contents/MacOS/xbar", nil }, } _, hasUpdate, err := u.HasUpdate() is.NoErr(err) is.Equal(hasUpdate, true) _, err = u.Update() is.NoErr(err) } func TestUpdate(t *testing.T) { is := is.New(t) t.Cleanup(func() { err := os.RemoveAll(filepath.Join("testupdate-testarea")) is.NoErr(err) }) downloadServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { zipWriter := zip.NewWriter(w) defer func() { err := zipWriter.Close() is.NoErr(err) }() f, err := zipWriter.Create("xbar.version.zip") is.NoErr(err) n, err := f.Write([]byte("12345678")) // sample data is.NoErr(err) // tarWriter.Write is.Equal(n, 8) // should write eight bytes })) t.Cleanup(func() { downloadServer.Close() }) apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { version := "v2.0.0" response := Release{ CreatedAtString: time.Now().Format(time.RFC3339Nano), TagName: version, Assets: []Asset{ { Name: "xbar-" + version + ".zip", BrowserDownloadURL: downloadServer.URL + "/xbar-" + version + ".zip", }, }, } b, err := json.Marshal(response) is.NoErr(err) // marshal _, err = w.Write(b) is.NoErr(err) // write })) t.Cleanup(func() { apiServer.Close() }) u := Updater{ DownloadBytesLimit: 1_000_000, LatestReleaseGitHubEndpoint: apiServer.URL, CurrentVersion: "v1.9.0", Client: &http.Client{Timeout: 1 * time.Minute}, SelectAsset: func(release Release, asset Asset) bool { return path.Base(asset.BrowserDownloadURL) == "xbar-"+release.TagName+".zip" }, GetExecutable: func() (string, error) { return "./testupdate-testarea/xbar.app/Contents/MacOS/xbar", nil }, } release, err := u.Update() is.NoErr(err) is.Equal(release.TagName, "v2.0.0") } func TestAppPathFromExecutable(t *testing.T) { is := is.New(t) appPath, err := appPathFromExecutable("/path/to/xbar.app/Contents/MacOS/xbar") is.NoErr(err) is.Equal(appPath, "/path/to/xbar.app") } func TestMatchingVersions(t *testing.T) { is := is.New(t) t.Cleanup(func() { err := os.RemoveAll(filepath.Join("testreal-testarea")) is.NoErr(err) }) apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { version := "v2.1.0" response := Release{ CreatedAtString: time.Now().Format(time.RFC3339Nano), TagName: version, Assets: []Asset{ { Name: "xbar-" + version + ".zip", BrowserDownloadURL: "/xbar-" + version + ".zip", }, }, } b, err := json.Marshal(response) is.NoErr(err) // marshal _, err = w.Write(b) is.NoErr(err) // write })) t.Cleanup(func() { apiServer.Close() }) u := &Updater{ CurrentVersion: "v2.1.0", LatestReleaseGitHubEndpoint: apiServer.URL, Client: &http.Client{Timeout: 10 * time.Minute}, SelectAsset: func(release Release, asset Asset) bool { return asset.Name == "xbar."+release.TagName+".zip" }, DownloadBytesLimit: 10_741_824, // 10MB GetExecutable: func() (string, error) { return "./testreal-testarea/xbar.app/Contents/MacOS/xbar", nil }, } _, hasUpdate, err := u.HasUpdate() is.NoErr(err) is.Equal(hasUpdate, false) _, err = u.Update() is.NoErr(err) } func TestLocalVersionIsHigher(t *testing.T) { is := is.New(t) t.Cleanup(func() { err := os.RemoveAll(filepath.Join("testreal-testarea")) is.NoErr(err) }) apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { version := "v2.1.0" response := Release{ CreatedAtString: time.Now().Format(time.RFC3339Nano), TagName: version, Assets: []Asset{ { Name: "xbar-" + version + ".zip", BrowserDownloadURL: "/xbar-" + version + ".zip", }, }, } b, err := json.Marshal(response) is.NoErr(err) // marshal _, err = w.Write(b) is.NoErr(err) // write })) t.Cleanup(func() { apiServer.Close() }) u := &Updater{ CurrentVersion: "v2.1.1", LatestReleaseGitHubEndpoint: apiServer.URL, Client: &http.Client{Timeout: 10 * time.Minute}, SelectAsset: func(release Release, asset Asset) bool { return asset.Name == "xbar."+release.TagName+".zip" }, DownloadBytesLimit: 10_741_824, // 10MB GetExecutable: func() (string, error) { return "./testreal-testarea/xbar.app/Contents/MacOS/xbar", nil }, } _, hasUpdate, err := u.HasUpdate() is.NoErr(err) is.Equal(hasUpdate, false) _, err = u.Update() is.NoErr(err) } ================================================ FILE: pkg/update/updatetest/main.go ================================================ package main import ( "errors" "fmt" "log" "os" "github.com/matryer/xbar/pkg/update" ) func main() { log.Println("Running...") if err := run(); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } func run() error { if os.Getenv("XBAR_UPDATE_RESTART_COUNTER") != "" { return errors.New("skipping, already restarted") } u := update.Updater{} u.Restart() return nil } ================================================ FILE: talk-overview.md ================================================ # xbar - technical deep dive - Mat Ryer @matryer ``` _ | | __ _| |__ __ _ _ __ \ \/ / '_ \ / _` | '__| > <| |_) | (_| | | /_/\_\_.__/ \__,_|_| ``` 1. Who am I and what is xbar? - @matryer: open source, author, @GoTimeFM podcast - xbar demo 2. Project structure - simple - monorepo contains everything: app front-end, app back-end, tools, website 3. Wails - xbar uses Wails by Lea Anthony - Wails is currently getting a v2 rewrite (which xbar uses) - build and package multi-platform desktop apps for Go https://wails.app - delivers a WebView, handles os calls, client and "server" RPC 4. Front-end - Svelte https://svelte.dev/ - components contain markup, script, and style - does its work at compile time - very fast - Tailwind CSS - low-level CSS framework - fine tuned control - dark mode ;) 5. The Go Back-end - main.go just kicks off Wails app - app.Start callback - clearCache - function abstractions - *wails.Runtime object - Parsing plugin output - Great candidate for TDD - Processing strings - Fuzzing? - Turning the output into Wails menus - The Plugins service - Incoming URLs 6. xbarapp.com - static site, generated by a custom tool - metadata extraction from GitHub repo - static API ================================================ FILE: tools/sitegen/README.md ================================================ # xbar sitegen This tool generates the xbar website by mixing the plugin data from the github.com/matryer/xbar-plugins repo with the templates. ## Before you run this You should probably go to `../xbarapp.com` and update the CSS: ```bash npm run build ``` * See the README in that folder for more information ## To run ```bash go build -o sitegen && XBAR_GITHUB_ACCESS_TOKEN=xxx ./sitegen -small && cd ../../xbarapp.com && npm run build ``` * Remove `-small` flag to process all plugins * GitHub may rate limit if you use this tool too much ================================================ FILE: tools/sitegen/docs.go ================================================ package main import ( "bufio" "bytes" "context" _ "embed" "encoding/json" "fmt" "html/template" "io" "io/fs" "log" "math/rand" "os" "path/filepath" "sort" "strings" "time" "github.com/gomarkdown/markdown" "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" ) var ( sourceArticlesFolder = filepath.Join("../", "../", "xbarapp.com", "articles") destFolder = filepath.Join("../", "../", "xbarapp.com", "public", "docs") templatesFolder = filepath.Join("../", "../", "xbarapp.com", "templates") // categoriesJSON is the categories.json file that is generated. // If it's not there, this tool will fail. So run sitegen first. categoriesJSON = filepath.Join("../", "../", "xbarapp.com", "public", "docs", "plugins", "categories.json") ) func generateDocs(ctx context.Context) ([]Article, error) { rand.Seed(time.Now().Unix()) g, err := newDocsGenerator() if err != nil { return nil, errors.Wrap(err, "newDocsGenerator") } docs := make(map[string]string) err = filepath.Walk(sourceArticlesFolder, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil // ignore directories } if strings.HasPrefix(info.Name(), ".") { return nil // skip dotfiles } rel, err := filepath.Rel(sourceArticlesFolder, path) if err != nil { return err } dest := filepath.Join(destFolder, rel) ext := filepath.Ext(path) if ext == ".md" { docs[path] = rel return nil // don't copy the file } _, err = copyFile(dest, path) if err != nil { return err } return nil }) if err != nil { return nil, err } for path, rel := range docs { filename := filepath.Base(path) filename = strings.ToLower(filename[:len(filename)-2] + "html") dest := filepath.Join(destFolder, filepath.Dir(rel), filename) destFilename := filepath.Join(filepath.Dir(rel), filename) err := g.parseArticleSource(ctx, destFilename, dest, path) if err != nil { log.Printf("%s: %s", path, err) } } // sort articles by time sort.Slice(g.articles, func(i, j int) bool { return g.articles[i].PublishTime.Before(g.articles[j].PublishTime) }) err = g.generateArticlePages() if err != nil { return nil, errors.Wrap(err, "generateArticlePages") } err = g.generateArticlesIndexPage() if err != nil { return nil, errors.Wrap(err, "generateArticlesIndexPage") } return g.articles, nil } type Article struct { Path string DestFilepath string Title string Desc string ImageURL string PublishTime time.Time PublishTimeStr string HTML template.HTML } type docsGenerator struct { articleTemplate *template.Template articlesIndexTemplate *template.Template categories map[string]metadata.Category articles []Article } func newDocsGenerator() (*docsGenerator, error) { articleTemplate, err := template.ParseFiles( filepath.Join(templatesFolder, "_layout.html"), filepath.Join(templatesFolder, "article.html"), ) if err != nil { return nil, err } articlesIndexTemplate, err := template.ParseFiles( filepath.Join(templatesFolder, "_layout.html"), filepath.Join(templatesFolder, "articles-index.html"), ) if err != nil { return nil, err } // load the categories b, err := os.ReadFile(categoriesJSON) if err != nil { return nil, errors.Wrap(err, "read categories.json") } var payload struct { Categories []metadata.Category } err = json.Unmarshal(b, &payload) if err != nil { return nil, errors.Wrap(err, "json marshal") } categoriesMap := make(map[string]metadata.Category) for _, category := range payload.Categories { categoriesMap[category.Path] = category } g := &docsGenerator{ articleTemplate: articleTemplate, articlesIndexTemplate: articlesIndexTemplate, categories: categoriesMap, } return g, nil } func (g *docsGenerator) parseArticleSource(_ context.Context, path, dest, src string) error { fmt.Printf("parsing: %s\n", path) pathSegs := strings.Split(path, string(filepath.Separator)) yearStr := pathSegs[0] monthStr := pathSegs[1] dayStr := pathSegs[1] publishTime, err := time.Parse("02/01/2006", fmt.Sprintf("%s/%s/%s", dayStr, monthStr, yearStr)) if err != nil { return errors.Wrap(err, "parse time from path") } publishTimeStr := publishTime.Format("January 2006") b, err := os.ReadFile(src) if err != nil { return err } firstLine := string(bytes.Split(b, []byte("\n"))[0]) // find the first image var imagePath string s := bufio.NewScanner(bytes.NewReader(b)) for s.Scan() { line := strings.TrimSpace(s.Text()) if strings.HasPrefix(line, "![") { imagePath = strings.Split(line, "](")[1] imagePath = strings.TrimSuffix(imagePath, ")") imagePath = filepath.Join(filepath.Dir(path), imagePath) imagePath = "https://xbarapp.com/docs/" + imagePath break } } html := markdown.ToHTML(b, nil, nil) err = os.MkdirAll(filepath.Dir(dest), 0777) if err != nil { return err } title := filepath.Base(src) title = title[:len(title)-len(filepath.Ext(title))] title = strings.ReplaceAll(title, "-", " ") a := Article{ Path: path, DestFilepath: dest, PublishTime: publishTime, PublishTimeStr: publishTimeStr, Title: title, Desc: firstLine, ImageURL: imagePath, HTML: template.HTML(html), } g.articles = append(g.articles, a) return nil } func (g *docsGenerator) generateArticlePages() error { for _, article := range g.articles { fmt.Printf("creating: %s\n", article.DestFilepath) f, err := os.Create(article.DestFilepath) if err != nil { return errors.Wrap(err, "create dest") } defer f.Close() pagedata := struct { Version string LastUpdatedFormatted string CurrentCategoryPath string Categories map[string]metadata.Category AllArticles []Article RandomArticles []Article Article Article }{ Version: version, LastUpdatedFormatted: time.Now().Format(time.RFC822), Categories: g.categories, AllArticles: g.articles, RandomArticles: g.randomArticles(article.Path, 5), Article: article, } err = g.articleTemplate.ExecuteTemplate(f, "_main", pagedata) if err != nil { return errors.Wrap(err, "render") } } return nil } func (g *docsGenerator) generateArticlesIndexPage() error { f, err := os.Create(filepath.Join(destFolder, "index.html")) if err != nil { return errors.Wrap(err, "create index.html") } defer f.Close() pagedata := struct { Version string LastUpdatedFormatted string CurrentCategoryPath string Categories map[string]metadata.Category AllArticles []Article }{ Version: version, LastUpdatedFormatted: time.Now().Format(time.RFC822), Categories: g.categories, AllArticles: g.articles, } err = g.articlesIndexTemplate.ExecuteTemplate(f, "_main", pagedata) if err != nil { return errors.Wrap(err, "render") } return nil } // randomArticles gets a selection of random articles. // excluding is the path of an article to exclude, empty string // will include them all. func (g *docsGenerator) randomArticles(excluding string, n int) []Article { skips := make(map[string]bool) if n > len(g.articles) { // not enough articles, we'll just return fewer. n = len(g.articles) } if excluding != "" { skips[excluding] = true if n == len(g.articles) { // expect one less n-- } } selectedArticles := make([]Article, 0, n) for len(selectedArticles) < n { randomArticle := g.articles[rand.Intn(len(g.articles))] if _, shouldSkip := skips[randomArticle.Path]; shouldSkip { continue } selectedArticles = append(selectedArticles, randomArticle) } return selectedArticles } // copyFile copies a file. // from https://opensource.com/article/18/6/copying-files-go func copyFile(dst, src string) (int64, error) { sourceFileStat, err := os.Stat(src) if err != nil { return 0, err } if !sourceFileStat.Mode().IsRegular() { return 0, fmt.Errorf("%s is not a regular file", src) } if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { return 0, err } source, err := os.Open(src) if err != nil { return 0, err } defer source.Close() destination, err := os.Create(dst) if err != nil { return 0, err } defer destination.Close() nBytes, err := io.Copy(destination, source) return nBytes, err } ================================================ FILE: tools/sitegen/go.mod ================================================ module github.com/matryer/xbar/tools/sitegen go 1.22 replace github.com/matryer/xbar/pkg/metadata => ../../pkg/metadata require ( github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8 github.com/google/go-github v17.0.0+incompatible github.com/matryer/is v1.4.0 github.com/matryer/xbar/pkg/metadata v0.0.0-00010101000000-000000000000 github.com/pkg/errors v0.9.1 github.com/snabb/sitemap v1.0.0 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 ) require ( github.com/golang/protobuf v1.4.2 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/leaanthony/go-ansi-parser v1.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/snabb/diagio v1.0.0 // indirect golang.org/x/net v0.17.0 // indirect google.golang.org/appengine v1.4.0 // indirect google.golang.org/protobuf v1.23.0 // indirect ) ================================================ FILE: tools/sitegen/go.sum ================================================ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8 h1:nWU6p08f1VgIalT6iZyqXi4o5cZsz4X6qa87nusfcsc= github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/leaanthony/go-ansi-parser v1.2.0 h1:CcBhxqkxPATj7Lgdp9EgPUGv2o3FGkg3A5eN7ermsbM= github.com/leaanthony/go-ansi-parser v1.2.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/snabb/diagio v1.0.0 h1:kovhQ1rDXoEbmpf/T5N2sUp2iOdxEg+TcqzbYVHV2V0= github.com/snabb/diagio v1.0.0/go.mod h1:ZyGaWFhfBVqstGUw6laYetzeTwZ2xxVPqTALx1QQa1w= github.com/snabb/sitemap v1.0.0 h1:7vJeNPAaaj7fQSRS3WYuJHzUjdnhLdSLLpvVtnhbzC0= github.com/snabb/sitemap v1.0.0/go.mod h1:Id8uz1+WYdiNmSjEi4BIvL5UwNPYLsTHzRbjmDwNDzA= golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= ================================================ FILE: tools/sitegen/images.go ================================================ package main import ( "fmt" "io" "log" "net/http" "os" "path" "path/filepath" "sync" "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" ) type imageDownloader struct { client *http.Client outputDir string } func (d *imageDownloader) DownloadImages(plugins []metadata.Plugin) { // ten work slots sem := make(chan struct{}, 20) var wg sync.WaitGroup for i := range plugins { wg.Add(1) go func(plugin *metadata.Plugin) { defer wg.Done() // wait for a slot sem <- struct{}{} defer func() { // free up a slot <-sem }() if err := d.downloadImage(plugin); err != nil { log.Println("ERR:", errors.Wrap(err, "DownloadImages")) } }(&plugins[i]) } wg.Wait() } func (d *imageDownloader) downloadImage(plugin *metadata.Plugin) error { ext := path.Ext(plugin.ImageURL) imagePath := plugin.Path + ext _, err := os.Stat(imagePath) if nil == err { // the image is already there, no work to do return nil } if plugin.ImageURL == "" { plugin.ProcessingNotes = append(plugin.ProcessingNotes, "missing image URL") return nil } resp, err := d.client.Get(plugin.ImageURL) if err != nil { plugin.ProcessingNotes = append(plugin.ProcessingNotes, fmt.Sprintf("unable to access image: %s", err)) return nil } defer resp.Body.Close() if resp.StatusCode < 200 && resp.StatusCode >= 400 { plugin.ProcessingNotes = append(plugin.ProcessingNotes, fmt.Sprintf("unable to access image: got %d HTTP status code", resp.StatusCode)) return nil } fullImagePath := filepath.Join(d.outputDir, "plugins", imagePath) if err := os.MkdirAll(filepath.Dir(fullImagePath), 0777); err != nil { return err } f, err := os.Create(fullImagePath) if err != nil { return errors.Wrap(err, "create") } defer f.Close() const mb = 1 << 20 // megabyte if resp.ContentLength > 5*mb { plugin.ProcessingNotes = append(plugin.ProcessingNotes, "image too big, should be less than 5MB") return nil } _, err = io.Copy(f, io.LimitReader(resp.Body, 5*mb)) if err != nil { return errors.Wrap(err, "copy") } plugin.ImageURL = "https://xbarapp.com/docs/plugins/" + imagePath fmt.Print("🌆") return nil } ================================================ FILE: tools/sitegen/main.go ================================================ package main import ( "archive/zip" "context" _ "embed" "encoding/json" "flag" "fmt" "html/template" "io" "log" "net/http" "os" "path/filepath" "sort" "strings" "sync" "time" "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" "github.com/snabb/sitemap" ) //go:embed .version var version string func main() { if err := run(context.Background(), os.Args); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } func run(ctx context.Context, args []string) error { fmt.Println("xbarapp.com site generator", version) flags := flag.NewFlagSet(args[0], flag.ContinueOnError) var ( out = flags.String("out", "../../xbarapp.com/public/docs", "output folder") small = flags.Bool("small", false, "run only a small sample (default is to process all)") skipdata = flags.Bool("skipdata", false, "skip the data - just render the index template") shouldPrintErrs = flags.Bool("errs", false, "print out error details") nodocs = flags.Bool("nodocs", false, "skip docs generation") verbose = flags.Bool("verbose", false, "verbose output") ) if err := flags.Parse(args[1:]); err != nil { return err } if err := os.RemoveAll(*out); err != nil { return err } g, err := newGenerator(*out) if err != nil { return err } var categoriesLock sync.Mutex // protects categories categories := make(map[string]metadata.Category) var plugins []metadata.Plugin pluginsByPath := make(map[string][]metadata.Plugin) var allPlugins []metadata.Plugin moonCycleIndex := 0 eachPlugin := EachFunc(func(plugin metadata.Plugin) { if *verbose { fmt.Println(plugin.CategoryPath) } categoriesLock.Lock() plugins = append(plugins, plugin) metadata.CategoryEnsurePath(categories, nil, plugin.PathSegments) allPlugins = append(allPlugins, plugin) categoriesLock.Unlock() moonCycleIndex, err = moonCycle.Print(os.Stdout, moonCycleIndex) if err != nil { log.Println(err) } }) reader := &RepoReader{ RepoOwner: "matryer", RepoName: "xbar-plugins", EachPluginFn: eachPlugin, GitHubAccessToken: os.Getenv("XBAR_GITHUB_ACCESS_TOKEN"), SmallSample: *small, PrintErrors: *shouldPrintErrs, } if !*skipdata { if err := reader.All(ctx); err != nil { return err } } else { metadata.CategoryEnsurePath(categories, nil, []string{"Parent"}) metadata.CategoryEnsurePath(categories, nil, []string{"Parent", "Child"}) metadata.CategoryEnsurePath(categories, nil, []string{"One"}) metadata.CategoryEnsurePath(categories, nil, []string{"Two"}) metadata.CategoryEnsurePath(categories, nil, []string{"Three"}) plugins = []metadata.Plugin{ { Path: "Parent/Child/plugin1.1m.sh", Title: "Plugin 1", ImageURL: "https://xbarapp.com/public/img/xbar-menu-preview.png", Authors: []metadata.Person{ { GitHubUsername: "matryer", Name: "Mat Ryer", ImageURL: "https://avatars.githubusercontent.com/u/101659?s=400&u=1d1b31bbb68719f4514834440b6ea53e99f91be1&v=4", Bio: "Something about Mat goes here", Primary: true, }, }, }, { Path: "Parent/Child/plugin2.1m.sh", Title: "Plugin 2", ImageURL: "https://xbarapp.com/public/img/xbar-menu-preview.png", Authors: []metadata.Person{ { GitHubUsername: "leaanthony", Name: "Lea Anthony", ImageURL: "https://avatars.githubusercontent.com/u/1943904?s=460&v=4", Bio: "Something about Lea goes here", Primary: true, }, }, }, } } sort.Slice(plugins, func(i, j int) bool { return plugins[i].Title < plugins[j].Title }) if err := g.mkdirall(); err != nil { return errors.Wrap(err, "mkdirall") } d := imageDownloader{ client: &http.Client{ Timeout: 5 * time.Second, }, outputDir: *out, } d.DownloadImages(plugins) for _, plugin := range plugins { pluginsByPath[plugin.Dir] = append(pluginsByPath[plugin.Dir], plugin) } featuredPlugins := metadata.RandomPlugins(pluginsByPath, "", 6) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() if err := g.generatePluginsIndexPage(categories, pluginsByPath, featuredPlugins); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generatePluginsIndexPage")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateCategoriesJSON(categories); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateCategoriesJSON")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateCategoryPluginsJSONFiles(categories, pluginsByPath); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateCategoryPluginsJSONFiles")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateCategoryPages(categories, categories, pluginsByPath); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateCategoryPages")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generatePluginPages(categories, plugins); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generatePluginPages")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateContributorPages(categories, pluginsByPath); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateContributorPages")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateFeaturedPluginsJSON(featuredPlugins); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateFeaturedPluginsJSON")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateContributorsPage(categories, plugins); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateContributorsPage")) } } }() wg.Add(1) go func() { defer wg.Done() if err := g.generateBigPluginsPayload(plugins); err != nil { log.Println(errors.Wrap(err, "generateBigPluginsPayload")) } }() wg.Wait() fmt.Println() log.Printf("processed %d plugins\n", len(allPlugins)) var articles []Article if !*nodocs { articles, err = generateDocs(ctx) if err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateDocs")) } } } if err := g.generateSitemap(categories, pluginsByPath, articles, *out); err != nil { if *shouldPrintErrs { log.Println(errors.Wrap(err, "generateSitemap")) } } return nil } type generator struct { outputDir string pluginsDir string authorsDir string peopleCycleIndex int categoryTemplate *template.Template pluginTemplate *template.Template indexTemplate *template.Template contributorTemplate *template.Template contributorsTemplate *template.Template } func newGenerator(outputDir string) (*generator, error) { categoryTemplate, err := template.ParseFiles( filepath.Join("../../xbarapp.com", "templates", "_layout.html"), filepath.Join("../../xbarapp.com", "templates", "category.html"), ) if err != nil { return nil, err } pluginTemplate, err := template.ParseFiles( filepath.Join("../../xbarapp.com", "templates", "_layout.html"), filepath.Join("../../xbarapp.com", "templates", "plugin.html"), ) if err != nil { return nil, err } indexTemplate, err := template.ParseFiles( filepath.Join("../../xbarapp.com", "templates", "_layout.html"), filepath.Join("../../xbarapp.com", "templates", "index.html"), ) if err != nil { return nil, err } contributorTemplate, err := template.ParseFiles( filepath.Join("../../xbarapp.com", "templates", "_layout.html"), filepath.Join("../../xbarapp.com", "templates", "contributor.html"), ) if err != nil { return nil, err } contributorsTemplate, err := template.ParseFiles( filepath.Join("../../xbarapp.com", "templates", "_layout.html"), filepath.Join("../../xbarapp.com", "templates", "contributors.html"), ) if err != nil { return nil, err } g := &generator{ outputDir: outputDir, pluginsDir: filepath.Join(outputDir, "plugins"), authorsDir: filepath.Join(outputDir, "contributors"), categoryTemplate: categoryTemplate, pluginTemplate: pluginTemplate, indexTemplate: indexTemplate, contributorTemplate: contributorTemplate, contributorsTemplate: contributorsTemplate, } return g, nil } func (g *generator) mkdirall() error { if err := os.MkdirAll(g.outputDir, 0777); err != nil { return err } if err := os.MkdirAll(g.authorsDir, 0777); err != nil { return err } if err := os.MkdirAll(g.pluginsDir, 0777); err != nil { return err } return nil } func (g *generator) generateContributorPages(categories map[string]metadata.Category, pluginsByPath map[string][]metadata.Plugin) error { authors := make(map[string]metadata.Person) for _, plugins := range pluginsByPath { for _, plugin := range plugins { for _, author := range plugin.Authors { if author.Name == "" { continue } if author.GitHubUsername == "" { continue } authors[author.GitHubUsername] = author } } } for _, author := range authors { if err := g.generateContributorPage(categories, pluginsByPath, author); err != nil { return err } if err := g.generateAuthorJSON(pluginsByPath, author); err != nil { return err } } return nil } func (g *generator) generateAuthorJSON(pluginsByPath map[string][]metadata.Plugin, author metadata.Person) error { var thisUsersPlugins []metadata.Plugin for _, plugins := range pluginsByPath { for _, plugin := range plugins { for _, person := range plugin.Authors { if person.GitHubUsername == author.GitHubUsername { thisUsersPlugins = append(thisUsersPlugins, plugin) } } } } f, err := os.Create(filepath.Join(g.authorsDir, author.GitHubUsername+".json")) if err != nil { return err } defer f.Close() payload := struct { Version string `json:"version"` LastUpdated string `json:"lastUpdated"` Person metadata.Person `json:"person"` Plugins []metadata.Plugin `json:"plugins"` }{ Version: version, LastUpdated: time.Now().Format(time.RFC822), Person: author, Plugins: thisUsersPlugins, } b, err := json.MarshalIndent(payload, "", "\t") if err != nil { return err } _, err = f.Write(b) if err != nil { return err } return nil } func (g *generator) generateContributorPage(categories map[string]metadata.Category, pluginsByPath map[string][]metadata.Plugin, author metadata.Person) error { var thisUsersPlugins []metadata.Plugin for _, plugins := range pluginsByPath { for _, plugin := range plugins { for _, pluginAuthor := range plugin.Authors { if pluginAuthor.GitHubUsername == author.GitHubUsername { thisUsersPlugins = append(thisUsersPlugins, plugin) } } } } f, err := os.Create(filepath.Join(g.authorsDir, author.GitHubUsername+".html")) if err != nil { return err } defer f.Close() j, err := json.Marshal(categories) if err != nil { return err } pageData := struct { Version string CurrentCategoryPath string Categories map[string]metadata.Category Author metadata.Person Plugins []metadata.Plugin CategoriesJSON template.JS LastUpdatedFormatted string }{ CurrentCategoryPath: firstSegment(""), Categories: categories, Author: author, Plugins: thisUsersPlugins, CategoriesJSON: template.JS(j), LastUpdatedFormatted: time.Now().Format(time.RFC822), Version: version, } if err := g.contributorTemplate.ExecuteTemplate(f, "_main", pageData); err != nil { return err } g.peopleCycleIndex, err = peopleCycle.Print(os.Stdout, g.peopleCycleIndex) if err != nil { return err } fmt.Print() return nil } func (g *generator) generateContributorsPage(categories map[string]metadata.Category, plugins []metadata.Plugin) error { people := make(map[string]metadata.Person) for _, plugin := range plugins { for _, person := range plugin.Authors { if person.GitHubUsername == "" { continue } if person.ImageURL == "" { continue } if person.Name == "" { continue } people[person.GitHubUsername] = person } } f, err := os.Create(filepath.Join(g.authorsDir, "index.html")) if err != nil { return err } defer f.Close() pageData := struct { Version string CurrentCategoryPath string Categories map[string]metadata.Category People map[string]metadata.Person CategoriesJSON template.JS LastUpdatedFormatted string PeopleLen int }{ CurrentCategoryPath: firstSegment(""), Categories: categories, People: people, LastUpdatedFormatted: time.Now().Format(time.RFC822), Version: version, PeopleLen: len(people), } if err := g.contributorsTemplate.ExecuteTemplate(f, "_main", pageData); err != nil { return err } return nil } func (g *generator) generatePluginsIndexPage( categories map[string]metadata.Category, pluginsByPath map[string][]metadata.Plugin, featuredPlugins []metadata.Plugin, ) error { f, err := os.Create(filepath.Join(g.pluginsDir, "index.html")) if err != nil { return err } defer f.Close() j, err := json.Marshal(categories) if err != nil { return err } pageData := struct { Version string CurrentCategoryPath string Categories map[string]metadata.Category PluginsByPath map[string][]metadata.Plugin GetPlugins func(string) ([]metadata.Plugin, error) CategoriesJSON template.JS FeaturedPlugins []metadata.Plugin LastUpdatedFormatted string }{ CurrentCategoryPath: firstSegment(""), Categories: categories, PluginsByPath: pluginsByPath, CategoriesJSON: template.JS(j), FeaturedPlugins: featuredPlugins, LastUpdatedFormatted: time.Now().Format(time.RFC822), Version: version, } pageData.GetPlugins = func(pathPrefix string) ([]metadata.Plugin, error) { var matchingPlugins []metadata.Plugin for path, plugins := range pluginsByPath { if strings.HasPrefix(path, pathPrefix) { matchingPlugins = append(matchingPlugins, plugins...) } } return matchingPlugins, nil } if err := g.indexTemplate.ExecuteTemplate(f, "_main", pageData); err != nil { return err } return nil } func (g *generator) generateCategoriesJSON(categories map[string]metadata.Category) error { categoryList := g.categoriesToCategory(categories) f, err := os.Create(filepath.Join(g.pluginsDir, "categories.json")) if err != nil { return err } defer f.Close() payload := struct { Version string `json:"version"` LastUpdated string `json:"lastUpdated"` Categories []metadata.Category `json:"categories"` }{ Version: version, LastUpdated: time.Now().Format(time.RFC822), Categories: categoryList, } b, err := json.MarshalIndent(payload, "", "\t") if err != nil { return err } if _, err := io.WriteString(f, string(b)); err != nil { return err } return nil } func (g *generator) generateCategoryPluginsJSONFiles(categories map[string]metadata.Category, pluginsByPath map[string][]metadata.Plugin) error { for _, category := range categories { // collect all plugins that are children to this category. var plugins []metadata.Plugin for path, thesePlugins := range pluginsByPath { if strings.HasPrefix(path, category.Path) { plugins = append(plugins, thesePlugins...) } } // sort plugins by name sort.Slice(plugins, func(i, j int) bool { return plugins[i].Title < plugins[j].Title }) if err := g.generateCategoryPluginsJSON(category.Path, plugins); err != nil { return err } if err := g.generateCategoryPluginsJSONFiles(category.ChildrenCategories, pluginsByPath); err != nil { return err } } return nil } func (g *generator) generateFeaturedPluginsJSON(featuredPlugins []metadata.Plugin) error { filename := filepath.Join(g.pluginsDir, "featured-plugins.json") payload := struct { Version string `json:"version"` LastUpdated string `json:"lastUpdated"` Plugins []metadata.Plugin `json:"plugins"` }{ Version: version, LastUpdated: time.Now().Format(time.RFC822), Plugins: featuredPlugins, } b, err := json.MarshalIndent(payload, "", "\t") if err != nil { return err } f, err := os.Create(filename) if err != nil { return err } defer f.Close() if _, err := io.WriteString(f, string(b)); err != nil { return err } return nil } func (g *generator) generateCategoryPluginsJSON(categoryPath string, plugins []metadata.Plugin) error { dir := filepath.Join(g.pluginsDir, categoryPath) if err := os.MkdirAll(dir, 0700); err != nil { return err } filename := filepath.Join(dir, "plugins.json") payload := struct { Version string `json:"version"` LastUpdated string `json:"lastUpdated"` Plugins []metadata.Plugin `json:"plugins"` }{ Version: version, LastUpdated: time.Now().Format(time.RFC822), Plugins: plugins, } // sort the plugins by title sort.Slice(payload.Plugins, func(i, j int) bool { return payload.Plugins[i].Title < payload.Plugins[j].Title }) b, err := json.MarshalIndent(payload, "", "\t") if err != nil { return err } f, err := os.Create(filename) if err != nil { return err } defer f.Close() if _, err := io.WriteString(f, string(b)); err != nil { return err } return nil } func (g *generator) generateBigPluginsPayload(plugins []metadata.Plugin) error { f, err := os.Create(filepath.Join(g.pluginsDir, "all-plugins.json")) if err != nil { return err } defer f.Close() payload := struct { Version string `json:"version"` LastUpdated string `json:"lastUpdated"` Plugins []metadata.Plugin `json:"plugins"` }{ Version: version, LastUpdated: time.Now().Format(time.RFC822), Plugins: plugins, } b, err := json.MarshalIndent(payload, "", "\t") if err != nil { return err } if _, err := f.Write(b); err != nil { return err } return nil } func (g *generator) generatePluginPages(categories map[string]metadata.Category, plugins []metadata.Plugin) error { for _, plugin := range plugins { if err := g.generatePluginPage(categories, plugin); err != nil { return err } if err := g.generatePluginJSONPayload(plugin); err != nil { return err } } return nil } func (g generator) generatePluginJSONPayload(plugin metadata.Plugin) error { dir := filepath.Dir(filepath.Join(g.pluginsDir, plugin.Path)) if err := os.MkdirAll(dir, 0700); err != nil { return err } jsonFilePath := filepath.Join(g.pluginsDir, plugin.Path+".json") f, err := os.Create(jsonFilePath) if err != nil { return err } defer f.Close() payload := struct { Version string `json:"version"` LastUpdated string `json:"lastUpdated"` Plugin metadata.Plugin `json:"plugin"` }{ Version: version, LastUpdated: time.Now().Format(time.RFC822), Plugin: plugin, } b, err := json.MarshalIndent(payload, "", "\t") if err != nil { return err } if _, err := f.Write(b); err != nil { return err } return nil } func (g *generator) generatePluginPage(categories map[string]metadata.Category, plugin metadata.Plugin) error { dir := filepath.Dir(filepath.Join(g.pluginsDir, plugin.Path)) if err := os.MkdirAll(dir, 0700); err != nil { return err } pagePath := filepath.Join(g.pluginsDir, plugin.Path+".html") f, err := os.Create(pagePath) if err != nil { return err } defer f.Close() j, err := json.Marshal(categories) if err != nil { return err } pageData := struct { Version string CurrentCategoryPath string Categories map[string]metadata.Category Plugin metadata.Plugin CategoriesJSON template.JS LastUpdatedFormatted string }{ CurrentCategoryPath: firstSegment(plugin.CategoryPath), Categories: categories, Plugin: plugin, CategoriesJSON: template.JS(j), LastUpdatedFormatted: time.Now().Format(time.RFC822), Version: version, } if err := g.pluginTemplate.ExecuteTemplate(f, "_main", pageData); err != nil { return err } fmt.Print("🔌") return nil } func (g *generator) generateCategoryPages(allcategories, categories map[string]metadata.Category, pluginsByPath map[string][]metadata.Plugin) error { for _, category := range categories { if err := g.generateCategoryPage(allcategories, category, pluginsByPath, g.pluginsDir); err != nil { return err } if err := g.generateCategoryPages(allcategories, category.ChildrenCategories, pluginsByPath); err != nil { return err } } return nil } func (g *generator) generateCategoryPage(categories map[string]metadata.Category, category metadata.Category, pluginsByPath map[string][]metadata.Plugin, outputDir string) error { pagePath := filepath.Join(outputDir, category.Path+".html") pathDir := filepath.Dir(pagePath) if err := os.MkdirAll(pathDir, 0777); err != nil { return err } f, err := os.Create(pagePath) if err != nil { return err } defer f.Close() var plugins []metadata.Plugin for _, ps := range pluginsByPath { for _, p := range ps { if strings.HasPrefix(p.Path, category.Path) { plugins = append(plugins, p) } } } j, err := json.Marshal(categories) if err != nil { return err } pageData := struct { Version string CurrentCategoryPath string Categories map[string]metadata.Category Category metadata.Category Plugins []metadata.Plugin FeaturedPlugins []metadata.Plugin CategoriesJSON template.JS LastUpdatedFormatted string }{ CurrentCategoryPath: firstSegment(category.Path), Categories: categories, Category: category, Plugins: plugins, FeaturedPlugins: metadata.RandomPlugins(pluginsByPath, category.Path, 3), CategoriesJSON: template.JS(j), LastUpdatedFormatted: time.Now().Format(time.RFC822), Version: version, } if err := g.categoryTemplate.ExecuteTemplate(f, "_main", pageData); err != nil { return err } return nil } func (g *generator) generateSitemap(categories map[string]metadata.Category, pluginsByPath map[string][]metadata.Plugin, articles []Article, outputDir string) error { now := time.Now() sm := sitemap.New() sm.Add(&sitemap.URL{ Loc: "https://xbarapp.com/", LastMod: &now, ChangeFreq: sitemap.Weekly, }) for _, article := range articles { sm.Add(&sitemap.URL{ Loc: "https://xbarapp.com/docs/" + article.Path, LastMod: &now, ChangeFreq: sitemap.Weekly, }) } var addCategory func(categories map[string]metadata.Category) addCategory = func(categories map[string]metadata.Category) { for _, category := range categories { sm.Add(&sitemap.URL{ Loc: "https://xbarapp.com/docs/plugins/" + category.Path + ".html", LastMod: &now, ChangeFreq: sitemap.Weekly, }) addCategory(category.ChildrenCategories) } } addCategory(categories) for _, plugins := range pluginsByPath { for _, plugin := range plugins { sm.Add(&sitemap.URL{ Loc: "https://xbarapp.com/docs/plugins/" + plugin.Path + ".html", LastMod: &now, ChangeFreq: sitemap.Monthly, }) } } f, err := os.Create(filepath.Join(outputDir, "sitemap.xml")) if err != nil { return err } defer f.Close() zf, err := os.Create(filepath.Join(outputDir, "sitemap.xml.gz")) if err != nil { return err } defer zf.Close() w := zip.NewWriter(zf) defer w.Close() zippedFile, err := w.Create("sitemap.xml") if err != nil { return err } if _, err := sm.WriteTo(io.MultiWriter(f, zippedFile)); err != nil { return err } return nil } func (g *generator) categoriesToCategory(categories map[string]metadata.Category) []metadata.Category { cats := make([]metadata.Category, 0, len(categories)) for _, cat := range categories { c := metadata.Category{ Path: cat.Path, Text: cat.Text, CategoryPathSegments: cat.CategoryPathSegments, LastUpdated: cat.LastUpdated, } c.Children = g.categoriesToCategory(cat.ChildrenCategories) cats = append(cats, c) } sort.Slice(cats, func(i, j int) bool { return cats[i].Text < cats[j].Text }) return cats } func firstSegment(path string) string { return strings.Split(path, "/")[0] } var ( moonCycle = cycleString{"\r🌕", "\r🌖", "\r🌗", "\r🌘", "\r🌑", "\r🌒", "\r🌓", "\r🌔"} peopleCycle = cycleString{"😃 "} ) // cycleString cycles through strings. // Make a counter and use Print to write the strings and keep count. // // moonCycle = cycleString{"🌕", "🌖", "🌗", "🌘", "🌑", "🌒", "🌓", "🌔"} // var moonIndex int // var err error // for { // moonIndex, err = moonCycle.Print(os.Stdout, moonIndex) // if err != nil { // return errors.Wrap(err, "write error") // } // } type cycleString []string // Next gets the index of the next string. func (c cycleString) Print(w io.Writer, i int) (int, error) { if _, err := io.WriteString(w, c[i]); err != nil { return i, err } i++ if i >= len(c) { i = 0 } return i, nil } ================================================ FILE: tools/sitegen/repo.go ================================================ package main import ( "context" "encoding/base64" "fmt" "path" "path/filepath" "strings" "sync" "time" "github.com/google/go-github/github" "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" "golang.org/x/oauth2" ) // EachFunc is the callback that recievesplugins // described with metadata.Plugin. type EachFunc func(payload metadata.Plugin) // RepoReader reads the repo calling EachFunc for each // page of metadata.Plugin results. // Any parsing errors are ignored. // Connection errors will return. type RepoReader struct { RepoOwner string RepoName string EachPluginFn EachFunc GitHubAccessToken string PrintErrors bool // SmallSample will only run a small selection of // items. Useful for dev/testing. SmallSample bool usersLock sync.RWMutex // protects users users map[string]*github.User } // All walks all items in the plugin repository. func (r *RepoReader) All(ctx context.Context) error { r.users = make(map[string]*github.User) ts := oauth2.StaticTokenSource( &oauth2.Token{ AccessToken: r.GitHubAccessToken, }, ) var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) defer cancel() tc := oauth2.NewClient(ctx, ts) tc.Timeout = 2 * time.Second gh := github.NewClient(tc) const branchName = "master" // todo: update this branch, _, err := gh.Repositories.GetBranch(ctx, r.RepoOwner, r.RepoName, branchName) if err != nil { return errors.Wrapf(err, "get branch: %s/%s[%s]", r.RepoOwner, r.RepoName, branchName) } sha := branch.Commit.SHA tree, _, err := gh.Git.GetTree(ctx, r.RepoOwner, r.RepoName, *sha, true) if err != nil { return errors.Wrapf(err, "get tree: %s/%s (%s)", r.RepoOwner, r.RepoName, *sha) } stop := make(chan struct{}) payloadChan := make(chan metadata.Plugin) errChan := make(chan error) go func() { for payload := range payloadChan { r.EachPluginFn(payload) } }() go func() { for err := range errChan { if r.PrintErrors { fmt.Printf("\n%s\n", err) } } }() var wg sync.WaitGroup semaphore := make(chan struct{}, 8) defer close(stop) // stop should fire first if r.SmallSample { tree.Entries = tree.Entries[:50] } for _, item := range tree.Entries { time.Sleep(50 * time.Millisecond) // keep cool if err := ctx.Err(); err != nil { return err } if *item.Type == "blob" { if strings.HasPrefix(path.Base(*item.Path), ".") { // skip dotfiles continue } if filepath.Ext(*item.Path) == ".md" { // skip markdown files continue } wg.Add(1) select { case semaphore <- struct{}{}: // will block if buffer is full case <-ctx.Done(): return ctx.Err() case <-stop: return nil } go func(ctx context.Context, gh *github.Client, treeEntry github.TreeEntry) { defer wg.Done() defer func() { select { case <-stop: return case <-time.After(100 * time.Millisecond): // pace <-semaphore // release slot } }() // todo: skip .github and other dotfiles payload, err := r.loadPluginMetadata(ctx, gh, *treeEntry.Path, treeEntry) if err != nil { err = errors.Wrapf(err, "loadPluginMetadata: %v", *treeEntry.Path) select { case <-stop: return case errChan <- err: } return } select { case <-stop: return case payloadChan <- payload: } }(ctx, gh, item) } } wg.Wait() return nil } func (r *RepoReader) loadPluginMetadata(ctx context.Context, gh *github.Client, path string, treeEntry github.TreeEntry) (metadata.Plugin, error) { var plugin metadata.Plugin if err := ctx.Err(); err != nil { return plugin, err } blob, _, err := gh.Git.GetBlob(ctx, r.RepoOwner, r.RepoName, *treeEntry.SHA) if err != nil { return plugin, errors.Wrapf(err, "get blob: %s/%s (%s)", r.RepoOwner, r.RepoName, *treeEntry.SHA) } if blob.Content == nil && *blob.Content != "" { return plugin, errors.Wrapf(err, "empty blob: %s/%s (%s)", r.RepoOwner, r.RepoName, *treeEntry.SHA) } decodedContent, err := base64.StdEncoding.DecodeString(*blob.Content) if err != nil { return plugin, errors.Wrapf(err, "decode blob: %s/%s (%s)", r.RepoOwner, r.RepoName, *treeEntry.SHA) } plugin, err = metadata.Parse(metadata.DebugfNoop, path, string(decodedContent)) if err != nil { return plugin, err } plugin.Path = path plugin.DocsPlugin = path + ".html" plugin.DocsCategory = filepath.Dir(path) + ".html" plugin.CategoryPath = filepath.Dir(path) if err := plugin.Complete(); err != nil { return plugin, err } for i := range plugin.Authors { if plugin.Authors[i].GitHubUsername != "" { var user *github.User var ok bool r.usersLock.RLock() user, ok = r.users[plugin.Authors[i].GitHubUsername] r.usersLock.RUnlock() if !ok { user, _, err = gh.Users.Get(ctx, plugin.Authors[i].GitHubUsername) if err != nil { return plugin, errors.Wrapf(err, "get %q from GitHub", plugin.Authors[i].GitHubUsername) } r.usersLock.Lock() r.users[plugin.Authors[i].GitHubUsername] = user r.usersLock.Unlock() } if user.Name != nil { plugin.Authors[i].Name = *user.Name } if user.AvatarURL != nil { plugin.Authors[i].ImageURL = *user.AvatarURL } if user.Bio != nil { plugin.Authors[i].Bio = *user.Bio } } } return plugin, nil } ================================================ FILE: tools/sitegen/repo_test.go ================================================ package main import ( "context" "os" "testing" "time" "github.com/matryer/is" "github.com/matryer/xbar/pkg/metadata" ) func TestReadRepo(t *testing.T) { if testing.Short() { t.Skip("skipping") return } is := is.New(t) ctx := context.Background() var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, 2*time.Minute) defer cancel() each := EachFunc(func(payload metadata.Plugin) { //log.Printf("discovered plugin: %v\b", payload.Path) }) r := &RepoReader{ RepoOwner: "matryer", RepoName: "xbar-plugins", EachPluginFn: each, GitHubAccessToken: os.Getenv("XBAR_GITHUB_ACCESS_TOKEN"), } err := r.All(ctx) is.NoErr(err) // ReadRepo } ================================================ FILE: tools/sitegen/run.sh ================================================ #!/bin/bash set -e export VERSION=`git describe --tags` echo -n "${VERSION}" > .version go build -o sitegen ./sitegen $1 rm sitegen # run the tests in xbarapp.com - we may have # just broken them cd ../../xbarapp.com npm install ./gen.sh npm run build go test ================================================ FILE: tools/xbarmdcheck/README.md ================================================ # xbarmdcheck - Plugin metadata check Usage: ``` cat plugin-file.sh | xbarmdcheck ``` Exit code `0` means it's valid. Exit code `1` indicates invalid. ================================================ FILE: tools/xbarmdcheck/go.mod ================================================ module github.com/matryer/xbar/tools/xbarmdcheck go 1.16 replace github.com/matryer/xbar/pkg/metadata => ../../pkg/metadata require github.com/matryer/xbar/pkg/metadata v0.0.0-00010101000000-000000000000 ================================================ FILE: tools/xbarmdcheck/go.sum ================================================ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= ================================================ FILE: tools/xbarmdcheck/main.go ================================================ package main import ( "encoding/json" "fmt" "io" "os" "github.com/matryer/xbar/pkg/metadata" ) func main() { if err := run(); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } } func run() error { input, err := io.ReadAll(os.Stdin) if err != nil { return err } md, err := metadata.Parse(metadata.DebugfLog, "stdin", string(input)) if err != nil { return err } b, err := json.MarshalIndent(md, "", "\t") if err != nil { return err } fmt.Println(string(b)) if err := md.Validate(); err != nil { return err } return nil } ================================================ FILE: tools/xbarmdcheck/testdata/sample-plugin.sh ================================================ # Metadata allows your plugin to show up in the app, and website. # # Title goes here # v1.0 # Your Name, Another author name # your-github-username,another-github-username # Short description of what your plugin does. # http://www.hosted-somewhere/pluginimage # python,ruby,node # http://url-to-about.com/ # Variables become preferences in the app: # # string(VAR_NAME="Mat Ryer"): Your name. # number(VAR_COUNTER=1): A counter. # boolean(VAR_VERBOSE=true): Whether to be verbose or not. # select(VAR_STYLE="normal"): Which style to use. [small, normal, big] ================================================ FILE: xbarapp.com/.gcloudignore ================================================ # This file specifies files that are *not* uploaded to Google Cloud Platform # using gcloud. It follows the same syntax as .gitignore, with the addition of # "#!include" directives (which insert the entries of the given .gitignore-style # file at that point). # # For more information, run: # $ gcloud topic gcloudignore # .gcloudignore # If you would like to upload your .git directory, .gitignore file or files # from your .gitignore file, remove the corresponding line # below: .git .gitignore .DS_Store # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out node_modules package-lock.json ================================================ FILE: xbarapp.com/README.md ================================================ ## Run in dev ``` go run main.go ``` ## deploy ```bash gcloud app deploy --project xbarapp --version 6 ``` ## Install ``` npm install ``` ## Build front-end styles ``` npm run build ``` ================================================ FILE: xbarapp.com/app.yaml ================================================ service: default runtime: go122 main: . handlers: - url: /docs/ static_dir: public/docs - url: /public/img/ static_dir: public/img - url: /public/css/ static_dir: public/css - url: /index.html static_files: public/plugins/index.html upload: public/plugins/index.html - url: /.* script: auto ================================================ FILE: xbarapp.com/articles/2021/03/13/Upgrade-legacy-BitBar-plugins.md ================================================ xbar (the BitBar reboot) has shipped, and you might want to upgrade your BitBar plugin. Your plugin will run in xbar without any changes. However, there are a few tweaks you should make and one or two shiny new features to look at. # What's new/changed in xbar? 1. Unlock new features by upgrading your metadata - change `` tags to `` 1. Use the `shell` parameter instead of `bash` 1. There is now no limit to the number of `paramN` parameters you can use 1. Use variables (new feature) instead of asking users to edit your scripts 1. Add keyboard shortcuts (new feature) to make your plugins even easier to use ## Variables for configuration Variables are defined in your plugin's metadata. The xbar UI lets users configure the values without editing your script. * To learn more, read about the [New Variables feature in xbar](/docs/2021/03/14/new-variables-feature-in-xbar.html) ## Keyboard shortcuts xbar lets you specify keyboard shortcuts for your menu items. Use the `key` parameter: ``` Let's go | key=shift+g | href=https://xbarapp.com/ ``` You can specify a range of modifiers and special keys, for a full list check out the [Parameters documentation](https://github.com/matryer/xbar#parameters). # Please try xbar, and report any issues xbar is still brand new, so please help us reach a full release by reporting any issues you find. ================================================ FILE: xbarapp.com/articles/2021/03/14/Variables-in-xbar.md ================================================ xbar plugins can now define Variables in their metadata. End users will be prompted to input the values when they install the plugin. The [new xbar app](https://github.com/matryer/xbar/releases/latest) renders variables like this: ![](xbar-plugin-with-variables.jpg) ## Use cases Variables are great for: * API keys or tokens that your plugin needs * Different style or presentation options * Letting users decide how many items to show ## Defining variables To define variables for your plugin, use `xbar.var` tags. ``` type(VAR_NAME=default): description [options] ``` * `type` - the kind of field (can be `string`, `number`, `boolean`, or `select`) * `VAR_NAME` - the name of the variable (will become an environment variable when the plugin runs). If you prefix your variable name with `"VAR_"`, xbar will nicely format the variable label in the UI Prefix names with `VAR_` and use underscores for spaces * `default` - the default/initial value for this variable * `description` - a short label describing the variable * `[options]` - for `select` types, a comma separated list of option strings Some examples include: ```html # string(VAR_NAME="Mat Ryer"): Your name. # number(VAR_COUNTER=1): A counter. # boolean(VAR_VERBOSE=true): Verbose or not? # select(VAR_STYLE="normal"): Which style to use. [small, normal, big] ``` ### Select options The `[small, normal, big]` in the example above shows the valid options. It is required for `select` types. ## Using variables in your plugin The `NAME` you use in the metadata will become the environment variable that holds the value input by the user. ```bash # string(VAR_NAME="World"): Your name. echo "Hello, ${VAR_NAME}" ``` * Remember: Environment variable values are always strings ## Values are stored in sidecar JSON files Variables are stored in JSON files alongside your plugin. The key is the name of the Variable as well as the name of the environment variable. The values are the user's preferences, usually input through the xbar app. For example, the variables file for the `tail.5s.sh` plugin looks like this: ```json tail.5s.sh.vars.json { "VAR_FILE": "./001-tail.5s.sh", "VAR_LINES": 15 } ``` ## Plugins using Variables are more likely to get featured When deciding which plugins to feature, we are planning on prioritising plugins that make use of more modern xbar features. It shows they're recently tested and verified to be working, and it shows that the maintainer(s) are still involved. ================================================ FILE: xbarapp.com/build.sh ================================================ #!/bin/bash cd ../tools/sitegen ./run.sh cd ../../xbarapp.com ./gen.sh ================================================ FILE: xbarapp.com/deploy.sh ================================================ #!/bin/bash set -e VERSION=`git describe --tags` printf "package main\n\nconst version = \"${VERSION}\"" > version.gen.go echo "Running test..." go test -v echo "Deploying xbarapp.com (${VERSION})..." gcloud app deploy --project xbarapp --version=beta ================================================ FILE: xbarapp.com/download.go ================================================ package main import "net/http" // downloadHandler gets an http.HandlerFunc that provides that // latest release, and counts the download. func downloadHandler() http.HandlerFunc { const url = "https://github.com/matryer/xbar/releases/latest" return func(w http.ResponseWriter, r *http.Request) { // todo: log this download http.Redirect(w, r, url, http.StatusFound) } } ================================================ FILE: xbarapp.com/gen.sh ================================================ #/bin/bash set -e VERSION=`git describe --tags` printf "package main\n\nconst version = \"${VERSION}\"" > version.gen.go ================================================ FILE: xbarapp.com/go.mod ================================================ module github.com/matryer/xbar/xbarapp.com go 1.19 require ( github.com/matryer/is v1.4.0 github.com/matryer/xbar/pkg/metadata v0.0.0-20210312151302-803da66ba9c6 ) require github.com/pkg/errors v0.9.1 // indirect // replace github.com/matryer/xbar/pkg/metadata => ../pkg/metadata ================================================ FILE: xbarapp.com/go.sum ================================================ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/xbar/pkg/metadata v0.0.0-20210312151302-803da66ba9c6 h1:69UBI+rLtZQ2JXqwtu9XEL0wSPAcCZAU2JCsnYrv9Yw= github.com/matryer/xbar/pkg/metadata v0.0.0-20210312151302-803da66ba9c6/go.mod h1:KYMTrUZYW0od/4hkL0Psb9zSwRVv4soFN6Np2W0QGu8= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= ================================================ FILE: xbarapp.com/main.go ================================================ package main import ( "fmt" "net/http" "os" "path/filepath" ) func main() { if err := run(); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } func run() error { port := os.Getenv("PORT") if port == "" { port = "9000" } port = ":" + port http.Handle("/docs/", http.StripPrefix("/docs/", http.FileServer(http.Dir(filepath.Join("public", "docs"))))) http.Handle("/public/img/", http.StripPrefix("/public/img/", http.FileServer(http.Dir(filepath.Join("public", "img"))))) http.Handle("/public/css/", http.StripPrefix("/public/css/", http.FileServer(http.Dir(filepath.Join("public", "css"))))) http.Handle("/dl", downloadHandler()) http.Handle("/", serveFileHandler(filepath.Join("public", "docs", "plugins", "index.html"))) fmt.Printf("listening on 0.0.0.0%s\n", port) return http.ListenAndServe("0.0.0.0"+port, nil) } func serveFileHandler(filename string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, filename) } } ================================================ FILE: xbarapp.com/main_test.go ================================================ package main import ( "encoding/json" "io" "os" "path/filepath" "strings" "testing" "github.com/matryer/is" "github.com/matryer/xbar/pkg/metadata" ) var docsFolder = filepath.Join("public", "docs") func TestPluginMetadata(t *testing.T) { is := is.New(t) pluginMetadataPath := filepath.Join(docsFolder, "/plugins/Dev/Tutorial/cycle_text_and_detail.sh.json") // if the file doesn't exist - skip the test if _, err := os.Stat(pluginMetadataPath); os.IsNotExist(err) { t.Skipf("file %s doesn't exist", pluginMetadataPath) return } p := loadPluginMetadata(is, pluginMetadataPath) is.True(p.Version != "") // version doesn't match is.True(p.LastUpdated != "") is.Equal(p.Plugin.Path, "Dev/Tutorial/cycle_text_and_detail.sh") is.Equal(len(p.Plugin.Files), 1) is.Equal(p.Plugin.Files[0].Filename, "cycle_text_and_detail.sh") is.True(p.Plugin.Files[0].Content != "") is.Equal(p.Plugin.Filename, "cycle_text_and_detail.sh") is.Equal(p.Plugin.Dir, "Dev/Tutorial") is.Equal(p.Plugin.DocsPlugin, "Dev/Tutorial/cycle_text_and_detail.sh.html") is.Equal(p.Plugin.DocsCategory, "Dev/Tutorial.html") is.Equal(len(p.Plugin.PathSegments), 2) is.Equal(p.Plugin.PathSegments[0], "Dev") is.Equal(p.Plugin.PathSegments[1], "Tutorial") is.Equal(len(p.Plugin.CategoryPathSegments), 2) is.Equal(p.Plugin.CategoryPathSegments[0].Text, "Dev") is.Equal(p.Plugin.CategoryPathSegments[0].Path, "Dev") is.Equal(p.Plugin.CategoryPathSegments[0].IsLast, false) is.Equal(p.Plugin.CategoryPathSegments[1].Text, "Tutorial") is.Equal(p.Plugin.CategoryPathSegments[1].Path, "Dev/Tutorial") is.Equal(p.Plugin.CategoryPathSegments[1].IsLast, true) is.Equal(p.Plugin.Version, "v1.0") is.Equal(p.Plugin.Title, "Cycle text and detail text") is.Equal(p.Plugin.Desc, "Example of how to include items that cycle in the top, and items that only appear in the dropdown.") is.Equal(p.Plugin.Author, "Mat Ryer") is.Equal(len(p.Plugin.Authors), 1) is.Equal(p.Plugin.Authors[0].GitHubUsername, "matryer") is.True(p.Plugin.ImageURL != "") is.Equal(strings.Join(p.Plugin.Dependencies, ", "), "") is.Equal(p.Plugin.AboutURL, "https://github.com/matryer/bitbar-plugins/blob/master/Tutorial/cycle_text_and_detail.sh") is.Equal(p.Plugin.LastUpdated.IsZero(), false) is.Equal(len(p.Plugin.Vars), 0) } type pluginPayload struct { Version string LastUpdated string Plugin metadata.Plugin } func loadPluginMetadata(is *is.I, path string) pluginPayload { is.Helper() f, err := os.Open(path) is.NoErr(err) // Open defer f.Close() b, err := io.ReadAll(f) is.NoErr(err) // ReadAll var payload pluginPayload err = json.Unmarshal(b, &payload) is.NoErr(err) return payload } ================================================ FILE: xbarapp.com/package.json ================================================ { "name": "xbarapp.com", "scripts": { "build:tailwind": "NODE_ENV=production postcss styles.css -o public/css/xbar.css", "build": "npm run build:tailwind" }, "devDependencies": { "autoprefixer": "^10.2.4", "cssnano": "^4.1.10", "postcss": "^8.4.18", "postcss-cli": "^8.3.1", "tailwindcss": "^2.0.3" } } ================================================ FILE: xbarapp.com/postcss.config.js ================================================ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, cssnano: { preset: 'default', }, } } ================================================ FILE: xbarapp.com/public/css/xbar.css ================================================ /*! xbar styles */html{min-height:100%}body{min-height:100vh;background:#0f0c29;background-image:linear-gradient(60deg,#29323c,#485563);overflow-x:hidden}.nice-wrapping{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;hyphens:auto}header{background:linear-gradient(180deg,transparent 0,rgba(0,0,0,.1) 10%,rgba(0,0,0,.3) 80%,rgba(0,0,0,.6))}.fancy-font{font-family:Bodoni Moda,serif}.fancy-font.text-6xl{line-height:1.2}.hyphens-auto{-webkit-hyphens:auto;hyphens:auto}strong.bg-brand-text{display:inline-block;color:#fff}.tiny-photo{display:inline-block;background-color:#fff;transform:rotate(-10deg);border-radius:3px;margin-top:-20px}.tiny-photo img{max-width:32px;max-height:32px;margin-left:32px}.tiny-photo img.primary{max-width:64px;max-height:64px;margin-left:0}.light-background{background:#fff;background:linear-gradient(-135deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.15))}#inner-space{color:#fff}#inner-space,#menubar{background-color:hsla(0,0%,100%,.2)}#menubar{text-shadow:1px 1px 2px rgba(0,0,0,.4);width:100%;font-size:14px;z-index:100}#menubar a{padding:2px 8px}#menubar a.selected{background-color:hsla(0,0%,100%,.15)}.plugin-image{max-height:150px} /*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com*/ /*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */html{-moz-tab-size:4;-o-tab-size:4;tab-size:4;line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],button{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{font-family:inherit;line-height:inherit}*,:after,:before{box-sizing:border-box;border:0 solid}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder, textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:after,:before{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.m-8{margin:2rem}.m-16{margin:4rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-8{margin-top:2rem;margin-bottom:2rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-16{margin-top:4rem}.mr-1{margin-right:.25rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mb-16{margin-bottom:4rem}.ml-3{margin-left:.75rem}.ml-8{margin-left:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-8{height:2rem}.h-24{height:6rem}.max-h-48{max-height:12rem}.w-8{width:2rem}.w-24{width:6rem}.w-full{width:100%}.max-w-sm{max-width:24rem}.max-w-md{max-width:28rem}.max-w-lg{max-width:32rem}.max-w-xl{max-width:36rem}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-full{max-width:100%}.max-w-screen-md{max-width:768px}.flex-grow{flex-grow:1}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.gap-4{gap:1rem}.gap-8{gap:2rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(0.5rem*var(--tw-space-x-reverse));margin-left:calc(0.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.overflow-hidden,.truncate{overflow:hidden}.truncate{text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded-sm{border-radius:.125rem}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.border-b-4{border-bottom-width:4px}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}.bg-white,.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-opacity-25{--tw-bg-opacity:0.25}.p-2{padding:.5rem}.p-4{padding:1rem}.p-8{padding:2rem}.p-16{padding:4rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-12{padding-left:3rem;padding-right:3rem}.px-16{padding-left:4rem;padding-right:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pt-0{padding-top:0}.pt-16{padding-top:4rem}.pb-2{padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pb-8{padding-bottom:2rem}.pb-24{padding-bottom:6rem}.pb-32{padding-bottom:8rem}.text-center{text-align:center}.text-right{text-align:right}.align-bottom{vertical-align:bottom}.text-sm{font-size:.875rem;line-height:1.25rem}.text-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.font-bold{font-weight:700}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-10{line-height:2.5rem}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgba(31,41,55,var(--tw-text-opacity))}.hover\:text-black:hover{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.hover\:underline:hover,.underline{text-decoration:underline}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.hover\:opacity-100:hover{opacity:1}*,:after,:before{--tw-shadow:0 0 transparent}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,0.1),0 1px 2px 0 rgba(0,0,0,0.06)}.shadow,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,0.1),0 2px 4px -1px rgba(0,0,0,0.06)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,0.1),0 10px 10px -5px rgba(0,0,0,0.04)}.shadow-2xl,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,0.25)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}*,:after,:before{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,0.5);--tw-ring-offset-shadow:0 0 transparent;--tw-ring-shadow:0 0 transparent}.filter{--tw-blur:var(--tw-empty,/*!*/ /*!*/);--tw-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-invert:var(--tw-empty,/*!*/ /*!*/);--tw-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-drop-shadow:var(--tw-empty,/*!*/ /*!*/);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@media (min-width:768px){.md\:my-16{margin-top:4rem;margin-bottom:4rem}.md\:mt-0{margin-top:0}.md\:mr-8{margin-right:2rem}.md\:block{display:block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:h-12{height:3rem}.md\:w-12{width:3rem}.md\:w-1\/3{width:33.333333%}.md\:w-2\/3{width:66.666667%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.md\:px-16{padding-left:4rem;padding-right:4rem}.md\:py-16{padding-top:4rem;padding-bottom:4rem}.md\:pb-2{padding-bottom:.5rem}.md\:text-right{text-align:right}.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-6xl{font-size:3.75rem;line-height:1}}@media (min-width:1024px){.lg\:flex{display:flex}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.lg\:py-8{padding-top:2rem;padding-bottom:2rem}.lg\:py-12{padding-top:3rem;padding-bottom:3rem}.lg\:pb-32{padding-bottom:8rem}} ================================================ FILE: xbarapp.com/run.sh ================================================ #!/bin/bash go build -o xbarappcom && ./xbarappcom rm xbarappcom ================================================ FILE: xbarapp.com/styles.css ================================================ /*! xbar styles */ html { min-height: 100%; } body { min-height: 100vh; background: #0f0c29; /* fallback for old browsers */ background-image: linear-gradient(60deg, #29323c 0%, #485563 100%); overflow-x: hidden; } /* from https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ */ .nice-wrapping { /* These are technically the same, but use both */ overflow-wrap: break-word; word-wrap: break-word; -ms-word-break: break-all; /* This is the dangerous one in WebKit, as it breaks things wherever */ word-break: break-all; /* Instead use this non-standard one: */ word-break: break-word; /* Adds a hyphen where the word breaks, if supported (No Blink) */ -ms-hyphens: auto; -moz-hyphens: auto; -webkit-hyphens: auto; hyphens: auto; } header { background: linear-gradient( to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.3) 80%, rgba(0, 0, 0, 0.6) 100% ); } .fancy-font { font-family: "Bodoni Moda", serif; } .fancy-font.text-6xl { line-height: 1.2; } .hyphens-auto { hyphens: auto; } strong.bg-brand-text { display: inline-block; color: white; } .tiny-photo { display: inline-block; background-color: white; transform: rotate(-10deg); border-radius: 3px; margin-top: -20px; } .tiny-photo img { max-width: 32px; max-height: 32px; margin-left: 32px; } .tiny-photo img.primary { max-width: 64px; max-height: 64px; margin-left: 0px; } .light-background { background: rgb(255, 255, 255); background: linear-gradient( -135deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.15) 100% ); } #inner-space { background-color: rgba(255, 255, 255, 0.2); color: white; } #menubar { background-color: rgba(255, 255, 255, 0.2); text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.4); width: 100%; font-size: 14px; z-index: 100; } #menubar a { padding: 2px 8px; } #menubar a.selected { background-color: rgba(255, 255, 255, 0.15); } .plugin-image { max-height: 150px; } @tailwind base; @tailwind components; @tailwind utilities; ================================================ FILE: xbarapp.com/tailwind.config.js ================================================ module.exports = { purge: { enabled: true, content: [ "./public/**/*.html", "../tools/sitegen/templates/*.html" ], options: { keyframes: true, }, }, darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], } ================================================ FILE: xbarapp.com/templates/_layout.html ================================================ {{ define "title" }}xbar, the BitBar reboot{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}

    There is no good reason why you should ever see this. But here we are. Maybe you can let me know by Tweeting me @matryer, to let me know.

    Thanks,
    Mat.

    {{ end }} {{ define "_main" }} {{ template "title" . }} {{ template "head" . }}
    {{ template "body" . }} {{ end }} {{ define "plugins" }} {{ if . }}
    {{ range . }}

    {{ .Title }}

    {{ if .Desc }}{{ if not .ImageURL }}—{{ .Desc }}{{ end }}{{ end }} {{ if .ImageURL }}
    {{if ne .ImageURL "https://xbarapp.com/public/img/xbar-2048.png"}} {{ end }}

    {{ .Desc }}

    {{ range .Authors }} {{ end }} {{ else }} {{ .Filename }} {{ end }}
    {{ end }}
    {{ end }} {{ end }} {{ define "support" }}

    💜
    Show your support
    by
    Sponsoring xbar

    Every contribution makes a big difference

    If you use xbar, please consider sponsoring it on GitHub

    {{ end }} ================================================ FILE: xbarapp.com/templates/article.html ================================================ {{ define "title" }}{{ .Article.Title }}{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}
    {{ .Article.PublishTimeStr }}

    {{ .Article.Title }}

    {{ .Article.HTML }}

    You might also like

    {{ range .AllArticles }}

    {{ .Title }}

    {{ end }}
    {{ template "support" . }}
    {{ end }} ================================================ FILE: xbarapp.com/templates/articles-index.html ================================================ {{ define "title" }}Welcome to xbar docs{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}

    Articles

      {{ range .AllArticles }}
    • {{ .PublishTimeStr }} {{ .Title }}
    • {{ end }}
    {{ end }} ================================================ FILE: xbarapp.com/templates/category.html ================================================ {{ define "title" }}{{ .Category.Text }} in your macOS menu bar{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}
    {{ if .FeaturedPlugins }}
    {{ range .FeaturedPlugins }}
    Featured {{ if .Desc }}

    {{ .Desc }}

    {{ end }} {{ range .Authors }} {{ if .Name }}
    {{ if .GitHubUsername }} {{ .Name }} (@{{ .GitHubUsername }}) {{ if .ImageURL }} {{ end }} {{ else }} {{ .Name }} {{ end }}
    {{ end }} {{ end }}
    {{ end }}

    Fancy having your plugin featured? Get in touch.

    {{ end }} {{ if .Category.ChildrenCategories }}

    Looking for something specific?

    {{ template "subcategories" .Category.ChildrenCategories }}

    {{ end }}
    {{ template "plugins" .Plugins }}
    {{ end }} {{ define "subcategories" }} {{ if . }} {{ range $k, $v := . }} {{ $v.Text }} {{ template "subcategories" .ChildrenCategories }} {{ end }} {{ end }} {{ end }} ================================================ FILE: xbarapp.com/templates/contributor.html ================================================ {{ define "title" }}{{ .Author.Name }} is on xbar{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}

    {{ .Author.Name }}—@{{ .Author.GitHubUsername }}

    {{ .Author.Name }}—@{{ .Author.GitHubUsername }}

    {{ .Author.NiceBio }}

    {{ if eq .Author.GitHubUsername "matryer" }}

    Support xbar on GitHub Sponsors

    {{ else }}

    Visit @{{ .Author.GitHubUsername }} on GitHub

    {{ end }} {{ if .Plugins }} {{ template "plugins-list" .Plugins }} {{ end }}
    {{ end }} {{ define "plugins-list" }} {{ if . }} {{ range . }} {{ end }} {{ end }} {{ end }} ================================================ FILE: xbarapp.com/templates/contributors.html ================================================ {{ define "title" }}{{ .PeopleLen }} wonderful people have contributed xbar plugins{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}
    {{ range .People -}} Photo of the wonderful {{ .Desc }} {{- end }}
    {{ end }} ================================================ FILE: xbarapp.com/templates/index.html ================================================ {{ define "title" }}Welcome to xbar{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}

    Put anything in your macOS menu bar

    (the BitBar reboot)

    Screenshot showing xbar menu in action

    Screenshot showing the xbar plugin browser app

    • Hundreds of pre-built plugins to choose from
    • Information you care about, at a glance
    • Write your own plugins—in any language

    with Plugins like these…

    {{ template "plugins" .FeaturedPlugins }}
    Browse hundreds of plugins—by category
    {{ range $k, $v := .Categories }} {{ $v.Text }} {{ end }}

    Meet the plugin contributors…

    {{ template "support" . }} {{ end }} {{ define "subcategories" }} {{ if . }}
      {{ range $k, $v := . }}
    • {{ $v.Text }} {{ template "subcategories" $v.ChildrenCategories }}
    • {{ end }}
    {{ end }} {{ end }} ================================================ FILE: xbarapp.com/templates/plugin.html ================================================ {{ define "title" }}{{ .Plugin.Title }} in your macOS menu bar{{ end }} {{ define "head" }} {{ end }} {{ define "body" }}

    {{ .Plugin.Title }}

    {{ range .Plugin.Authors }} {{ if .Name }} {{ if .GitHubUsername }} {{ else }}

    and {{ .Name }}

    {{ end }} {{ end }} {{ end }} {{ if .Plugin.Desc }}

    {{ .Plugin.Desc }}

    {{ end }} {{ if eq .Plugin.ImageURL "https://xbarapp.com/public/img/xbar-2048.png" }}

    Can you help?—This plugin is missing an image. Please add one (via <xbar.image>...</xbar.image> metadata) to enhance this plugin's presence. View file on GitHub

    {{ end }}
    Image preview of {{ .Plugin.Title }} plugin.
    {{ range .Plugin.Files }}

    {{ .Filename }}

    {{ .Content }}
    {{ end }} {{ end }}