├── .github
└── FUNDING.yml
├── .gitignore
├── Cartfile
├── Cartfile.resolved
├── Carthage
└── Build
│ ├── .Alamofire.version
│ ├── .BurntCocoaUI.version
│ ├── .BurntFoundation.version
│ ├── .Grain.version
│ ├── .Ono.version
│ ├── .Syrup.version
│ └── Mac
│ ├── Alamofire.framework.dSYM
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ └── DWARF
│ │ └── Alamofire
│ ├── Alamofire.framework
│ ├── Alamofire
│ ├── Headers
│ ├── Modules
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── Alamofire
│ │ ├── Headers
│ │ │ ├── Alamofire-Swift.h
│ │ │ └── Alamofire.h
│ │ ├── Modules
│ │ │ ├── Alamofire.swiftmodule
│ │ │ │ ├── arm64-apple-macos.swiftdoc
│ │ │ │ ├── arm64-apple-macos.swiftmodule
│ │ │ │ ├── arm64.swiftdoc
│ │ │ │ ├── arm64.swiftmodule
│ │ │ │ ├── x86_64-apple-macos.swiftdoc
│ │ │ │ ├── x86_64-apple-macos.swiftmodule
│ │ │ │ ├── x86_64.swiftdoc
│ │ │ │ └── x86_64.swiftmodule
│ │ │ └── module.modulemap
│ │ └── Resources
│ │ │ └── Info.plist
│ │ └── Current
│ ├── BurntCocoaUI.framework.dSYM
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ └── DWARF
│ │ └── BurntCocoaUI
│ ├── BurntCocoaUI.framework
│ ├── BurntCocoaUI
│ ├── Headers
│ ├── Modules
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── BurntCocoaUI
│ │ ├── Headers
│ │ │ ├── BurntCocoaUI-Swift.h
│ │ │ └── BurntCocoaUI.h
│ │ ├── Modules
│ │ │ ├── BurntCocoaUI.swiftmodule
│ │ │ │ ├── arm64-apple-macos.swiftdoc
│ │ │ │ ├── arm64-apple-macos.swiftmodule
│ │ │ │ ├── arm64.swiftdoc
│ │ │ │ ├── arm64.swiftmodule
│ │ │ │ ├── x86_64-apple-macos.swiftdoc
│ │ │ │ ├── x86_64-apple-macos.swiftmodule
│ │ │ │ ├── x86_64.swiftdoc
│ │ │ │ └── x86_64.swiftmodule
│ │ │ └── module.modulemap
│ │ └── Resources
│ │ │ └── Info.plist
│ │ └── Current
│ ├── BurntFoundation.framework.dSYM
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ └── DWARF
│ │ └── BurntFoundation
│ ├── BurntFoundation.framework
│ ├── BurntFoundation
│ ├── Headers
│ ├── Modules
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── BurntFoundation
│ │ ├── Headers
│ │ │ ├── BurntFoundation-Swift.h
│ │ │ └── BurntFoundation.h
│ │ ├── Modules
│ │ │ ├── BurntFoundation.swiftmodule
│ │ │ │ ├── arm64-apple-macos.swiftdoc
│ │ │ │ ├── arm64-apple-macos.swiftmodule
│ │ │ │ ├── arm64.swiftdoc
│ │ │ │ ├── arm64.swiftmodule
│ │ │ │ ├── x86_64-apple-macos.swiftdoc
│ │ │ │ ├── x86_64-apple-macos.swiftmodule
│ │ │ │ ├── x86_64.swiftdoc
│ │ │ │ └── x86_64.swiftmodule
│ │ │ └── module.modulemap
│ │ └── Resources
│ │ │ └── Info.plist
│ │ └── Current
│ ├── Grain.framework.dSYM
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ └── DWARF
│ │ └── Grain
│ ├── Grain.framework
│ ├── Grain
│ ├── Headers
│ ├── Modules
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── Grain
│ │ ├── Headers
│ │ │ ├── Grain-Swift.h
│ │ │ └── Grain.h
│ │ ├── Modules
│ │ │ ├── Grain.swiftmodule
│ │ │ │ ├── x86_64.swiftdoc
│ │ │ │ └── x86_64.swiftmodule
│ │ │ └── module.modulemap
│ │ └── Resources
│ │ │ └── Info.plist
│ │ └── Current
│ ├── Ono.framework.dSYM
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ └── DWARF
│ │ └── Ono
│ ├── Ono.framework
│ ├── Headers
│ ├── Modules
│ ├── Ono
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── Headers
│ │ │ ├── ONOXMLDocument.h
│ │ │ └── Ono.h
│ │ ├── Modules
│ │ │ └── module.modulemap
│ │ ├── Ono
│ │ └── Resources
│ │ │ └── Info.plist
│ │ └── Current
│ ├── Syrup.framework.dSYM
│ └── Contents
│ │ ├── Info.plist
│ │ └── Resources
│ │ └── DWARF
│ │ └── Syrup
│ └── Syrup.framework
│ ├── Headers
│ ├── Modules
│ ├── Resources
│ ├── Syrup
│ └── Versions
│ ├── A
│ ├── Headers
│ │ ├── Syrup-Swift.h
│ │ └── Syrup.h
│ ├── Modules
│ │ ├── Syrup.swiftmodule
│ │ │ ├── arm64-apple-macos.swiftdoc
│ │ │ ├── arm64-apple-macos.swiftmodule
│ │ │ ├── arm64.swiftdoc
│ │ │ ├── arm64.swiftmodule
│ │ │ ├── x86_64-apple-macos.swiftdoc
│ │ │ ├── x86_64-apple-macos.swiftmodule
│ │ │ ├── x86_64.swiftdoc
│ │ │ └── x86_64.swiftmodule
│ │ └── module.modulemap
│ ├── Resources
│ │ └── Info.plist
│ └── Syrup
│ └── Current
├── LICENSE
├── Lantern.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── Hoverlytics.xccheckout
│ │ ├── IDEWorkspaceChecks.plist
│ │ ├── Lantern.xccheckout
│ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── pgwsmith.xcuserdatad
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ ├── Lantern Model.xcscheme
│ └── Lantern.xcscheme
├── Lantern
├── AppDelegate.swift
├── Base.lproj
│ └── Main.storyboard
├── BrowserMenuController.swift
├── BrowserPreferences.swift
├── ContentPreview.storyboard
├── ContentPreview.swift
├── CrawlerMenuController.swift
├── CrawlerPreferences.swift
├── Credits.rtf
├── ImagePreviewViewController.swift
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ ├── 128x128.png
│ │ ├── 16x16.png
│ │ ├── 256x256-1.png
│ │ ├── 256x256.png
│ │ ├── 32x32-1.png
│ │ ├── 32x32.png
│ │ ├── 32x32@2x.png
│ │ ├── 512x512-1.png
│ │ ├── 512x512.png
│ │ ├── 512x512@2x.png
│ │ └── Contents.json
├── Info.plist
├── Lantern-Bridging-Header.h
├── Lantern.entitlements
├── MainSection.swift
├── MainState.swift
├── MainView.storyboard
├── MainWindowController.swift
├── MetaViewController.swift
├── MultipleStringPreviewViewController.swift
├── NSEvent+extension.swift
├── NSViewController+PGWSConstraintConvenience.h
├── NSViewController+PGWSConstraintConvenience.m
├── Page.storyboard
├── PageMapper+UI.swift
├── PageViewController.swift
├── SiteMenuItem.swift
├── SiteSettings.storyboard
├── SiteSettingsViewController.swift
├── SourcePreviewViewController.swift
├── StatsViewController.swift
├── TableView-OutlineView-Subclass.swift
├── ValidatedStringValue+Presentation.swift
├── ViewController.swift
├── ViewState.swift
├── WKUserContentController+bundledScripts.swift
├── code-sign-frameworks.sh
├── console.js
├── insertHoverlytics.js
├── panelAuthorizationChanged.js
├── setPanelAuthorizationToken.js
├── userAgent.js
└── windowClose.js
├── LanternModel
├── Dictionary+UpdateClosure.swift
├── EnabledFeatures.swift
├── Info.plist
├── LanternModel.h
├── ModelManager.swift
├── NSData+ONOXMLDocument.swift
├── ONOXMLDocument+StringEncoding.swift
├── PageInfo.swift
├── PageInfoRequest.swift
├── PageMapper+CSV.swift
├── PageMapper+Filtering.swift
├── PageMapper+InfoPresenting.swift
├── PageMapper+LinkFiltering.swift
├── PageMapper+Validating.swift
├── PageMapper.swift
├── Site.swift
├── SiteEssentialsInfo.swift
├── SystemDirectory.swift
├── UniqueURLArray.swift
├── ValidatedStringValue.swift
├── ValueStorable.swift
└── ValueValidation.swift
├── LanternModelTests
├── Info.plist
└── LanternModelTests.swift
├── LanternTests
├── Info.plist
└── LanternTests.swift
├── Makefile
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: lantern
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Carthage/Checkouts
2 | xcuserdata
3 | /Lantern.xcodeproj/xcuserdata
4 | /build
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire" ~> 4.7
2 | github "burntcaramel/Ono" "master"
3 | github "burntcaramel/BurntFoundation" >= 0.7
4 | github "burntcaramel/BurntCocoaUI" >= 0.6.0
5 | github "burntcaramel/Syrup" >= 0.10
6 |
7 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire" "4.9.1"
2 | github "burntcaramel/BurntCocoaUI" "0.6.0"
3 | github "burntcaramel/BurntFoundation" "0.8.0"
4 | github "burntcaramel/Ono" "f7facf05e2e1c020a8421c8b3836755f9f15f2dc"
5 | github "burntcaramel/Syrup" "0.10.0"
6 |
--------------------------------------------------------------------------------
/Carthage/Build/.Alamofire.version:
--------------------------------------------------------------------------------
1 | {
2 | "commitish" : "4.9.1",
3 | "Mac" : [
4 | {
5 | "hash" : "b15bbc536b5d7123db92652c3239a77bce9d279e271de43fe1f6297d6611ef86",
6 | "name" : "Alamofire",
7 | "linking" : "dynamic",
8 | "swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/Carthage/Build/.BurntCocoaUI.version:
--------------------------------------------------------------------------------
1 | {
2 | "commitish" : "0.6.0",
3 | "Mac" : [
4 | {
5 | "hash" : "75254fc433075ae1ad7d18fdc44a301eb4054fef727b951ceeefb7ee439d8199",
6 | "name" : "BurntCocoaUI",
7 | "linking" : "dynamic",
8 | "swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/Carthage/Build/.BurntFoundation.version:
--------------------------------------------------------------------------------
1 | {
2 | "commitish" : "0.8.0",
3 | "Mac" : [
4 | {
5 | "hash" : "19097d4ee677017d2bbfddcd9dad5318ed60a2719d5628a1ab40566014af6995",
6 | "name" : "BurntFoundation",
7 | "linking" : "dynamic",
8 | "swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/Carthage/Build/.Grain.version:
--------------------------------------------------------------------------------
1 | {
2 | "commitish" : "0.8.3",
3 | "Mac" : [
4 | {
5 | "name" : "Syrup",
6 | "hash" : "6b64f7d2f01aa5c3eafd2cf982d2ac80b2a01585bf1b376f8393398f09e4c516"
7 | }
8 | ]
9 | }
--------------------------------------------------------------------------------
/Carthage/Build/.Ono.version:
--------------------------------------------------------------------------------
1 | {
2 | "commitish" : "f7facf05e2e1c020a8421c8b3836755f9f15f2dc",
3 | "Mac" : [
4 | {
5 | "name" : "Ono",
6 | "hash" : "4999445f7dce5b1d18b80af11e1ef0070460aaf6cc72d08397437ec62dec3ae6",
7 | "linking" : "dynamic"
8 | }
9 | ]
10 | }
--------------------------------------------------------------------------------
/Carthage/Build/.Syrup.version:
--------------------------------------------------------------------------------
1 | {
2 | "commitish" : "0.10.0",
3 | "Mac" : [
4 | {
5 | "hash" : "b5ebfa6e94a52bdff89a248d72828f1987d290fe27d3d0ae6ccd5b294a844032",
6 | "name" : "Syrup",
7 | "linking" : "dynamic",
8 | "swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.org.alamofire.Alamofire
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 4.9.1
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework.dSYM/Contents/Resources/DWARF/Alamofire:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework.dSYM/Contents/Resources/DWARF/Alamofire
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Alamofire:
--------------------------------------------------------------------------------
1 | Versions/Current/Alamofire
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Alamofire:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Alamofire
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Headers/Alamofire.h:
--------------------------------------------------------------------------------
1 | //
2 | // Alamofire.h
3 | //
4 | // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | @import Foundation;
26 |
27 | FOUNDATION_EXPORT double AlamofireVersionNumber;
28 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[];
29 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/arm64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/Alamofire.swiftmodule/x86_64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module Alamofire {
2 | umbrella header "Alamofire.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
8 | module Alamofire.Swift {
9 | header "Alamofire-Swift.h"
10 | requires objc
11 | }
12 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 20E5210c
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | Alamofire
11 | CFBundleIdentifier
12 | org.alamofire.Alamofire
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | Alamofire
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 4.9.1
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 12E262
33 | DTPlatformName
34 | macosx
35 | DTPlatformVersion
36 | 11.3
37 | DTSDKBuild
38 | 20E214
39 | DTSDKName
40 | macosx11.3
41 | DTXcode
42 | 1250
43 | DTXcodeBuild
44 | 12E262
45 | LSMinimumSystemVersion
46 | 10.10
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Alamofire.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.com.burntcaramel.BurntCocoaUI
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 0.5
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework.dSYM/Contents/Resources/DWARF/BurntCocoaUI:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework.dSYM/Contents/Resources/DWARF/BurntCocoaUI
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/BurntCocoaUI:
--------------------------------------------------------------------------------
1 | Versions/Current/BurntCocoaUI
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/BurntCocoaUI:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/BurntCocoaUI
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Headers/BurntCocoaUI.h:
--------------------------------------------------------------------------------
1 | //
2 | // BurntCocoaUI.h
3 | // BurntCocoaUI
4 | //
5 | // Created by Patrick Smith on 4/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for BurntCocoaUI.
12 | FOUNDATION_EXPORT double BurntCocoaUIVersionNumber;
13 |
14 | //! Project version string for BurntCocoaUI.
15 | FOUNDATION_EXPORT const unsigned char BurntCocoaUIVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/arm64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/BurntCocoaUI.swiftmodule/x86_64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module BurntCocoaUI {
2 | umbrella header "BurntCocoaUI.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
8 | module BurntCocoaUI.Swift {
9 | header "BurntCocoaUI-Swift.h"
10 | requires objc
11 | }
12 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 20E5210c
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | BurntCocoaUI
11 | CFBundleIdentifier
12 | com.burntcaramel.BurntCocoaUI
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | BurntCocoaUI
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 0.5
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 12E262
33 | DTPlatformName
34 | macosx
35 | DTPlatformVersion
36 | 11.3
37 | DTSDKBuild
38 | 20E214
39 | DTSDKName
40 | macosx11.3
41 | DTXcode
42 | 1250
43 | DTXcodeBuild
44 | 12E262
45 | LSMinimumSystemVersion
46 | 10.10
47 | NSHumanReadableCopyright
48 | Copyright © 2015 Burnt Caramel. All rights reserved.
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntCocoaUI.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.com.burntcaramel.BurntFoundation
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework.dSYM/Contents/Resources/DWARF/BurntFoundation:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework.dSYM/Contents/Resources/DWARF/BurntFoundation
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/BurntFoundation:
--------------------------------------------------------------------------------
1 | Versions/Current/BurntFoundation
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/BurntFoundation:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/BurntFoundation
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Headers/BurntFoundation.h:
--------------------------------------------------------------------------------
1 | //
2 | // BurntFoundation.h
3 | // BurntFoundation
4 | //
5 | // Created by Patrick Smith on 16/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for BurntFoundation.
12 | FOUNDATION_EXPORT double BurntFoundationVersionNumber;
13 |
14 | //! Project version string for BurntFoundation.
15 | FOUNDATION_EXPORT const unsigned char BurntFoundationVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/arm64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/BurntFoundation.swiftmodule/x86_64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module BurntFoundation {
2 | umbrella header "BurntFoundation.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
8 | module BurntFoundation.Swift {
9 | header "BurntFoundation-Swift.h"
10 | requires objc
11 | }
12 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 20E5210c
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | BurntFoundation
11 | CFBundleIdentifier
12 | com.burntcaramel.BurntFoundation
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | BurntFoundation
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 12E262
33 | DTPlatformName
34 | macosx
35 | DTPlatformVersion
36 | 11.3
37 | DTSDKBuild
38 | 20E214
39 | DTSDKName
40 | macosx11.3
41 | DTXcode
42 | 1250
43 | DTXcodeBuild
44 | 12E262
45 | LSMinimumSystemVersion
46 | 10.10
47 | NSHumanReadableCopyright
48 | Copyright © 2015 Burnt Caramel. All rights reserved.
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/BurntFoundation.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.com.burntcaramel.Grain
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 0.2.0
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework.dSYM/Contents/Resources/DWARF/Grain:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Grain.framework.dSYM/Contents/Resources/DWARF/Grain
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Grain:
--------------------------------------------------------------------------------
1 | Versions/Current/Grain
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Grain:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Grain.framework/Versions/A/Grain
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Headers/Grain-Swift.h:
--------------------------------------------------------------------------------
1 | // Generated by Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42)
2 | #pragma clang diagnostic push
3 |
4 | #if defined(__has_include) && __has_include()
5 | # include
6 | #endif
7 |
8 | #pragma clang diagnostic ignored "-Wauto-import"
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #if !defined(SWIFT_TYPEDEFS)
15 | # define SWIFT_TYPEDEFS 1
16 | # if defined(__has_include) && __has_include()
17 | # include
18 | # elif !defined(__cplusplus) || __cplusplus < 201103L
19 | typedef uint_least16_t char16_t;
20 | typedef uint_least32_t char32_t;
21 | # endif
22 | typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
23 | typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
24 | typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
25 | typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
26 | typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
27 | typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
28 | typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
29 | typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
30 | typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
31 | typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
32 | typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
33 | typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
34 | #endif
35 |
36 | #if !defined(SWIFT_PASTE)
37 | # define SWIFT_PASTE_HELPER(x, y) x##y
38 | # define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
39 | #endif
40 | #if !defined(SWIFT_METATYPE)
41 | # define SWIFT_METATYPE(X) Class
42 | #endif
43 | #if !defined(SWIFT_CLASS_PROPERTY)
44 | # if __has_feature(objc_class_property)
45 | # define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
46 | # else
47 | # define SWIFT_CLASS_PROPERTY(...)
48 | # endif
49 | #endif
50 |
51 | #if defined(__has_attribute) && __has_attribute(objc_runtime_name)
52 | # define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
53 | #else
54 | # define SWIFT_RUNTIME_NAME(X)
55 | #endif
56 | #if defined(__has_attribute) && __has_attribute(swift_name)
57 | # define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
58 | #else
59 | # define SWIFT_COMPILE_NAME(X)
60 | #endif
61 | #if defined(__has_attribute) && __has_attribute(objc_method_family)
62 | # define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
63 | #else
64 | # define SWIFT_METHOD_FAMILY(X)
65 | #endif
66 | #if defined(__has_attribute) && __has_attribute(noescape)
67 | # define SWIFT_NOESCAPE __attribute__((noescape))
68 | #else
69 | # define SWIFT_NOESCAPE
70 | #endif
71 | #if defined(__has_attribute) && __has_attribute(warn_unused_result)
72 | # define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
73 | #else
74 | # define SWIFT_WARN_UNUSED_RESULT
75 | #endif
76 | #if !defined(SWIFT_CLASS_EXTRA)
77 | # define SWIFT_CLASS_EXTRA
78 | #endif
79 | #if !defined(SWIFT_PROTOCOL_EXTRA)
80 | # define SWIFT_PROTOCOL_EXTRA
81 | #endif
82 | #if !defined(SWIFT_ENUM_EXTRA)
83 | # define SWIFT_ENUM_EXTRA
84 | #endif
85 | #if !defined(SWIFT_CLASS)
86 | # if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
87 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
88 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
89 | # else
90 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
91 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
92 | # endif
93 | #endif
94 |
95 | #if !defined(SWIFT_PROTOCOL)
96 | # define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
97 | # define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
98 | #endif
99 |
100 | #if !defined(SWIFT_EXTENSION)
101 | # define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
102 | #endif
103 |
104 | #if !defined(OBJC_DESIGNATED_INITIALIZER)
105 | # if defined(__has_attribute) && __has_attribute(objc_designated_initializer)
106 | # define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
107 | # else
108 | # define OBJC_DESIGNATED_INITIALIZER
109 | # endif
110 | #endif
111 | #if !defined(SWIFT_ENUM)
112 | # define SWIFT_ENUM(_type, _name) enum _name : _type _name; enum SWIFT_ENUM_EXTRA _name : _type
113 | # if defined(__has_feature) && __has_feature(generalized_swift_name)
114 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_EXTRA _name : _type
115 | # else
116 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) SWIFT_ENUM(_type, _name)
117 | # endif
118 | #endif
119 | #if !defined(SWIFT_UNAVAILABLE)
120 | # define SWIFT_UNAVAILABLE __attribute__((unavailable))
121 | #endif
122 | #if !defined(SWIFT_UNAVAILABLE_MSG)
123 | # define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
124 | #endif
125 | #if !defined(SWIFT_AVAILABILITY)
126 | # define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
127 | #endif
128 | #if !defined(SWIFT_DEPRECATED)
129 | # define SWIFT_DEPRECATED __attribute__((deprecated))
130 | #endif
131 | #if !defined(SWIFT_DEPRECATED_MSG)
132 | # define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
133 | #endif
134 | #if defined(__has_feature) && __has_feature(modules)
135 | #endif
136 |
137 | #pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
138 | #pragma clang diagnostic ignored "-Wduplicate-method-arg"
139 | #pragma clang diagnostic pop
140 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Headers/Grain.h:
--------------------------------------------------------------------------------
1 | //
2 | // Grain.h
3 | // Grain
4 | //
5 | // Created by Patrick Smith on 17/03/2016.
6 | // Copyright © 2016 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | //! Project version number for Grain.
12 | FOUNDATION_EXPORT double GrainVersionNumber;
13 |
14 | //! Project version string for Grain.
15 | FOUNDATION_EXPORT const unsigned char GrainVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Modules/Grain.swiftmodule/x86_64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Grain.framework/Versions/A/Modules/Grain.swiftmodule/x86_64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Modules/Grain.swiftmodule/x86_64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Grain.framework/Versions/A/Modules/Grain.swiftmodule/x86_64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module Grain {
2 | umbrella header "Grain.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
8 | module Grain.Swift {
9 | header "Grain-Swift.h"
10 | }
11 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 16E195
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | Grain
11 | CFBundleIdentifier
12 | com.burntcaramel.Grain
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | Grain
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 0.2.0
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 8E2002
33 | DTPlatformVersion
34 | GM
35 | DTSDKBuild
36 | 16E185
37 | DTSDKName
38 | macosx10.12
39 | DTXcode
40 | 0832
41 | DTXcodeBuild
42 | 8E2002
43 | NSHumanReadableCopyright
44 | Copyright © 2016 Burnt Caramel. All rights reserved.
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Grain.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.com.mattt.Ono
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework.dSYM/Contents/Resources/DWARF/Ono:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Ono.framework.dSYM/Contents/Resources/DWARF/Ono
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Ono:
--------------------------------------------------------------------------------
1 | Versions/Current/Ono
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Versions/A/Headers/Ono.h:
--------------------------------------------------------------------------------
1 | // Ono.h
2 | //
3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import
24 |
25 | //! Project version number for Ono iOS.
26 | FOUNDATION_EXPORT double Ono_VersionNumber;
27 |
28 | //! Project version string for Ono iOS.
29 | FOUNDATION_EXPORT const unsigned char Ono_VersionString[];
30 |
31 | #import
32 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module Ono {
2 | umbrella header "Ono.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Versions/A/Ono:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Ono.framework/Versions/A/Ono
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 20E5210c
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | Ono
11 | CFBundleIdentifier
12 | com.mattt.Ono
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | Ono
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 12E262
33 | DTPlatformName
34 | macosx
35 | DTPlatformVersion
36 | 11.3
37 | DTSDKBuild
38 | 20E214
39 | DTSDKName
40 | macosx11.3
41 | DTXcode
42 | 1250
43 | DTXcodeBuild
44 | 12E262
45 | LSMinimumSystemVersion
46 | 10.10
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Ono.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.com.burntcaramel.Syrup
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 0.10.0
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework.dSYM/Contents/Resources/DWARF/Syrup:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework.dSYM/Contents/Resources/DWARF/Syrup
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Syrup:
--------------------------------------------------------------------------------
1 | Versions/Current/Syrup
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Headers/Syrup.h:
--------------------------------------------------------------------------------
1 | //
2 | // Syrup.h
3 | // Syrup
4 | //
5 | // Created by Patrick Smith on 17/03/2016.
6 | // Copyright © 2016–2017 Patrick Smith. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | //! Project version number for Syrup.
12 | FOUNDATION_EXPORT double SyrupVersionNumber;
13 |
14 | //! Project version string for Syrup.
15 | FOUNDATION_EXPORT const unsigned char SyrupVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/arm64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64-apple-macos.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64-apple-macos.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64-apple-macos.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64-apple-macos.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64.swiftdoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64.swiftdoc
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64.swiftmodule:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/Syrup.swiftmodule/x86_64.swiftmodule
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module Syrup {
2 | umbrella header "Syrup.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
8 | module Syrup.Swift {
9 | header "Syrup-Swift.h"
10 | requires objc
11 | }
12 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 20E5210c
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | Syrup
11 | CFBundleIdentifier
12 | com.burntcaramel.Syrup
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | Grain
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 0.10.0
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 12E262
33 | DTPlatformName
34 | macosx
35 | DTPlatformVersion
36 | 11.3
37 | DTSDKBuild
38 | 20E214
39 | DTSDKName
40 | macosx11.3
41 | DTXcode
42 | 1250
43 | DTXcodeBuild
44 | 12E262
45 | LSMinimumSystemVersion
46 | 10.12
47 | NSHumanReadableCopyright
48 | Copyright © 2016–2017 Patrick Smith
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/A/Syrup:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Carthage/Build/Mac/Syrup.framework/Versions/A/Syrup
--------------------------------------------------------------------------------
/Carthage/Build/Mac/Syrup.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Lantern.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/project.xcworkspace/xcshareddata/Hoverlytics.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | D93679C8-6A02-4CC0-AA2C-8C9204E909AE
9 | IDESourceControlProjectName
10 | Hoverlytics
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
14 | burntcaramel.git.beanstalkapp.com:/burntcaramel/hoverlytics-for-mac.git
15 |
16 | IDESourceControlProjectPath
17 | Hoverlytics.xcodeproj
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | burntcaramel.git.beanstalkapp.com:/burntcaramel/hoverlytics-for-mac.git
25 | IDESourceControlProjectVersion
26 | 111
27 | IDESourceControlProjectWCCIdentifier
28 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
36 | IDESourceControlWCCName
37 | Hoverlytics%20for%20Mac
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/project.xcworkspace/xcshareddata/Lantern.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | 7C530460-06FB-4D0C-81F5-3318564DDC8B
9 | IDESourceControlProjectName
10 | project
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
14 | burntcaramel.git.beanstalkapp.com:/burntcaramel/hoverlytics-for-mac.git
15 |
16 | IDESourceControlProjectPath
17 | Lantern.xcodeproj/project.xcworkspace
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | burntcaramel.git.beanstalkapp.com:/burntcaramel/hoverlytics-for-mac.git
25 | IDESourceControlProjectVersion
26 | 111
27 | IDESourceControlProjectWCCIdentifier
28 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | 2F4F68DFA0550A2C87C14C3AC2F3397F1F46A29B
36 | IDESourceControlWCCName
37 | hoverlytics-for-mac
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "CSV.swift",
6 | "repositoryURL": "https://github.com/yaslab/CSV.swift.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "67c5209ae699d5d3ded866de5ee6a6b4005a1b56",
10 | "version": "2.4.2"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/project.xcworkspace/xcuserdata/pgwsmith.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges
6 |
7 | SnapshotAutomaticallyBeforeSignificantChanges
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/xcshareddata/xcschemes/Lantern Model.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/Lantern.xcodeproj/xcshareddata/xcschemes/Lantern.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
42 |
48 |
49 |
50 |
52 |
58 |
59 |
60 |
61 |
62 |
72 |
74 |
80 |
81 |
82 |
83 |
86 |
87 |
88 |
89 |
95 |
97 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/Lantern/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Hoverlytics for Mac
4 | //
5 | // Created by Patrick Smith on 28/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import LanternModel
11 |
12 |
13 | let NSApp = NSApplication.shared
14 |
15 | @NSApplicationMain
16 | class AppDelegate: NSObject, NSApplicationDelegate {
17 |
18 | var browserMenuController: BrowserMenuController!
19 | @IBOutlet var browserWidthPlaceholderMenuItem: NSMenuItem!
20 |
21 | var crawlerMenuController: CrawlerMenuController!
22 | @IBOutlet var crawlerImageDownloadPlaceholderMenuItem: NSMenuItem!
23 |
24 |
25 | deinit {
26 | let nc = NotificationCenter.default
27 | for observer in windowWillCloseObservers {
28 | nc.removeObserver(observer)
29 | }
30 | windowWillCloseObservers.removeAll()
31 | }
32 |
33 | func applicationDidFinishLaunching(_ aNotification: Notification) {
34 | // Create shared manager to ensure quickest start up time.
35 | let modelManager = LanternModel.ModelManager.sharedManager
36 | modelManager.errorReceiver.errorCallback = { error in
37 | NSApp.presentError(error)
38 | }
39 |
40 | browserMenuController = BrowserMenuController(browserWidthPlaceholderMenuItem: browserWidthPlaceholderMenuItem)
41 | crawlerMenuController = CrawlerMenuController(imageDownloadPlaceholderMenuItem: crawlerImageDownloadPlaceholderMenuItem)
42 |
43 | #if DEBUG
44 | // Update the bloody Dock icon
45 | NSApp.applicationIconImage = nil
46 |
47 | UserDefaults.standard.set(true, forKey: "NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints")
48 | #endif
49 | }
50 |
51 | func applicationWillTerminate(_ aNotification: Notification) {
52 | // Insert code here to tear down your application
53 | }
54 |
55 |
56 |
57 | lazy var mainStoryboard: NSStoryboard = {
58 | return NSStoryboard(name: "Main", bundle: nil)
59 | }()
60 |
61 | var mainWindowControllers = [MainWindowController]()
62 | var windowWillCloseObservers = [AnyObject]()
63 |
64 | func applicationOpenUntitledFile(_ sender: NSApplication) -> Bool {
65 | let windowController = mainStoryboard.instantiateInitialController() as! MainWindowController
66 | windowController.showWindow(nil)
67 |
68 | mainWindowControllers.append(windowController)
69 |
70 | let nc = NotificationCenter.default
71 | windowWillCloseObservers.append(nc.addObserver(forName: NSWindow.willCloseNotification, object: windowController.window!, queue: nil, using: { [unowned self] note in
72 | if let index = self.mainWindowControllers.firstIndex(of: windowController) {
73 | self.mainWindowControllers.remove(at: index)
74 | }
75 | }))
76 |
77 | return true
78 | }
79 |
80 | @IBAction func newDocument(_ sender: AnyObject?) {
81 | _ = self.applicationOpenUntitledFile(NSApp)
82 | }
83 |
84 | @IBAction func forkOnGitHub(_ sender: AnyObject?) {
85 | let URL = Foundation.URL(string: "https://github.com/BurntCaramel/Lantern")!
86 | NSWorkspace.shared.open(URL)
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Lantern/BrowserMenuController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BrowserMenuAssistant.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 11/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import BurntFoundation
11 | import BurntCocoaUI
12 |
13 |
14 | extension BrowserWidthChoice : UIChoiceRepresentative {
15 | var tag: Int? { return self.rawValue }
16 |
17 | typealias UniqueIdentifier = BrowserWidthChoice
18 | var uniqueIdentifier: UniqueIdentifier { return self }
19 | }
20 |
21 | extension BrowserWidthChoice : UIChoiceEnumerable {
22 | static var allChoices: [BrowserWidthChoice] {
23 | return [
24 | .slimMobile,
25 | .mediumMobile,
26 | .mediumTabletPortrait,
27 | .mediumTabletLandscape,
28 | .fullWidth
29 | ]
30 | }
31 | }
32 |
33 |
34 |
35 | class BrowserMenuController: NSObject, NSUserInterfaceValidations {
36 | var widthMenuItemsAssistant: PlaceholderMenuItemAssistant!
37 |
38 | var browserPreferencesObserver: NotificationObserver!
39 |
40 | var browserPrefences: BrowserPreferences {
41 | return BrowserPreferences.shared
42 | }
43 |
44 | init(browserWidthPlaceholderMenuItem: NSMenuItem) {
45 | super.init()
46 |
47 | widthMenuItemsAssistant = PlaceholderMenuItemAssistant(placeholderMenuItem: browserWidthPlaceholderMenuItem)
48 | widthMenuItemsAssistant.menuItemRepresentatives = [
49 | .slimMobile,
50 | .mediumMobile,
51 | .mediumTabletPortrait,
52 | .mediumTabletLandscape,
53 | .fullWidth
54 | ]
55 | widthMenuItemsAssistant.customization.actionAndTarget = { [weak self] widthChoice in
56 | return (action: #selector(BrowserMenuController.changeWidthChoice(_:)), target: self)
57 | }
58 | // widthMenuItemsAssistant.customization.state = { widthChoice in
59 | // let chosenWidthChoice = BrowserPreferences.sharedBrowserPreferences.widthChoice
60 | // return (chosenWidthChoice == widthChoice) ? NSOnState : NSOffState
61 | // }
62 | widthMenuItemsAssistant.customization.additionalSetUp = { widthChoice, menuItem in
63 | let chosenWidthChoice = BrowserPreferences.shared.widthChoice
64 | menuItem.state = (chosenWidthChoice == widthChoice) ? NSControl.StateValue.on : NSControl.StateValue.off
65 | }
66 |
67 | updateWidthMenu()
68 |
69 | startObservingBrowserPreferences()
70 | }
71 |
72 | deinit {
73 | stopObservingBrowserPreferences()
74 | }
75 |
76 | func updateWidthMenu() {
77 | widthMenuItemsAssistant.update()
78 | }
79 |
80 | func startObservingBrowserPreferences() {
81 | browserPreferencesObserver = NotificationObserver(object: BrowserPreferences.shared)
82 |
83 | browserPreferencesObserver.observe(.widthChoiceDidChange) { notification in
84 | self.updateWidthMenu()
85 | }
86 | }
87 |
88 | func stopObservingBrowserPreferences() {
89 | browserPreferencesObserver.stopObserving()
90 | browserPreferencesObserver = nil
91 | }
92 |
93 | @IBAction func changeWidthChoice(_ sender: AnyObject?) {
94 | if let
95 | menuItem = sender as? NSMenuItem,
96 | let widthChoice = widthMenuItemsAssistant.itemRepresentative(for: menuItem)
97 | {
98 | BrowserPreferences.shared.widthChoice = widthChoice
99 | }
100 | }
101 |
102 | @objc func validateUserInterfaceItem(_ anItem: NSValidatedUserInterfaceItem) -> Bool {
103 | return true
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Lantern/BrowserPreferences.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BrowserState.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 11/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import BurntFoundation
11 |
12 |
13 | enum BrowserWidthChoice : Int {
14 | case slimMobile = 1
15 | case mediumMobile
16 | case mediumTabletPortrait
17 | case mediumTabletLandscape
18 | case fullWidth
19 |
20 | var value: CGFloat? {
21 | switch self {
22 | case .slimMobile:
23 | return 320.0
24 | case .mediumMobile:
25 | return 480.0
26 | case .mediumTabletPortrait:
27 | return 768.0
28 | case .mediumTabletLandscape:
29 | return 1024.0
30 | case .fullWidth:
31 | return nil
32 | }
33 | }
34 |
35 | var title: String {
36 | switch self {
37 | case .slimMobile:
38 | return "320"
39 | case .mediumMobile:
40 | return "375"
41 | case .mediumTabletPortrait:
42 | return "768"
43 | case .mediumTabletLandscape:
44 | return "1024"
45 | case .fullWidth:
46 | return "Fluid"
47 | }
48 | /*switch self {
49 | case .slimMobile:
50 | return "Slim Mobile (iPhone 4)"
51 | case .mediumMobile:
52 | return "Medium Mobile (iPhone 6)"
53 | case .mediumTabletPortrait:
54 | return "Medium Tablet Portrait (iPad)"
55 | case .mediumTabletLandscape:
56 | return "Medium Tablet Landscape (iPad)"
57 | case .fullWidth:
58 | return "Full Width"
59 | }(*/
60 | }
61 | }
62 |
63 | extension BrowserWidthChoice : UserDefaultsChoiceRepresentable {
64 | static var identifier = "browserPreferences.widthChoice"
65 | static var defaultValue: BrowserWidthChoice = .fullWidth
66 | }
67 |
68 |
69 | private var ud = UserDefaults.standard
70 |
71 |
72 | class BrowserPreferences {
73 | enum Notification: String {
74 | case widthChoiceDidChange = "BrowserPreferences.WidthChoiceDidChange"
75 |
76 | var name : Foundation.Notification.Name {
77 | return Foundation.Notification.Name(rawValue: rawValue)
78 | }
79 | }
80 |
81 | private func notify(_ identifier: Notification, userInfo: [String:AnyObject]? = nil) {
82 | let nc = NotificationCenter.default
83 | nc.post(name: Notification.widthChoiceDidChange.name, object: self, userInfo: userInfo)
84 | }
85 |
86 | var widthChoice: BrowserWidthChoice = .fullWidth {
87 | didSet {
88 | ud.setChoice(widthChoice)
89 |
90 | notify(.widthChoiceDidChange)
91 | }
92 | }
93 |
94 | private func updateFromDefaults() {
95 | widthChoice = ud.choice(BrowserWidthChoice.self)
96 | }
97 |
98 | private init() {
99 | updateFromDefaults()
100 | }
101 |
102 | static var shared = BrowserPreferences()
103 | }
104 |
--------------------------------------------------------------------------------
/Lantern/ContentPreview.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentPreview.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 3/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 |
12 | // Globals are lazy in Swift
13 | var contentPreviewStoryboard: NSStoryboard = NSStoryboard(name: "ContentPreview", bundle: nil)
14 |
15 | extension NSStoryboard {
16 | class var lantern_contentPreviewStoryboard: NSStoryboard {
17 | return contentPreviewStoryboard
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Lantern/CrawlerMenuController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CrawlerMenuController.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 12/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import BurntFoundation
11 | import BurntCocoaUI
12 |
13 |
14 | extension CrawlerImageDownloadChoice: UIChoiceRepresentative {
15 | var tag: Int? { return self.rawValue }
16 |
17 | typealias UniqueIdentifier = CrawlerImageDownloadChoice
18 | var uniqueIdentifier: UniqueIdentifier { return self }
19 | }
20 |
21 |
22 | class CrawlerMenuController: NSObject, NSUserInterfaceValidations {
23 | var imageDownloadMenuItemsAssistant: PlaceholderMenuItemAssistant!
24 |
25 | var crawlerPreferencesObserver: NotificationObserver!
26 |
27 | init(imageDownloadPlaceholderMenuItem: NSMenuItem) {
28 | super.init()
29 |
30 | imageDownloadMenuItemsAssistant = PlaceholderMenuItemAssistant(placeholderMenuItem: imageDownloadPlaceholderMenuItem)
31 | imageDownloadMenuItemsAssistant.menuItemRepresentatives = [
32 | .neverDownload,
33 | .total1MB,
34 | .total10MB,
35 | .total100MB,
36 | .unlimited
37 | ]
38 | imageDownloadMenuItemsAssistant.customization.actionAndTarget = { [weak self] widthChoice in
39 | return (action: #selector(CrawlerMenuController.changeImageDownloadChoice(_:)), target: self)
40 | }
41 | // imageDownloadMenuItemsAssistant.customization.state = { imageDownloadChoice in
42 | // let chosenImageDownloadChoice = CrawlerPreferences.sharedCrawlerPreferences.imageDownloadChoice
43 | // return (chosenImageDownloadChoice == imageDownloadChoice) ? NSOnState : NSOffState
44 | // }
45 | imageDownloadMenuItemsAssistant.customization.additionalSetUp = { imageDownloadChoice, menuItem in
46 | let chosenImageDownloadChoice = CrawlerPreferences.shared.imageDownloadChoice
47 | menuItem.state = (chosenImageDownloadChoice == imageDownloadChoice) ? NSControl.StateValue.on : NSControl.StateValue.off
48 | }
49 |
50 | updateImageDownloadMenu()
51 |
52 | startObservingCrawlerPreferences()
53 | }
54 |
55 | deinit {
56 | stopObservingCrawlerPreferences()
57 | }
58 |
59 | func updateImageDownloadMenu() {
60 | imageDownloadMenuItemsAssistant?.update()
61 | }
62 |
63 | func startObservingCrawlerPreferences() {
64 | crawlerPreferencesObserver = NotificationObserver(object: CrawlerPreferences.shared)
65 |
66 | crawlerPreferencesObserver.observe(.ImageDownloadChoiceDidChange) { notification in
67 | self.updateImageDownloadMenu()
68 | }
69 | }
70 |
71 | func stopObservingCrawlerPreferences() {
72 | crawlerPreferencesObserver.stopObserving()
73 | crawlerPreferencesObserver = nil
74 | }
75 |
76 | @IBAction func changeImageDownloadChoice(_ sender: AnyObject?) {
77 | if let
78 | menuItem = sender as? NSMenuItem,
79 | let imageDownloadChoice = imageDownloadMenuItemsAssistant.itemRepresentative(for: menuItem)
80 | {
81 | CrawlerPreferences.shared.imageDownloadChoice = imageDownloadChoice
82 | }
83 | }
84 |
85 | @objc func validateUserInterfaceItem(_ anItem: NSValidatedUserInterfaceItem) -> Bool {
86 | return true
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Lantern/CrawlerPreferences.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CrawlerPreferences.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 12/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import BurntFoundation
11 |
12 |
13 | private func bytesForMegabytes(_ MB: UInt) -> UInt {
14 | return MB * 1024 * 1024
15 | }
16 |
17 |
18 | enum CrawlerImageDownloadChoice: Int {
19 | case neverDownload = 0
20 | case total1MB = 1
21 | case total10MB = 10
22 | case total100MB = 100
23 | case unlimited = -1
24 |
25 | var maximumByteCount: UInt? {
26 | switch self {
27 | case .neverDownload:
28 | return 0
29 | case .total1MB:
30 | return bytesForMegabytes(1)
31 | case .total10MB:
32 | return bytesForMegabytes(10)
33 | case .total100MB:
34 | return bytesForMegabytes(100)
35 | case .unlimited:
36 | return nil
37 | }
38 | }
39 |
40 | var title: String {
41 | switch self {
42 | case .neverDownload:
43 | return "Never Download"
44 | case .total1MB:
45 | return "Total 1MB"
46 | case .total10MB:
47 | return "Total 10MB"
48 | case .total100MB:
49 | return "Total 100MB"
50 | case .unlimited:
51 | return "Unlimited"
52 | }
53 | }
54 | }
55 |
56 | extension CrawlerImageDownloadChoice: UserDefaultsChoiceRepresentable {
57 | static var identifier = "crawlerPreferences.imageDownloadChoice"
58 | static var defaultValue: CrawlerImageDownloadChoice = .total10MB
59 | }
60 |
61 |
62 | private var ud = UserDefaults.standard
63 |
64 |
65 | class CrawlerPreferences {
66 | enum Notification: String {
67 | case ImageDownloadChoiceDidChange = "CrawlerPreferences.ImageDownloadChoiceDidChange"
68 | }
69 |
70 | func notify(_ identifier: Notification, userInfo: [String:AnyObject]? = nil) {
71 | NotificationCenter.default.postNotification(identifier, object: self, userInfo: userInfo)
72 | }
73 |
74 | var imageDownloadChoice: CrawlerImageDownloadChoice = .total10MB {
75 | didSet {
76 | ud.setChoice(imageDownloadChoice)
77 |
78 | notify(.ImageDownloadChoiceDidChange)
79 | }
80 | }
81 |
82 | func updateFromDefaults() {
83 | imageDownloadChoice = ud.choice(CrawlerImageDownloadChoice.self)
84 | }
85 |
86 | init() {
87 | updateFromDefaults()
88 | }
89 |
90 | static var shared = CrawlerPreferences()
91 | }
92 |
--------------------------------------------------------------------------------
/Lantern/Credits.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf2580
2 | \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 AvenirNext-Medium;\f1\fnil\fcharset0 AvenirNext-MediumItalic;\f2\fnil\fcharset0 AppleColorEmoji;
3 | }
4 | {\colortbl;\red255\green255\blue255;}
5 | {\*\expandedcolortbl;;}
6 | \margl1440\margr1440\vieww9000\viewh8400\viewkind0
7 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
8 |
9 | \f0\fs26 \cf0 \
10 |
11 | \f1\i Support & Feedback
12 | \f0\i0 \
13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
14 | {\field{\*\fldinst{HYPERLINK "mailto:products@burntcaramel.com"}}{\fldrslt \cf0 products@burntcaramel.com}}\
15 | \
16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
17 |
18 | \f1\i \cf0 Website
19 | \f0\i0 \
20 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
21 | {\field{\*\fldinst{HYPERLINK "https://icing.space/tools/lantern/"}}{\fldrslt \cf0 icing.space/tools/lantern}}\
22 | \
23 | Please leave a review on the Mac App Store if you like Lantern!\
24 | \
25 | Designed & Developed in
26 | \f2 \uc0\u55356 \u57103
27 | \f0 Australia\
28 | by Patrick Smith \'b7 {\field{\*\fldinst{HYPERLINK "http://twitter.com/concreteniche"}}{\fldrslt @concreteniche}}\
29 | \
30 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
31 |
32 | \f1\i \cf0 Uses open source:
33 | \f0\i0 \
34 | \
35 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
36 | {\field{\*\fldinst{HYPERLINK "https://github.com/Alamofire/Alamofire"}}{\fldrslt \cf0 https://github.com/Alamofire/Alamofire}}\
37 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
38 |
39 | \fs22 \cf0 \'a9 2014\'962020 Alamofire Software Foundation ({\field{\*\fldinst{HYPERLINK "http://alamofire.org/"}}{\fldrslt http://alamofire.org/}})
40 | \fs26 \
41 | \
42 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
43 | {\field{\*\fldinst{HYPERLINK "https://github.com/mattt/Ono"}}{\fldrslt \cf0 https://github.com/mattt/Ono}}\
44 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
45 |
46 | \fs22 \cf0 \'a9 2014\'962020 Mattt Thompson ({\field{\*\fldinst{HYPERLINK "https://mat.tt/"}}{\fldrslt https://mat.tt/}})\
47 | \
48 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
49 | {\field{\*\fldinst{HYPERLINK "https://github.com/yaslab/CSV.swift"}}{\fldrslt
50 | \fs26 \cf0 https://github.com/yaslab/CSV.swift}}
51 | \fs26 \
52 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
53 |
54 | \fs22 \cf0 \'a9 2016 Yasuhiro Hatta\
55 | \
56 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
57 | {\field{\*\fldinst{HYPERLINK "https://github.com/BurntCaramel/Grain"}}{\fldrslt
58 | \fs26 \cf0 https://github.com/BurntCaramel/Grain}}\
59 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
60 | {\field{\*\fldinst{HYPERLINK "https://github.com/BurntCaramel/BurntFoundation"}}{\fldrslt
61 | \fs26 \cf0 https://github.com/BurntCaramel/BurntFoundation}}
62 | \fs26 \
63 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
64 | {\field{\*\fldinst{HYPERLINK "https://github.com/BurntCaramel/BurntCocoaUI"}}{\fldrslt \cf0 https://github.com/BurntCaramel/BurntCocoaUI}}\
65 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\partightenfactor0
66 |
67 | \fs22 \cf0 \'a9 2015\'962021 Patrick Smith\
68 | }
--------------------------------------------------------------------------------
/Lantern/ImagePreviewViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImagePreviewViewController.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 4/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import Quartz
11 |
12 |
13 | class ImagePreviewViewController: NSViewController {
14 | @IBOutlet var MIMETypeField: NSTextField!
15 | @IBOutlet var pixelWidthField: NSTextField!
16 | @IBOutlet var pixelHeightField: NSTextField!
17 | var innerImageViewController: ImagePreviewInnerViewController?
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 |
22 | view.appearance = NSAppearance(named: NSAppearance.Name.aqua)
23 | }
24 |
25 | override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
26 | if segue.identifier == "imageViewController" {
27 | let innerImageViewController = segue.destinationController as! ImagePreviewInnerViewController
28 |
29 | innerImageViewController.imageData = imageData
30 |
31 | innerImageViewController.wantsToDismiss = {
32 | self.dismiss(nil)
33 | }
34 |
35 | innerImageViewController.imagePropertiesDidLoad = { [weak self] imageProperties in
36 | if let
37 | receiver = self,
38 | let pixelWidthNumber = imageProperties[kCGImagePropertyPixelWidth] as? NSNumber,
39 | let pixelHeightNumber = imageProperties[kCGImagePropertyPixelHeight] as? NSNumber
40 | {
41 | receiver.pixelWidthField.integerValue = pixelWidthNumber.intValue
42 | receiver.pixelHeightField.integerValue = pixelHeightNumber.intValue
43 | }
44 |
45 | }
46 |
47 | self.innerImageViewController = innerImageViewController
48 | }
49 | }
50 |
51 | func resetUI()
52 | {
53 | pixelWidthField.stringValue = ""
54 | pixelHeightField.stringValue = ""
55 | }
56 |
57 | var sourceURL: URL? {
58 | didSet {
59 | if let sourceURL = sourceURL {
60 | title = sourceURL.absoluteString
61 | }
62 | }
63 | }
64 |
65 | var imageData: Data! {
66 | didSet {
67 | resetUI()
68 |
69 | innerImageViewController?.imageData = imageData
70 | }
71 | }
72 |
73 | var MIMEType: String? {
74 | didSet {
75 | MIMETypeField.stringValue = MIMEType ?? ""
76 | }
77 | }
78 |
79 | var contentSizeConstraints = [NSLayoutConstraint]()
80 | var superviewSizeConstraints = [NSLayoutConstraint]()
81 | //var screenWidthConstraint
82 |
83 | #if false
84 | override func updateViewConstraints() {
85 | /*if let screen = view.window?.screen {
86 | let size = screen.visibleFrame.size
87 |
88 | }*/
89 | let view = self.view
90 | let imageView = self.imageView
91 | //if let windowContentView = view.window?.contentView as? NSView {
92 | if let window = view.window, let screen = window.screen {
93 | if self.contentSizeConstraints.count > 0 {
94 | view.removeConstraints(self.contentSizeConstraints)
95 | }
96 |
97 | func layoutConstraintWithView(view: NSView, attribute: NSLayoutAttribute, relatedBy: NSLayoutRelation, constant: CGFloat) -> NSLayoutConstraint {
98 | let constraint = NSLayoutConstraint(item: imageView, attribute: attribute, relatedBy: relatedBy, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: constant)
99 | return constraint
100 | }
101 |
102 | let imageSize = imageView.imageSize()
103 | let screenSize = screen.visibleFrame.size
104 | let windowSize = window.frame.size
105 | let maximumSize = NSSize(
106 | width: min(screenSize.width, windowSize.width),
107 | height: min(screenSize.height, windowSize.height)
108 | )
109 |
110 | let contentSizeConstraints = [
111 | //layoutConstraintWithView(imageView, attribute: .Width, relatedBy: .GreaterThanOrEqual, constant: imageSize.width, required: false),
112 | //layoutConstraintWithView(imageView, attribute: .Height, relatedBy: .GreaterThanOrEqual, constant: imageSize.height, required: false),
113 | layoutConstraintWithView(imageView, attribute: .Width, relatedBy: .LessThanOrEqual, constant: maximumSize.width),
114 | layoutConstraintWithView(imageView, attribute: .Height, relatedBy: .LessThanOrEqual, constant: maximumSize.width)
115 | ]
116 |
117 | view.addConstraints(contentSizeConstraints)
118 |
119 | self.contentSizeConstraints = contentSizeConstraints
120 |
121 | #if false
122 | if let superview = view.superview {
123 | let superviewSizeConstraints = [
124 | layoutConstraintWithView(view, attribute: .Width, relatedBy: .LessThanOrEqual, constant: maximumSize.width),
125 | layoutConstraintWithView(view, attribute: .Height, relatedBy: .LessThanOrEqual, constant: maximumSize.width),
126 | NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .LessThanOrEqual, toItem: superview, attribute: .Width, multiplier: 1.0, constant: 0.0),
127 | NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .LessThanOrEqual, toItem: superview, attribute: .Height, multiplier: 1.0, constant: 0.0),
128 | ]
129 |
130 | superview.addConstraints(superviewSizeConstraints)
131 |
132 | self.superviewSizeConstraints = superviewSizeConstraints
133 | }
134 | else {
135 | self.superviewSizeConstraints.removeAll()
136 | }
137 | #endif
138 | }
139 |
140 | super.updateViewConstraints()
141 | }
142 | #endif
143 | }
144 |
145 | extension ImagePreviewViewController {
146 | class func instantiateFromStoryboard() -> ImagePreviewViewController {
147 | let vc = NSStoryboard.lantern_contentPreviewStoryboard.instantiateController(withIdentifier: "Image View Controller") as! ImagePreviewViewController
148 | _ = vc.view // Stupid NSViewController
149 | return vc
150 | }
151 | }
152 |
153 | extension ImagePreviewViewController {
154 | @IBAction func copyImage(_ sender: AnyObject?) {
155 | performCopyImage()
156 | }
157 |
158 | private func performCopyImage() {
159 | if
160 | let imageData = imageData,
161 | let MIMEType = MIMEType, MIMEType != "",
162 | let UTIs = UTTypeCreateAllIdentifiersForTag(kUTTagClassMIMEType, MIMEType as CFString, kUTTypeImage)?.takeRetainedValue() as? [String]//,
163 | //let pasteboardType = UTTypeCopyPreferredTagWithClass(preferredUTI, kUTTagClassNSPboardType).takeRetainedValue()
164 | {
165 | let pasteboard = NSPasteboard.general
166 | pasteboard.clearContents()
167 |
168 | pasteboard.declareTypes(convertToNSPasteboardPasteboardTypeArray(UTIs), owner: nil)
169 | for UTI in UTIs {
170 | pasteboard.setData(imageData, forType: convertToNSPasteboardPasteboardType(UTI))
171 | }
172 | }
173 | }
174 |
175 | override var acceptsFirstResponder: Bool {
176 | return true
177 | }
178 |
179 | override func cancelOperation(_ sender: Any?) {
180 | dismiss(sender)
181 | }
182 |
183 | override func keyDown(with theEvent: NSEvent) {
184 | if theEvent.burnt_isSpaceKey {
185 | // Just like QuickLook, use space to dismiss.
186 | dismiss(nil)
187 | }
188 | }
189 | }
190 |
191 | extension ImagePreviewViewController: NSPopoverDelegate {
192 | func popoverDidShow(_ notification: Notification) {
193 | if let window = view.window {
194 | window.makeFirstResponder(self)
195 | }
196 |
197 | view.layoutSubtreeIfNeeded()
198 | }
199 |
200 | func popoverShouldDetach(_ popover: NSPopover) -> Bool {
201 | return true
202 | }
203 | }
204 |
205 |
206 | class ImagePreviewInnerViewController: NSViewController {
207 | @IBOutlet var imageView: IKImageView!
208 |
209 | var imagePropertiesDidLoad: ((_ imageProperties: [NSString: AnyObject]) -> ())?
210 | var wantsToDismiss: (() -> ())?
211 |
212 | var backgroundQueue: DispatchQueue!
213 |
214 | override func viewDidLoad() {
215 | super.viewDidLoad()
216 |
217 | backgroundQueue = DispatchQueue(label: "com.burntcaramel.ImagePreviewViewController.background", attributes: [])
218 | }
219 |
220 | var coreGraphicsImageSource: CGImageSource!
221 | var viewedCoreGraphicsImage: CGImage?
222 |
223 | fileprivate func updateUIWithImage(_ image: CGImage, imageProperties: [NSString: AnyObject]?) {
224 | let view = self.view
225 |
226 | let imageView = self.imageView!
227 | imageView.setImage(viewedCoreGraphicsImage, imageProperties: imageProperties)
228 | //imageView.zoomImageToActualSize(nil)
229 |
230 | var imageSize = imageView.imageSize()
231 | #if DEBUG
232 | print("imageSize \(imageSize)")
233 | #endif
234 |
235 | if var screenSize = view.window?.screen?.visibleFrame.size {
236 | #if DEBUG
237 | print("screenSize \(screenSize)")
238 | #endif
239 | screenSize.width -= 13.0 * 2.0 + 8.0
240 | screenSize.height -= 13.0 * 2.0 + 32.0
241 | //screenSize.height -= 32.0
242 |
243 | imageSize.width = min(imageSize.width, screenSize.width)
244 | imageSize.height = min(imageSize.height, screenSize.height)
245 | }
246 |
247 | //imageSize.width -= 13.0 * 2.0 + 8.0
248 | //imageSize.height -= 32.0
249 |
250 | #if DEBUG
251 | print("preferredContentSize \(imageSize)")
252 | #endif
253 |
254 | imageView.autoresizes = false
255 | preferredContentSize = imageSize
256 |
257 | if let imageProperties = imageProperties {
258 | imagePropertiesDidLoad?(imageProperties)
259 | }
260 |
261 | view.needsUpdateConstraints = true
262 |
263 | view.layoutSubtreeIfNeeded()
264 |
265 | //imageView.zoomImageToFit(nil)
266 | imageView.zoomImageToActualSize(nil)
267 | //view.superview
268 | }
269 |
270 | var imageData: Data? {
271 | didSet {
272 | _ = self.view // Stupid NSViewController
273 |
274 | if let imageData = self.imageData {
275 | backgroundQueue.async {
276 | guard let coreGraphicsImageSource = CGImageSourceCreateWithData(imageData as CFData, nil) else { return }
277 |
278 | let viewedCoreGraphicsImage = CGImageSourceCreateImageAtIndex(coreGraphicsImageSource, 0, nil)
279 | let imageProperties = CGImageSourceCopyPropertiesAtIndex(coreGraphicsImageSource, 0, nil) as? [NSString: AnyObject]
280 |
281 | if let viewedCoreGraphicsImage = viewedCoreGraphicsImage {
282 | DispatchQueue.main.async { [weak self] in
283 | self?.updateUIWithImage(viewedCoreGraphicsImage, imageProperties: imageProperties)
284 | }
285 | }
286 |
287 | self.viewedCoreGraphicsImage = viewedCoreGraphicsImage
288 | self.coreGraphicsImageSource = coreGraphicsImageSource
289 | }
290 | }
291 | }
292 | }
293 |
294 | @IBAction func clickedImageView(_ sender: AnyObject?) {
295 | wantsToDismiss?()
296 | }
297 |
298 |
299 | #if false
300 | func createMenu() {
301 | let menu = NSMenu(title: "Values Menu")
302 |
303 | let copyImageItem = menu.addItemWithTitle("Copy Image", action: "copyImage:", keyEquivalent: "")!
304 | copyImageItem.target = self
305 |
306 | imageView.menu = menu
307 | }
308 | #endif
309 | }
310 |
311 |
312 | // Helper function inserted by Swift 4.2 migrator.
313 | fileprivate func convertToNSPasteboardPasteboardTypeArray(_ input: [String]) -> [NSPasteboard.PasteboardType] {
314 | return input.map { key in NSPasteboard.PasteboardType(key) }
315 | }
316 |
317 | // Helper function inserted by Swift 4.2 migrator.
318 | fileprivate func convertToNSPasteboardPasteboardType(_ input: String) -> NSPasteboard.PasteboardType {
319 | return NSPasteboard.PasteboardType(rawValue: input)
320 | }
321 |
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/128x128.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/16x16.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/256x256-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/256x256-1.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/256x256.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/32x32-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/32x32-1.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/32x32.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/32x32@2x.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/512x512-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/512x512-1.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/512x512.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RoyalIcing/Lantern/2d1a2c06db7b20c49e42afa723151de04cb35b49/Lantern/Images.xcassets/AppIcon.appiconset/512x512@2x.png
--------------------------------------------------------------------------------
/Lantern/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "16x16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "32x32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "32x32-1.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "32x32@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "128x128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "256x256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "256x256-1.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "512x512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "512x512-1.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "512x512@2x.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Lantern/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSApplicationCategoryType
26 | public.app-category.developer-tools
27 | LSMinimumSystemVersion
28 | $(MACOSX_DEPLOYMENT_TARGET)
29 | NSAppTransportSecurity
30 |
31 | NSAllowsArbitraryLoads
32 |
33 |
34 | NSApplicationCrashOnExceptions
35 |
36 | NSHumanReadableCopyright
37 | Copyright © 2021 Patrick George Wyndham Smith. All rights reserved.
38 | NSMainStoryboardFile
39 | Main
40 | NSPrincipalClass
41 | NSApplication
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Lantern/Lantern-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "NSViewController+PGWSConstraintConvenience.h"
6 |
--------------------------------------------------------------------------------
/Lantern/Lantern.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.icloud-container-identifiers
6 |
7 | com.apple.security.app-sandbox
8 |
9 | com.apple.security.files.user-selected.read-write
10 |
11 | com.apple.security.network.client
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Lantern/MainSection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainSection.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 24/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | enum MainSection: String {
13 | case Preview = "preview"
14 | case Audience = "audience"
15 | case Audit = "audit"
16 | }
17 |
--------------------------------------------------------------------------------
/Lantern/MainState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainState.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 31/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import LanternModel
11 |
12 |
13 | enum SiteChoice {
14 | case savedSite(SiteValues)
15 | case custom
16 | }
17 |
18 | extension SiteChoice: Equatable {}
19 |
20 | func ==(lhs: SiteChoice, rhs: SiteChoice) -> Bool {
21 | switch (lhs, rhs) {
22 | case (.custom, .custom):
23 | return true
24 | case (.savedSite(let lSite), .savedSite(let rSite)):
25 | return lSite.UUID == rSite.UUID
26 | default:
27 | return false
28 | }
29 | }
30 |
31 |
32 | class MainState {
33 | let crawlerPreferences = CrawlerPreferences.shared
34 | let browserPreferences = BrowserPreferences.shared
35 |
36 | var startURL: URL? {
37 | didSet {
38 | print("startURL changing \(startURL)")
39 | if startURL == oldValue { return }
40 | mainQueue_notify(Self.startURLDidChangeNotification)
41 | }
42 | }
43 | // var currentURL: URL?
44 |
45 | var siteChoice = SiteChoice.custom {
46 | didSet {
47 | if siteChoice == oldValue { return }
48 | mainQueue_notify(Self.chosenSiteDidChangeNotification)
49 | }
50 | }
51 |
52 | var chosenSite: SiteValues? {
53 | switch siteChoice {
54 | case .savedSite(let site): return site
55 | default: return nil
56 | }
57 | }
58 |
59 | var initialHost: String?
60 |
61 | static let startURLDidChangeNotification = Notification.Name.init("LanternModel.MainState.startURLDidChangeNotification")
62 |
63 | static let chosenSiteDidChangeNotification = Notification.Name.init("LanternModel.MainState.ChosenSiteDidChangeNotification")
64 |
65 | private func mainQueue_notify(_ name: Notification.Name, userInfo: [String: AnyObject]? = nil) {
66 | let nc = NotificationCenter.default
67 | nc.post(name: name, object: self, userInfo: userInfo)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Lantern/MainView.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Lantern/MetaViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MetaViewController.swift
3 | // Lantern
4 | //
5 | // Created by Patrick Smith on 1/11/16.
6 | // Copyright © 2016 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | //import LanternModel
11 | //import Ono
12 |
13 |
14 | class MetaViewController : NSViewController {
15 | //@IBOutlet var stackView: NSStackView!
16 | @IBOutlet var tableView: NSTableView!
17 |
18 | var crawlerProviderListenerUUID = UUID()
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | }
23 |
24 | override func viewDidAppear() {
25 | super.viewDidAppear()
26 |
27 | tableView.dataSource = self
28 | tableView.delegate = self
29 |
30 | if let provider = pageMapperProvider {
31 | let crawlerProviderListenerUUID = self.crawlerProviderListenerUUID
32 |
33 | provider[activeURLChangedCallback: crawlerProviderListenerUUID] = { [weak self] url in
34 | self?.updateUI(url: url)
35 | }
36 |
37 | provider[pageMapperCreatedCallback: crawlerProviderListenerUUID] = { [weak self] pageMapper in
38 | pageMapper[didUpdateCallback: crawlerProviderListenerUUID] = { [weak self] url in
39 | if url == provider.activeURL {
40 | self?.updateUI(url: url)
41 | }
42 | }
43 | }
44 |
45 | updateUI(url: provider.activeURL)
46 | }
47 | }
48 |
49 | override func viewWillDisappear() {
50 | super.viewWillDisappear()
51 |
52 | guard let provider = pageMapperProvider else { return }
53 |
54 | provider[activeURLChangedCallback: crawlerProviderListenerUUID] = nil
55 | }
56 |
57 | /*func createStackViews(_ url: URL?) -> [NSView] {
58 | guard let url = url else {
59 | return []
60 | }
61 |
62 | if let crawler = pageMapperProvider?.pageMapper {
63 | if
64 | let resourceInfo = crawler.pageInfoForRequestedURL(url),
65 | let contentInfo = resourceInfo.contentInfo
66 | {
67 | let metaTagFields: [NSTextField] = contentInfo.metaElementAttributes.map { attributes in
68 | print(attributes)
69 | let textField = NSTextField(string: "\(attributes)")
70 | textField.translatesAutoresizingMaskIntoConstraints = false
71 | textField.setContentCompressionResistancePriority(NSLayoutPriorityDefaultHigh, for: .horizontal)
72 | return textField
73 | }
74 |
75 | print("metaTagFields \(metaTagFields)")
76 |
77 | return Array([
78 | metaTagFields
79 | ].joined())
80 | }
81 | }
82 |
83 | return [
84 | NSTextField(labelWithString: "Loading…"),
85 | ]
86 | }*/
87 |
88 | func findMetaElementAttributes(url: URL) -> [[String : String]]? {
89 | guard
90 | let crawler = pageMapperProvider?.pageMapper,
91 | let resourceInfo = crawler.pageInfoForRequestedURL(url),
92 | let contentInfo = resourceInfo.contentInfo
93 | else { return nil }
94 |
95 | return contentInfo.metaElementAttributes
96 | }
97 |
98 | var metaElementAttributes: [[String : String]] = []
99 |
100 | func updateUI(url: URL?) {
101 | metaElementAttributes = url.flatMap{ findMetaElementAttributes(url: $0) } ?? []
102 | tableView.reloadData()
103 | //stackView.setViews(createStackViews(url), in: NSStackViewGravity.center)
104 | }
105 | }
106 |
107 | extension MetaViewController : NSTableViewDataSource, NSTableViewDelegate {
108 | func numberOfRows(in tableView: NSTableView) -> Int {
109 | return metaElementAttributes.count
110 | }
111 |
112 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
113 | let attributes = metaElementAttributes[row]
114 | let identifier = convertFromNSUserInterfaceItemIdentifier(tableColumn!.identifier)
115 | let view = tableView.makeView(withIdentifier: convertToNSUserInterfaceItemIdentifier(identifier), owner: self) as! NSTableCellView
116 |
117 | var property = attributes["name"] ?? attributes["property"] ?? attributes["http-equiv"]
118 | var contentAttributeKey = "content"
119 | if property == nil && attributes["charset"] != nil {
120 | property = "charset"
121 | contentAttributeKey = "charset"
122 | }
123 |
124 | switch identifier {
125 | case "property":
126 | view.textField?.stringValue = property ?? attributes.keys.filter{ $0 != "content" }.joined(separator: " ")
127 | case "value":
128 | view.textField?.stringValue = attributes[contentAttributeKey] ?? "?"
129 | default:
130 | break
131 | }
132 |
133 | return view
134 | }
135 | }
136 |
137 | // Helper function inserted by Swift 4.2 migrator.
138 | fileprivate func convertFromNSUserInterfaceItemIdentifier(_ input: NSUserInterfaceItemIdentifier) -> String {
139 | return input.rawValue
140 | }
141 |
142 | // Helper function inserted by Swift 4.2 migrator.
143 | fileprivate func convertToNSUserInterfaceItemIdentifier(_ input: String) -> NSUserInterfaceItemIdentifier {
144 | return NSUserInterfaceItemIdentifier(rawValue: input)
145 | }
146 |
--------------------------------------------------------------------------------
/Lantern/MultipleStringPreviewViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultipleStringPreviewViewController.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 3/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import LanternModel
11 |
12 |
13 | class MultipleStringPreviewViewController: NSViewController {
14 | @IBOutlet var tableView: NSTableView!
15 | var measuringTableCellView: MultipleStringPreviewTableCellView!
16 |
17 | var itemMenu: NSMenu!
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 |
22 | tableView.tableColumns[0].minWidth = 100
23 |
24 | tableView.dataSource = self
25 | tableView.delegate = self
26 |
27 | measuringTableCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "stringValue"), owner: self) as! MultipleStringPreviewTableCellView
28 |
29 | view.appearance = NSAppearance(named: NSAppearance.Name.aqua)
30 | }
31 |
32 | func createRowMenu() {
33 | // Row Menu
34 | itemMenu = NSMenu(title: "Values Menu")
35 |
36 | let copyValueItem = itemMenu.addItem(withTitle: "Copy Value", action: #selector(MultipleStringPreviewViewController.copyValueForSelectedRow(_:)), keyEquivalent: "")
37 | copyValueItem.target = self
38 | }
39 |
40 | var validatedStringValues: [ValidatedStringValue] = [] {
41 | didSet {
42 | reloadValues()
43 | }
44 | }
45 |
46 | func reloadValues() {
47 | // Stupid NSViewController
48 | _ = self.view
49 |
50 | tableView.reloadData()
51 | }
52 |
53 | override func keyDown(with theEvent: NSEvent) {
54 | if theEvent.burnt_isSpaceKey {
55 | // Just like QuickLook, use space to dismiss.
56 | dismiss(nil)
57 | }
58 | }
59 | }
60 |
61 | extension MultipleStringPreviewViewController {
62 | class func instantiateFromStoryboard() -> MultipleStringPreviewViewController {
63 | return NSStoryboard.lantern_contentPreviewStoryboard.instantiateController(withIdentifier: "String Value View Controller") as! MultipleStringPreviewViewController
64 | }
65 | }
66 |
67 | extension MultipleStringPreviewViewController {
68 | @IBAction func copyValueForSelectedRow(_ menuItem: NSMenuItem) {
69 | let row = tableView.clickedRow
70 | if row != -1 {
71 | performCopyValueForItemAtRow(row)
72 | }
73 | }
74 |
75 | func performCopyValueForItemAtRow(_ row: Int) {
76 | switch validatedStringValues[row] {
77 | case .validString(let stringValue):
78 | let pasteboard = NSPasteboard.general
79 | pasteboard.clearContents()
80 |
81 | pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil)
82 | pasteboard.setString(stringValue, forType: NSPasteboard.PasteboardType.string)
83 | default:
84 | break
85 | }
86 | }
87 | }
88 |
89 | extension MultipleStringPreviewViewController: NSTableViewDataSource, NSTableViewDelegate {
90 | func numberOfRows(in tableView: NSTableView) -> Int {
91 | return validatedStringValues.count
92 | }
93 |
94 | func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
95 | switch validatedStringValues[row] {
96 | case .validString(let stringValue):
97 | return stringValue
98 | default:
99 | break
100 | }
101 |
102 | return nil
103 | }
104 |
105 | func setUpTableCellView(_ view: MultipleStringPreviewTableCellView, tableColumn: NSTableColumn?, row: Int, visualsAndInteraction: Bool = true) {
106 | let validatedStringValue = validatedStringValues[row]
107 |
108 | let textField = view.textField!
109 | textField.stringValue = validatedStringValue.stringValueForPresentation
110 |
111 | var presentedIndex: String {
112 | switch validatedStringValue {
113 | case .validKeyValue(let key, _): return key
114 | default: return String(row + 1) // 1-based index
115 | }
116 | }
117 |
118 | let indexField = view.indexField!
119 | indexField.stringValue = presentedIndex
120 | indexField.sizeToFit()
121 |
122 | if visualsAndInteraction {
123 | let textColor = NSColor.textColor
124 |
125 | indexField.textColor = textColor.withAlphaComponent(0.3)
126 |
127 | textField.textColor = textColor.withAlphaComponent(validatedStringValue.alphaValueForPresentation)
128 |
129 | view.menu = itemMenu
130 | }
131 | }
132 |
133 | func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
134 | let cellView = measuringTableCellView
135 |
136 | setUpTableCellView(cellView!, tableColumn: nil, row: row, visualsAndInteraction: false)
137 |
138 | let tableColumn = tableView.tableColumns[0]
139 | let cellWidth = tableColumn.width
140 | cellView?.setFrameSize(NSSize(width: cellWidth, height: 100.0))
141 | cellView?.layoutSubtreeIfNeeded()
142 |
143 | let textField = cellView?.textField!
144 | textField?.preferredMaxLayoutWidth = (textField?.bounds.width)!
145 |
146 | let extraPadding: CGFloat = 5.0 + 5.0
147 |
148 | let height = (textField?.intrinsicContentSize.height)! + extraPadding
149 |
150 | return height
151 | }
152 |
153 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
154 | if let view = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "stringValue"), owner: self) as? MultipleStringPreviewTableCellView {
155 | setUpTableCellView(view, tableColumn: tableColumn, row: row)
156 |
157 | return view
158 | }
159 |
160 | return nil
161 | }
162 |
163 | func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
164 | return false
165 | }
166 | }
167 |
168 | extension MultipleStringPreviewViewController: NSPopoverDelegate {
169 | func popoverWillShow(_ notification: Notification) {
170 | //let popover = notification.object as! NSPopover
171 | //popover.appearance = NSAppearance(named: NSAppearanceNameVibrantDark)
172 | //popover.appearance = NSAppearance(named: NSAppearanceNameLightContent)
173 | //popover.appearance = .HUD
174 | }
175 | }
176 |
177 |
178 | class MultipleStringPreviewTableCellView: NSTableCellView {
179 | @IBOutlet var indexField: NSTextField!
180 | }
181 |
--------------------------------------------------------------------------------
/Lantern/NSEvent+extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSViewController+extension.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 4/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 |
12 | extension NSEvent {
13 | var burnt_firstCharacter: Character? {
14 | if let charactersIgnoringModifiers = charactersIgnoringModifiers {
15 | return charactersIgnoringModifiers[charactersIgnoringModifiers.startIndex]
16 | }
17 |
18 | return nil
19 | }
20 |
21 | var burnt_isSpaceKey: Bool {
22 | return burnt_firstCharacter == " "
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Lantern/NSViewController+PGWSConstraintConvenience.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSViewController+PGWSConstraintConvenience.h
3 | // BurntIcing
4 | //
5 | // Created by Patrick Smith on 28/02/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | @import Cocoa;
10 |
11 |
12 | @interface NSViewController (PGWSConstraintConvenience)
13 |
14 | - (NSLayoutConstraint *)layoutConstraintWithIdentifier:(NSString *)constraintIdentifier;
15 |
16 | + (NSString *)layoutConstraintIdentifierWithBaseIdentifier:(NSString *)baseIdentifier forChildView:(NSView *)innerView;
17 | - (NSLayoutConstraint *)layoutConstraintWithIdentifier:(NSString *)baseIdentifier forChildView:(NSView *)innerView;
18 |
19 | #pragma mark -
20 |
21 | - (NSLayoutConstraint *)addLayoutConstraintToMatchAttribute:(NSLayoutAttribute)attribute withChildView:(NSView *)innerView identifier:(NSString *)identifier priority:(NSLayoutPriority)priority;
22 |
23 | - (NSLayoutConstraint *)addLayoutConstraintToMatchAttribute:(NSLayoutAttribute)attribute withChildView:(NSView *)innerView identifier:(NSString *)identifier;
24 |
25 | - (void)fillViewWithChildView:(NSView *)innerView;
26 | - (void)fillWithChildViewController:(NSViewController *)childViewController;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/Lantern/NSViewController+PGWSConstraintConvenience.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSViewController+PGWSConstraintConvenience.m
3 | // BurntIcing
4 | //
5 | // Created by Patrick Smith on 28/02/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | #import "NSViewController+PGWSConstraintConvenience.h"
10 |
11 |
12 | @implementation NSViewController (PGWSConstraintConvenience)
13 |
14 | - (NSLayoutConstraint *)layoutConstraintWithIdentifier:(NSString *)constraintIdentifier
15 | {
16 | NSArray *leadingConstraintInArray = [(self.view.constraints) filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"identifier = %@", constraintIdentifier]];
17 |
18 | if (leadingConstraintInArray.count == 0) {
19 | return nil;
20 | }
21 | else {
22 | return leadingConstraintInArray[0];
23 | }
24 | }
25 |
26 | + (NSString *)layoutConstraintIdentifierWithBaseIdentifier:(NSString *)baseIdentifier forChildView:(NSView *)innerView
27 | {
28 | return [NSString stringWithFormat:@"%@--%@", (innerView.identifier), baseIdentifier];
29 | }
30 |
31 | - (NSLayoutConstraint *)layoutConstraintWithIdentifier:(NSString *)baseIdentifier forChildView:(NSView *)innerView
32 | {
33 | if (!innerView) {
34 | return nil;
35 | }
36 |
37 | NSString *constraintIdentifier = [(self.class) layoutConstraintIdentifierWithBaseIdentifier:baseIdentifier forChildView:innerView];
38 | return [self layoutConstraintWithIdentifier:constraintIdentifier];
39 | }
40 |
41 | #pragma mark -
42 |
43 | - (NSLayoutConstraint *)addLayoutConstraintToMatchAttribute:(NSLayoutAttribute)attribute withChildView:(NSView *)innerView identifier:(NSString *)identifier priority:(NSLayoutPriority)priority
44 | {
45 | NSParameterAssert(innerView != nil);
46 | NSParameterAssert(identifier != nil);
47 |
48 | NSView *holderView = (self.view);
49 |
50 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:innerView attribute:attribute relatedBy:NSLayoutRelationEqual toItem:holderView attribute:attribute multiplier:1.0 constant:0.0];
51 |
52 | (constraint.identifier) = [(self.class) layoutConstraintIdentifierWithBaseIdentifier:identifier forChildView:innerView];
53 | (constraint.priority) = priority;
54 |
55 | [holderView addConstraint:constraint];
56 |
57 | return constraint;
58 | }
59 |
60 | - (NSLayoutConstraint *)addLayoutConstraintToMatchAttribute:(NSLayoutAttribute)attribute withChildView:(NSView *)innerView identifier:(NSString *)identifier
61 | {
62 | return [self addLayoutConstraintToMatchAttribute:attribute withChildView:innerView identifier:identifier priority:NSLayoutPriorityRequired];
63 | }
64 |
65 | - (void)fillViewWithChildView:(NSView *)innerView
66 | {
67 | NSParameterAssert(innerView != nil);
68 |
69 | if (!(innerView.identifier)) {
70 | NSUUID *UUID = [NSUUID UUID];
71 | (innerView.identifier) = [NSString stringWithFormat:@"(%@)", (UUID.UUIDString)];
72 | }
73 |
74 | [(self.view) addSubview:innerView];
75 |
76 | // Interface Builder's default is to have this on for new view controllers in 10.9 for some reason.
77 | // I have disabled it where I remember to in the xib file, but no harm in just setting it off here too.
78 | (innerView.translatesAutoresizingMaskIntoConstraints) = NO;
79 |
80 | // By setting width and height constraints, we can move the view around whilst keeping it the same size.
81 | [self addLayoutConstraintToMatchAttribute:NSLayoutAttributeWidth withChildView:innerView identifier:@"width"];
82 | [self addLayoutConstraintToMatchAttribute:NSLayoutAttributeHeight withChildView:innerView identifier:@"height"];
83 | [self addLayoutConstraintToMatchAttribute:NSLayoutAttributeLeading withChildView:innerView identifier:@"leading"];
84 | [self addLayoutConstraintToMatchAttribute:NSLayoutAttributeTop withChildView:innerView identifier:@"top"];
85 | }
86 |
87 | - (void)fillWithChildViewController:(NSViewController *)childViewController
88 | {
89 | NSParameterAssert(childViewController != nil);
90 |
91 | [self addChildViewController:childViewController];
92 | [self fillViewWithChildView:(childViewController.view)];
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/Lantern/PageMapper+UI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageMapper+UI.swift
3 | // Lantern
4 | //
5 | // Created by Patrick Smith on 1/11/16.
6 | // Copyright © 2016 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import LanternModel
11 |
12 |
13 | protocol PageMapperProvider : class {
14 | var pageMapper: PageMapper? { get }
15 |
16 | func clearPageMapper()
17 | func createPageMapper(primaryURL: URL) -> PageMapper?
18 | subscript(pageMapperCreatedCallback uuid: UUID) -> ((PageMapper) -> ())? { get set }
19 |
20 | var activeURL: URL? { get set }
21 | subscript(activeURLChangedCallback uuid: UUID) -> ((URL?) -> ())? { get set }
22 | }
23 |
24 | extension NSResponder {
25 | var pageMapperProvider: PageMapperProvider? {
26 | return sequence(first: self, next: { $0.nextResponder }).reduce(PageMapperProvider?.none) {
27 | $0 ?? ($1 as? PageMapperProvider)
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Lantern/SiteMenuItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SiteMenuItem.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 1/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import LanternModel
11 | import BurntCocoaUI
12 |
13 |
14 | enum SiteMenuItem {
15 | case choice(SiteChoice)
16 | case loadingSavedSites
17 | case noSavedSitesYet
18 | }
19 |
20 | extension SiteMenuItem: UIChoiceRepresentative {
21 | var title: String {
22 | switch self {
23 | case .choice(let siteChoice):
24 | switch siteChoice {
25 | case .savedSite(let site):
26 | return site.name
27 | case .custom:
28 | return "Custom"
29 | }
30 | case .loadingSavedSites:
31 | return "(Loading Saved Sites)"
32 | case .noSavedSitesYet:
33 | return "(No Saved Sites Yet)"
34 | }
35 | }
36 |
37 | var tag: Int? {
38 | return nil
39 | }
40 |
41 | typealias UniqueIdentifier = String
42 |
43 | var uniqueIdentifier: UniqueIdentifier {
44 | switch self {
45 | case .choice(let siteChoice):
46 | switch siteChoice {
47 | case .savedSite(let site):
48 | let url = site.homePageURL
49 | guard var urlComponents = URLComponents(url: site.homePageURL, resolvingAgainstBaseURL: true) else {
50 | return url.absoluteString
51 | }
52 | urlComponents.fragment = nil
53 | return urlComponents.string ?? url.absoluteString
54 | case .custom:
55 | return "Custom"
56 | }
57 | case .loadingSavedSites:
58 | return "LoadingSavedSites"
59 | case .noSavedSitesYet:
60 | return "NoSavedSitesYet"
61 | }
62 | }
63 | }
64 |
65 | extension SiteMenuItem: CustomDebugStringConvertible {
66 | var debugDescription: String {
67 | switch self {
68 | case .choice(let siteChoice):
69 | switch siteChoice {
70 | case .savedSite(let site):
71 | return "\(site.name) \(site.UUID.uuidString)"
72 | default:
73 | break
74 | }
75 | default:
76 | break
77 | }
78 |
79 | return uniqueIdentifier
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Lantern/SiteSettingsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SiteSettingsViewController.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 30/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import LanternModel
11 |
12 |
13 | class SiteSettingsViewController: NSViewController {
14 |
15 | var modelManager: ModelManager!
16 | var mainState: MainState!
17 |
18 | // MARK: Outlets
19 | @IBOutlet var homePageURLField: NSTextField!
20 | @IBOutlet var nameField: NSTextField!
21 | @IBOutlet var saveInFavoritesButton: NSButton! {
22 | didSet {
23 | saveInFavoritesButton.target = self
24 | saveInFavoritesButton.action = #selector(toggleSaveInFavorites(_:))
25 | }
26 | }
27 |
28 | // MARK: Callbacks
29 | var favoriteNameForURL: ((_ url: URL) -> String?)?
30 | var onSaveSite: ((_ viewController: SiteSettingsViewController) -> ())?
31 |
32 | // MARK: Init
33 | override func viewDidLoad() {
34 | super.viewDidLoad()
35 |
36 | homePageURLField.delegate = self
37 | }
38 |
39 | func prepareForReuse() {
40 | homePageURLField.stringValue = ""
41 | nameField.stringValue = ""
42 | }
43 |
44 | // MARK: -
45 |
46 | var editingFavorite = false
47 |
48 | func editFavorite(url: URL, name: String) {
49 | self.updateUI(url: url, favoriteName: name, editingFavorite: true)
50 | }
51 |
52 | func editVisited(url: URL) {
53 | self.updateUI(url: url)
54 | }
55 |
56 | func reset() {
57 | self.updateUI()
58 | }
59 |
60 | private func updateUI(url: URL? = nil, favoriteName: String? = nil, editingFavorite: Bool = false) {
61 | // Make sure view has loaded
62 | _ = self.view
63 |
64 | let urlString = url?.absoluteString ?? ""
65 | var name = ""
66 | self.editingFavorite = false
67 |
68 | if let favoriteName = favoriteName {
69 | name = favoriteName
70 | self.editingFavorite = editingFavorite
71 | }
72 |
73 | homePageURLField.stringValue = urlString
74 | saveInFavoritesButton.state = editingFavorite ? NSControl.StateValue.on : NSControl.StateValue.off
75 | nameField.isEnabled = editingFavorite
76 | nameField.stringValue = name
77 | }
78 |
79 | struct Output {
80 | var siteValues: SiteValues
81 | var saveInFavorites: Bool
82 | }
83 |
84 | func read(siteUUID: UUID? = nil) throws -> Output? {
85 | // Make sure view has loaded
86 | _ = self.view
87 |
88 | let saveInFavorites = saveInFavoritesButton.state == NSControl.StateValue.on
89 |
90 | let name: String
91 | if saveInFavorites {
92 | name = try ValidationError.validateString(nameField.stringValue, identifier: "Name")
93 | }
94 | else {
95 | name = ""
96 | }
97 |
98 | let url: URL
99 | do {
100 | url = try ValidationError.validate(urlString: homePageURLField.stringValue, identifier: "Primary URL")
101 | }
102 | catch let error as ValidationError {
103 | switch (saveInFavorites, error) {
104 | case (false, ValidationError.stringIsEmpty):
105 | return nil
106 | default:
107 | throw error
108 | }
109 | }
110 |
111 | let siteValues = SiteValues(name: name, homePageURL: url, UUID: siteUUID ?? UUID())
112 |
113 | return Output(siteValues: siteValues, saveInFavorites: saveInFavorites)
114 | }
115 |
116 | // MARK: - Actions
117 |
118 | @IBAction func createSite(_ sender: NSButton) {
119 | onSaveSite?(self)
120 | }
121 |
122 | @IBAction func toggleSaveInFavorites(_ sender: NSButton) {
123 | let hasFavorite = sender.state == NSControl.StateValue.on
124 | nameField.isEnabled = hasFavorite
125 | }
126 | }
127 |
128 | // MARK: -
129 |
130 | extension SiteSettingsViewController : NSTextFieldDelegate { // MARK: NSTextFieldDelegate
131 | func controlTextDidChange(_ notification: Notification) {
132 | guard !editingFavorite else {
133 | return
134 | }
135 |
136 | guard homePageURLField === (notification.object as! NSTextField) else {
137 | return
138 | }
139 |
140 | guard let url = try? ValidationError.validate(urlString: homePageURLField.stringValue, identifier: "Primary URL") else {
141 | return
142 | }
143 |
144 | guard url.absoluteString == homePageURLField.stringValue else {
145 | return
146 | }
147 |
148 | guard let favoriteNameForURL = self.favoriteNameForURL else {
149 | return
150 | }
151 |
152 | if let name = favoriteNameForURL(url) {
153 | self.updateUI(url: url, favoriteName: name)
154 | } else {
155 | self.updateUI(url: url)
156 | }
157 | }
158 | }
159 |
160 | extension SiteSettingsViewController : NSPopoverDelegate { // MARK: NSPopoverDelegate
161 | func popoverWillClose(_ notification: Notification) {
162 | onSaveSite?(self)
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/Lantern/SourcePreviewViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SourcePreviewViewController.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 27/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import LanternModel
11 |
12 |
13 | enum SourcePreviewTabItemSection: String {
14 | case Main = "Main"
15 | case HTMLHead = "HTMLHead"
16 | case HTMLBody = "HTMLBody"
17 |
18 | var stringValue: String { return rawValue }
19 |
20 | func titleWithPageInfo(_ pageInfo: PageInfo) -> String {
21 | switch self {
22 | case .Main:
23 | if let MIMEType = pageInfo.MIMEType {
24 | return MIMEType.stringValue
25 | }
26 | else {
27 | return "Main"
28 | }
29 | case .HTMLHead:
30 | return ""
31 | case .HTMLBody:
32 | return ""
33 | }
34 | }
35 | }
36 |
37 |
38 | class SourcePreviewTabViewController: NSTabViewController {
39 | override func viewDidLoad() {
40 | super.viewDidLoad()
41 |
42 | //tabView.delegate = self
43 | }
44 |
45 | var pageInfo: PageInfo! {
46 | didSet {
47 | switch pageInfo.baseContentType {
48 | case .localHTMLPage:
49 | updateForHTMLPreview()
50 | default:
51 | updateForGeneralPreview()
52 | }
53 | }
54 | }
55 |
56 | func updateWithSections(_ sections: [SourcePreviewTabItemSection]) {
57 | let tabViewItems = sections.map { section in
58 | self.newSourcePreviewTabViewItem(section)
59 | }
60 |
61 | // This crashes for some reason
62 | // self.tabViewItems = tabViewItems
63 |
64 | let existingItems = self.tabViewItems
65 | for existingItem in existingItems {
66 | removeTabViewItem(existingItem)
67 | }
68 | for tabViewItem in tabViewItems {
69 | addTabViewItem(tabViewItem)
70 | }
71 |
72 | //updateSourceTextForSection(sections[0], tabViewItem: tabViewItems[0])
73 | }
74 |
75 | func updateForGeneralPreview() {
76 | updateWithSections([.Main])
77 | }
78 |
79 | func updateForHTMLPreview() {
80 | updateWithSections([.HTMLHead, .HTMLBody])
81 | }
82 |
83 | func newSourcePreviewTabViewItem(_ section: SourcePreviewTabItemSection) -> NSTabViewItem {
84 | let item = NSTabViewItem(identifier: section.stringValue)
85 |
86 | let vc = newSourcePreviewController()
87 | vc.wantsToDismiss = {
88 | self.dismiss(nil)
89 | }
90 | item.viewController = vc
91 |
92 | item.label = section.titleWithPageInfo(pageInfo)
93 | return item
94 | }
95 |
96 | func newSourcePreviewController() -> SourcePreviewViewController {
97 | return NSStoryboard.lantern_contentPreviewStoryboard.instantiateController(withIdentifier: "Source Preview View Controller") as! SourcePreviewViewController
98 | }
99 |
100 | func updateSourceTextForSection(_ section: SourcePreviewTabItemSection, tabViewItem: NSTabViewItem) {
101 | let vc = tabViewItem.viewController as! SourcePreviewViewController
102 |
103 | if let contentInfo = self.pageInfo.contentInfo {
104 | switch section {
105 | case .Main:
106 | setSourceText(contentInfo.stringContent, forSourcePreviewViewController: vc)
107 | case .HTMLHead:
108 | setSourceText(contentInfo.HTMLHeadStringContent, forSourcePreviewViewController: vc)
109 | case .HTMLBody:
110 | setSourceText(contentInfo.HTMLBodyStringContent, forSourcePreviewViewController: vc)
111 | }
112 | }
113 | else {
114 | setSourceText("(none)", forSourcePreviewViewController: vc)
115 | }
116 | }
117 |
118 | func setSourceText(_ sourceText: String?, forSourcePreviewViewController vc: SourcePreviewViewController) {
119 | vc.sourceText = sourceText ?? "(None)"
120 | }
121 |
122 | override func tabView(_ tabView: NSTabView, willSelect tabViewItem: NSTabViewItem?) {
123 | if let
124 | tabViewItem = tabViewItem,
125 | let identifier = tabViewItem.identifier as? String,
126 | let section = SourcePreviewTabItemSection(rawValue: identifier)
127 | {
128 | updateSourceTextForSection(section, tabViewItem: tabViewItem)
129 | }
130 | }
131 |
132 | override func keyDown(with theEvent: NSEvent) {
133 | if theEvent.burnt_isSpaceKey {
134 | // Just like QuickLook, use space to dismiss.
135 | dismiss(nil)
136 | }
137 | }
138 | }
139 |
140 | extension SourcePreviewTabViewController: NSPopoverDelegate {
141 | func popoverWillShow(_ notification: Notification) {
142 | let popover = notification.object as! NSPopover
143 | popover.appearance = NSAppearance(named: NSAppearance.Name.vibrantDark)
144 | //popover.appearance = NSAppearance(named: NSAppearanceNameLightContent)
145 | //popover.appearance = .HUD
146 | }
147 |
148 | func popoverDidShow(_ notification: Notification) {
149 | if let window = view.window , selectedTabViewItemIndex != -1 {
150 | let tabViewItem = tabViewItems[selectedTabViewItemIndex]
151 | let vc = tabViewItem.viewController as! SourcePreviewViewController
152 | window.makeFirstResponder(vc.textView)
153 | }
154 |
155 | view.layoutSubtreeIfNeeded()
156 | }
157 | }
158 |
159 |
160 | class SourcePreviewViewController: NSViewController {
161 |
162 | @IBOutlet var textView: SourcePreviewTextView!
163 |
164 | var wantsToDismiss: (() -> ())?
165 |
166 | override func viewDidLoad() {
167 | super.viewDidLoad()
168 | // Do view setup here.
169 |
170 | textView.wantsToDismiss = wantsToDismiss
171 | }
172 |
173 | let defaultTextAttributes: [String: AnyObject] = [
174 | convertFromNSAttributedStringKey(NSAttributedString.Key.font): NSFont(name: "Menlo", size: 11.0)!,
175 | convertFromNSAttributedStringKey(NSAttributedString.Key.foregroundColor): NSColor.highlightColor
176 | ]
177 |
178 | var sourceText: String! {
179 | didSet {
180 | _ = self.view // Make sure view has loaded
181 |
182 | if let textStorage = textView.textStorage {
183 | let attributes = defaultTextAttributes
184 | let newAttributedString = NSAttributedString(string: sourceText, attributes:convertToOptionalNSAttributedStringKeyDictionary(attributes))
185 | textStorage.replaceCharacters(in: NSMakeRange(0, textStorage.length), with: newAttributedString)
186 | }
187 | }
188 | }
189 | }
190 |
191 |
192 | class SourcePreviewTextView: NSTextView {
193 |
194 | var wantsToDismiss: (() -> ())?
195 |
196 | override func keyDown(with theEvent: NSEvent) {
197 | if theEvent.burnt_isSpaceKey {
198 | wantsToDismiss?()
199 | }
200 | else {
201 | super.keyDown(with: theEvent)
202 | }
203 | }
204 | }
205 |
206 | // Helper function inserted by Swift 4.2 migrator.
207 | fileprivate func convertFromNSAttributedStringKey(_ input: NSAttributedString.Key) -> String {
208 | return input.rawValue
209 | }
210 |
211 | // Helper function inserted by Swift 4.2 migrator.
212 | fileprivate func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? {
213 | guard let input = input else { return nil }
214 | return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value)})
215 | }
216 |
--------------------------------------------------------------------------------
/Lantern/TableView-OutlineView-Subclass.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableCellView.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 4/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 |
12 | // An extension lets us both subclass NSTableView and NSOutlineView with the same functionality
13 | extension NSTableView {
14 | // Find a cell view, or a row view, that has a menu. (e.g. NSResponder’s menu: NSMenu?)
15 | func burnt_menuForEventFromCellOrRowViews(_ event: NSEvent) -> NSMenu? {
16 | let point = convert(event.locationInWindow, from: nil)
17 | let row = self.row(at: point)
18 | if row != -1 {
19 | if let rowView = rowView(atRow: row, makeIfNecessary: true) {
20 | let column = self.column(at: point)
21 | if column != -1 {
22 | if let cellView = rowView.view(atColumn: column) as? NSTableCellView {
23 | if let cellMenu = cellView.menu(for: event) {
24 | return cellMenu
25 | }
26 | }
27 | }
28 |
29 | if let rowMenu = rowView.menu(for: event) {
30 | return rowMenu
31 | }
32 | }
33 | }
34 |
35 | return nil
36 | }
37 | }
38 |
39 |
40 | class OutlineView: NSOutlineView {
41 | override func menu(for event: NSEvent) -> NSMenu? {
42 | // Because of weird NSTableView/NSOutlineView behaviour, must set receiver’s menu otherwise the target cannot be found
43 | self.menu = burnt_menuForEventFromCellOrRowViews(event)
44 |
45 | return super.menu(for: event)
46 | }
47 | }
48 |
49 | class TableView: NSTableView {
50 | override func menu(for event: NSEvent) -> NSMenu? {
51 | // Because of weird NSTableView/NSOutlineView behaviour, must set receiver’s menu otherwise the target cannot be found
52 | self.menu = burnt_menuForEventFromCellOrRowViews(event)
53 |
54 | return super.menu(for: event)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Lantern/ValidatedStringValue+Presentation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ValidatedStringValue+Presentation.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 3/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import LanternModel
11 |
12 |
13 | extension ValidatedStringValue {
14 | static var loadingAlphaValueForPresentation: CGFloat = 0.2
15 |
16 | var alphaValueForPresentation: CGFloat {
17 | switch self {
18 | case .validString:
19 | return 1.0
20 | default:
21 | return 0.3
22 | }
23 | }
24 |
25 | var stringValueForPresentation: String {
26 | switch self {
27 | case .validString(let stringValue):
28 | return stringValue
29 | case .validKeyValue(_, let value):
30 | return value
31 | case .notRequested:
32 | return "(not requested)"
33 | case .missing:
34 | return "(none)"
35 | case .empty:
36 | return "(empty)"
37 | case .multiple:
38 | return "(multiple)"
39 | case .invalid:
40 | return "(invalid)"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Lantern/ViewState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewState.swift
3 | // Lantern
4 | //
5 | // Created by Patrick Smith on 8/11/19.
6 | // Copyright © 2019 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | enum ToggleableViewIdentifier : String {
10 | case browser, meta
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/Lantern/WKUserContentController+bundledScripts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WKUserContentController+bundledScripts.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 22/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import WebKit
11 |
12 |
13 | extension WKUserContentController {
14 | func addBundledUserScript(_ scriptNameInBundle: String, injectAtStart: Bool = false, injectAtEnd: Bool = false, forMainFrameOnly: Bool = true, sourceReplacements: [String:String]? = nil) {
15 | assert(injectAtStart || injectAtEnd, "User script must either be injected at start or at end. Add injectAtStart: true or injectAtEnd: true")
16 |
17 | let scriptURL = Bundle.main.url(forResource: scriptNameInBundle, withExtension: "js")!
18 | let scriptSource = try! NSMutableString(contentsOf: scriptURL, usedEncoding: nil)
19 |
20 | if let sourceReplacements = sourceReplacements {
21 | func replaceInTemplate(find target: String, replace replacement: String) {
22 | scriptSource.replaceOccurrences(of: target, with: replacement, options: NSString.CompareOptions(rawValue: 0), range: NSMakeRange(0, scriptSource.length))
23 | }
24 |
25 | for (placeholderID, value) in sourceReplacements {
26 | replaceInTemplate(find: placeholderID, replace: value)
27 | }
28 | }
29 |
30 | if injectAtStart {
31 | let script = WKUserScript(source: scriptSource as String, injectionTime: .atDocumentStart, forMainFrameOnly: forMainFrameOnly)
32 | self.addUserScript(script)
33 | }
34 |
35 | if injectAtEnd {
36 | let script = WKUserScript(source: scriptSource as String, injectionTime: .atDocumentEnd, forMainFrameOnly: forMainFrameOnly)
37 | self.addUserScript(script)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Lantern/code-sign-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Source: http://stackoverflow.com/a/11284404/652615
4 |
5 | # WARNING: You may have to run Clean in Xcode after changing CODE_SIGN_IDENTITY!
6 |
7 | # Verify that $CODE_SIGN_IDENTITY is set
8 | if [ -z "${CODE_SIGN_IDENTITY}" ] ; then
9 | echo "CODE_SIGN_IDENTITY needs to be set for framework code-signing!"
10 |
11 | if [ "${CONFIGURATION}" = "Release" ] ; then
12 | exit 1
13 | else
14 | # Code-signing is optional for non-release builds.
15 | exit 0
16 | fi
17 | fi
18 |
19 | if [ -z "${CODE_SIGN_ENTITLEMENTS}" ] ; then
20 | echo "CODE_SIGN_ENTITLEMENTS needs to be set for framework code-signing!"
21 |
22 | if [ "${CONFIGURATION}" = "Release" ] ; then
23 | exit 1
24 | else
25 | # Code-signing is optional for non-release builds.
26 | exit 0
27 | fi
28 | fi
29 |
30 | ITEMS=""
31 |
32 | FRAMEWORK_DIR="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
33 | if [ -d "$FRAMEWORK_DIR" ] ; then
34 | FRAMEWORKS=$(find "${FRAMEWORK_DIR}" -depth -type d -name "*.framework" -or -name "*.dylib" -or -name "*.bundle" | sed -e "s/\(.*framework\)/\1\/Versions\/A\//")
35 | RESULT=$?
36 | if [[ $RESULT != 0 ]] ; then
37 | exit 1
38 | fi
39 |
40 | ITEMS="${FRAMEWORKS}"
41 | fi
42 |
43 | LOGINITEMS_DIR="${TARGET_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Library/LoginItems/"
44 | if [ -d "$LOGINITEMS_DIR" ] ; then
45 | LOGINITEMS=$(find "${LOGINITEMS_DIR}" -depth -type d -name "*.app")
46 | RESULT=$?
47 | if [[ $RESULT != 0 ]] ; then
48 | exit 1
49 | fi
50 |
51 | ITEMS="${ITEMS}"$'\n'"${LOGINITEMS}"
52 | fi
53 |
54 | # Prefer the expanded name, if available.
55 | CODE_SIGN_IDENTITY_FOR_ITEMS="${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
56 | if [ "${CODE_SIGN_IDENTITY_FOR_ITEMS}" = "" ] ; then
57 | # Fall back to old behavior.
58 | CODE_SIGN_IDENTITY_FOR_ITEMS="${CODE_SIGN_IDENTITY}"
59 | fi
60 |
61 | echo "Identity:"
62 | echo "${CODE_SIGN_IDENTITY_FOR_ITEMS}"
63 |
64 | echo "Entitlements:"
65 | echo "${CODE_SIGN_ENTITLEMENTS}"
66 |
67 | echo "Found:"
68 | echo "${ITEMS}"
69 |
70 | # Change the Internal Field Separator (IFS) so that spaces in paths will not cause problems below.
71 | SAVEIFS=$IFS
72 | IFS=$(echo -en "\n\b")
73 |
74 | # Loop through all items.
75 | for ITEM in $ITEMS;
76 | do
77 | echo "Signing '${ITEM}'"
78 | codesign --force --verbose --sign "${CODE_SIGN_IDENTITY_FOR_ITEMS}" --entitlements "${CODE_SIGN_ENTITLEMENTS}" "${ITEM}"
79 | RESULT=$?
80 | if [[ $RESULT != 0 ]] ; then
81 | IFS=$SAVEIFS
82 | exit 1
83 | fi
84 | done
85 |
86 | # Restore $IFS.
87 | IFS=$SAVEIFS
--------------------------------------------------------------------------------
/Lantern/console.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | function trace() {
3 | try {
4 | throw new Error('myError');
5 | }
6 | catch(e) {
7 | return e.stack;
8 | }
9 |
10 | return '';
11 | }
12 |
13 | function consoleMessage(type, messageArguments) {
14 | window.webkit.messageHandlers.console.postMessage({
15 | type: type,
16 | 'arguments': JSON.stringify(messageArguments),
17 | 'trace': trace()
18 | });
19 | }
20 |
21 | consoleMessage('test', ['testing']);
22 |
23 | window.console.log = function() {
24 | consoleMessage('log', arguments);
25 | };
26 |
27 | window.console.error = function() {
28 | consoleMessage('error', arguments);
29 | };
30 |
31 | window.console.warn = function() {
32 | consoleMessage('warn', arguments);
33 | };
34 | })();
35 |
--------------------------------------------------------------------------------
/Lantern/insertHoverlytics.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (window.top === window.self) {
3 | window.webkit.messageHandlers.googleAPIAuthorizationChanged.postMessage({
4 | googleClientAPILoaded: false,
5 | loadingHoverlyticsScript: true
6 | });
7 |
8 | var s = document.createElement('script');
9 | s.src = ('http://hoverlytics.burntcaramel.com/go/v3/go.js?_='+Math.random());
10 | document.body.appendChild(s);
11 | }
12 | })();
--------------------------------------------------------------------------------
/Lantern/panelAuthorizationChanged.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (window.location.hostname != "hoverlytics.burntcaramel.com") {
3 | return;
4 | }
5 |
6 | // Make sure Backbone app has been created by queuing in jQuery's ready.
7 | jQuery(document).ready(function() {
8 | var hoverlyticsApp = window.App;
9 | if (hoverlyticsApp) {
10 | var profile = hoverlyticsApp.profile;
11 | profile.on('change:isAuthorized', function() {
12 | var token = gapi.auth.getToken();
13 | window.webkit.messageHandlers.googleAPIAuthorizationChanged.postMessage({
14 | tokenJSONString: JSON.stringify(token)
15 | });
16 | }, window);
17 | }
18 | });
19 | })();
--------------------------------------------------------------------------------
/Lantern/setPanelAuthorizationToken.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (window.location.hostname != "hoverlytics.burntcaramel.com") {
3 | return;
4 | }
5 |
6 | window.webkit.messageHandlers.googleAPIAuthorizationChanged.postMessage({
7 | googleClientAPILoaded: false,
8 | addGoogleClientAPIReadyCallback: (typeof window.addGoogleClientAPIReadyCallback)
9 | });
10 |
11 | var token = __TOKEN__;
12 |
13 | window.addGoogleClientAPIReadyCallback(function() {
14 | gapi.auth.setToken(token);
15 |
16 | window.webkit.messageHandlers.googleAPIAuthorizationChanged.postMessage({
17 | googleClientAPILoaded: true,
18 | setToken: (typeof gapi.auth.setToken),
19 | getToken: JSON.stringify(gapi.auth.getToken())
20 | });
21 | });
22 |
23 | // Make sure Backbone app has been created by queuing in jQuery's ready.
24 | jQuery(document).ready(function() {
25 | var hoverlyticsApp = window.App;
26 | if (hoverlyticsApp) {
27 | var profile = hoverlyticsApp.profile;
28 |
29 | /*
30 | var oldCheckGoogleAuthorization = profile.checkGoogleAuthorization;
31 |
32 | profile.checkGoogleAuthorization = function() {
33 | window.webkit.messageHandlers.googleAPIAuthorizationChanged.postMessage({
34 | googleClientAPILoaded: true,
35 | changeProfileMethod: true
36 | });
37 |
38 | //oldCheckGoogleAuthorization({performAuthorization: false});
39 | profile.handleGoogleAuthorizationResult(token);
40 | };
41 | */
42 |
43 | var oldHandleGoogleAuthorizationResult = profile.handleGoogleAuthorizationResult
44 |
45 | profile.handleGoogleAuthorizationResult = function(authorizationResult) {
46 | window.webkit.messageHandlers.googleAPIAuthorizationChanged.postMessage({
47 | googleClientAPILoaded: true,
48 | authorizationResult: JSON.stringify(authorizationResult)
49 | });
50 |
51 | oldHandleGoogleAuthorizationResult.call(profile, authorizationResult);
52 | }
53 | }
54 | });
55 | })();
--------------------------------------------------------------------------------
/Lantern/userAgent.js:
--------------------------------------------------------------------------------
1 | window.navigator.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.4.10 (KHTML, like Gecko) Version/8.0.4 Safari/600.4.10";
--------------------------------------------------------------------------------
/Lantern/windowClose.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var oldWindowClose = window.close;
3 | window.close = function() {
4 | window.webkit.messageHandlers.windowDidClose.postMessage(true);
5 |
6 | oldWindowClose();
7 | };
8 | })();
--------------------------------------------------------------------------------
/LanternModel/Dictionary+UpdateClosure.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dictionary+UpdateClosure.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 28/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | extension Dictionary {
13 | mutating func updateValueForKey(_ key: Key, updater: ((_ previousValue: Value?) -> Value)) {
14 | let previousValue = self[key]
15 | self[key] = updater(previousValue)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/LanternModel/EnabledFeatures.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnabledFeatures.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 1/06/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | let USE_CLOUD_KIT = true
13 |
--------------------------------------------------------------------------------
/LanternModel/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Copyright © 2015 Burnt Caramel. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LanternModel/LanternModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // LanternModel.h
3 | // LanternModel
4 | //
5 | // Created by Patrick Smith on 30/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for LanternModel.
12 | FOUNDATION_EXPORT double LanternModelVersionNumber;
13 |
14 | //! Project version string for LanternModel.
15 | FOUNDATION_EXPORT const unsigned char LanternModelVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/LanternModel/ModelManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ModelManager.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 31/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | //import BurntFoundation
11 | import Syrup
12 |
13 |
14 | enum RecordType: String {
15 | case Site = "Site"
16 |
17 | var identifier: String {
18 | return self.rawValue
19 | }
20 | }
21 |
22 |
23 | public enum ModelManagerNotification: String {
24 | case allSitesDidChange = "LanternModel.ModelManager.AllSitesDidChangeNotification"
25 |
26 | public var notificationName: String {
27 | return self.rawValue
28 | }
29 | }
30 |
31 |
32 | public final class ErrorReceiver {
33 | public var errorCallback: ((_ error: NSError) -> ())?
34 |
35 | func receiveError(_ error: NSError) {
36 | errorCallback?(error)
37 | }
38 | }
39 |
40 |
41 | enum SitesLoadingProgression : Progression {
42 | case none
43 | case loadFromFile(fileURL: URL)
44 | case jsonData(Data)
45 | case json(Any)
46 | case sitesList(sitesList: [SiteValues], needsSaving: Bool)
47 |
48 | typealias Result = [SiteValues]
49 |
50 | enum ErrorKind : Error {
51 | case invalidJSON(json: Any)
52 | }
53 |
54 | mutating func updateOrDeferNext() throws -> Deferred? {
55 | switch self {
56 | case let .loadFromFile(fileURL):
57 | do {
58 | let data = try Data(contentsOf: fileURL, options: .mappedIfSafe)
59 | self = .jsonData(data)
60 | }
61 | catch let error as NSError {
62 | if error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoSuchFileError {
63 | self = .sitesList(sitesList: [], needsSaving: false)
64 | }
65 | }
66 | case let .jsonData(data):
67 | self = try .json(JSONSerialization.jsonObject(with: data, options: []))
68 | case let .json(json):
69 | guard
70 | let jsonDictionary = json as? [String: Any],
71 | let itemsJSON = jsonDictionary["items"] as? [Any]
72 | else { throw ErrorKind.invalidJSON(json: json) }
73 | let sitesList = try itemsJSON.map { (json: Any) -> SiteValues in
74 | guard let jsonObject = json as? [String: Any]
75 | else { throw ErrorKind.invalidJSON(json: json) }
76 |
77 | return SiteValues(fromJSON: jsonObject)
78 | }
79 |
80 | var seenUUIDs = Set()
81 | let uniqueSites = sitesList.filter { site in
82 | if seenUUIDs.contains(site.UUID) {
83 | return false
84 | }
85 |
86 | seenUUIDs.insert(site.UUID)
87 | return true
88 | }
89 |
90 | self = .sitesList(sitesList: uniqueSites, needsSaving: false)
91 | case .sitesList, .none:
92 | break
93 | }
94 | return nil
95 | }
96 |
97 | var result: Result? {
98 | guard case let .sitesList(result, _) = self else { return nil }
99 | return result
100 | }
101 |
102 | mutating func update(_ siteValues: SiteValues, uuid: Foundation.UUID) {
103 | switch self {
104 | case var .sitesList(sitesList, _):
105 | guard let index = sitesList.firstIndex(where: { $0.UUID == uuid })
106 | else { return }
107 | sitesList[index] = siteValues
108 | self = .sitesList(sitesList: sitesList, needsSaving: true)
109 | default:
110 | break
111 | }
112 | }
113 |
114 | mutating func addOrUpdate(_ siteValues: SiteValues) {
115 | let url = siteValues.homePageURL.absoluteURL
116 | switch self {
117 | case var .sitesList(sitesList, _):
118 | if let index = sitesList.firstIndex(where: { $0.UUID == siteValues.UUID }) {
119 | sitesList[index] = siteValues
120 | }
121 | else if let index = sitesList.firstIndex(where: { $0.homePageURL.absoluteURL == url }) {
122 | sitesList[index] = siteValues
123 | }
124 | else {
125 | sitesList.append(siteValues)
126 | }
127 | self = .sitesList(sitesList: sitesList, needsSaving: true)
128 | default:
129 | break
130 | }
131 | }
132 |
133 | mutating func remove(url: URL) {
134 | switch self {
135 | case var .sitesList(sitesList, _):
136 | sitesList = sitesList.filter{ $0.homePageURL != url }
137 | self = .sitesList(sitesList: sitesList, needsSaving: true)
138 | default:
139 | break
140 | }
141 | }
142 | }
143 |
144 | enum SitesSavingProgression : Progression {
145 | case saveToFile(fileURL: URL, sites: [SiteValues])
146 | case serializeJSON([String: Any], fileURL: URL)
147 | case writeData(Data, fileURL: URL)
148 | case savedFile(fileURL: URL)
149 |
150 | typealias Result = URL
151 |
152 | enum ErrorKind : Error {
153 | case invalidJSON
154 | case jsonSerialization(error: Error)
155 | case fileWriting(error: Error)
156 | }
157 |
158 | mutating func updateOrDeferNext() throws -> Deferred? {
159 | switch self {
160 | case let .saveToFile(fileURL, sites):
161 | let json = [
162 | "items": sites.map{ $0.toJSON() }
163 | ] as [String: [[String: Any]]]
164 | self = .serializeJSON(json, fileURL: fileURL)
165 | case let .serializeJSON(json, fileURL):
166 | do {
167 | if !JSONSerialization.isValidJSONObject(json) {
168 | throw ErrorKind.invalidJSON
169 | }
170 | self = try .writeData(JSONSerialization.data(withJSONObject: json), fileURL: fileURL)
171 | }
172 | catch {
173 | throw ErrorKind.jsonSerialization(error: error)
174 | }
175 | case let .writeData(data, fileURL):
176 | try data.write(to: fileURL, options: .atomic)
177 | self = .savedFile(fileURL: fileURL)
178 | case .savedFile:
179 | break
180 | }
181 | return nil
182 | }
183 |
184 | var result: Result? {
185 | guard case let .savedFile(fileURL) = self else { return nil }
186 | return fileURL
187 | }
188 | }
189 |
190 | open class ModelManager {
191 | var isAvailable = false
192 |
193 | public let errorReceiver = ErrorReceiver()
194 |
195 | fileprivate var storeDirectory: SystemDirectory
196 | fileprivate var sitesURL: URL?
197 |
198 | // Change this value, by mutating it, and it will be saved to disk
199 | fileprivate var sitesLoadingProgression: SitesLoadingProgression = .none {
200 | didSet {
201 | let progression = sitesLoadingProgression
202 | notifyAllSitesDidChange()
203 |
204 | switch progression {
205 | case .loadFromFile:
206 | progression / .utility >>= { [weak self] useResult in
207 | guard let receiver = self else { return }
208 | do {
209 | let sites = try useResult()
210 | receiver.sitesLoadingProgression = .sitesList(sitesList: sites, needsSaving: false)
211 | }
212 | catch let error {
213 | print("Error loading local sites \(error)")
214 | }
215 | }
216 | // If changed, and needs saving, then save
217 | case let .sitesList(sites, needsSaving):
218 | if needsSaving, let sitesURL = sitesURL {
219 | sitesSavingProgression = .saveToFile(fileURL: sitesURL, sites: sites)
220 | }
221 | default: break
222 | }
223 | }
224 | }
225 |
226 | fileprivate var sitesSavingProgression: SitesSavingProgression? {
227 | didSet(newValue) {
228 | guard let progression = sitesSavingProgression else { return }
229 | progression / .utility >>= { useResult in
230 | do {
231 | let _ = try useResult()
232 | }
233 | catch let error {
234 | print("Error saving local sites \(error)")
235 | }
236 | }
237 | }
238 | }
239 |
240 | public var allSites: [SiteValues]? {
241 | return sitesLoadingProgression.result
242 | }
243 |
244 | public func siteWithURL(url: URL) -> SiteValues? {
245 | guard let sites = allSites else { return nil }
246 | return sites.first{ $0.homePageURL.absoluteURL == url.absoluteURL }
247 | }
248 |
249 |
250 | init() {
251 | storeDirectory = SystemDirectory(pathComponents: ["v1"], inUserDirectory: .applicationSupportDirectory, errorReceiver: errorReceiver.receiveError, useBundleIdentifier: true)
252 | storeDirectory.useOnQueue(DispatchQueue.main) { directoryURL in
253 | let jsonURL = directoryURL.appendingPathComponent("sites.json")
254 | self.sitesURL = jsonURL
255 |
256 | self.sitesLoadingProgression = SitesLoadingProgression.loadFromFile(fileURL: jsonURL)
257 | }
258 |
259 | }
260 |
261 | open class var sharedManager: ModelManager {
262 | struct Helper {
263 | static let sharedManager = ModelManager()
264 | }
265 | return Helper.sharedManager
266 | }
267 |
268 | func onSystemDirectoryError(_ error: NSError) {
269 |
270 | }
271 |
272 | func updateMainProperties() {
273 |
274 | }
275 |
276 | fileprivate func mainQueue_notify(_ identifier: ModelManagerNotification, userInfo: [String: Any]? = nil) {
277 | let nc = NotificationCenter.default
278 | nc.post(name: Notification.Name(rawValue: identifier.notificationName), object: self, userInfo: userInfo)
279 | }
280 |
281 | func notifyAllSitesDidChange() {
282 | self.mainQueue_notify(.allSitesDidChange)
283 | }
284 |
285 | public func addOrUpdateSite(values: SiteValues) {
286 | //sitesLoadingProgression.update(siteValues, uuid: uuid)
287 | sitesLoadingProgression.addOrUpdate(values)
288 | }
289 |
290 | public func removeSite(url: URL) {
291 | sitesLoadingProgression.remove(url: url)
292 | }
293 | }
294 |
--------------------------------------------------------------------------------
/LanternModel/NSData+ONOXMLDocument.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+ONOXMLDocument.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 29/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Ono
11 |
12 |
13 | extension Data {
14 | func stringRepresentationUsingONOXMLDocumentHints(_ document: ONOXMLDocument) -> String? {
15 | let stringEncoding = document.stringEncodingWithFallback()
16 | return NSString(data: self, encoding: stringEncoding.rawValue) as String?
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/LanternModel/ONOXMLDocument+StringEncoding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ONOXMLDocument+StringEncoding.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 29/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Ono
11 |
12 |
13 | extension ONOXMLDocument {
14 | func stringEncodingWithFallback(_ fallback: String.Encoding = String.Encoding.utf8) -> String.Encoding {
15 | var stringEncoding = self.stringEncoding
16 | if stringEncoding == 0 {
17 | stringEncoding = fallback.rawValue
18 | }
19 | return String.Encoding(rawValue: stringEncoding)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LanternModel/PageInfoRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageInfoRequest.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 2/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 | import Ono
12 | import Syrup
13 |
14 |
15 | open class PageInfoRequest {
16 | typealias CompletionHandler = (_ info: PageInfo, _ infoRequest: PageInfoRequest) -> ()
17 |
18 | public let URL: Foundation.URL
19 | public let includingContent: Bool
20 | public let method: Alamofire.HTTPMethod
21 | public let expectedBaseContentType: BaseContentType
22 | let completionHandler: CompletionHandler
23 | var request: Alamofire.Request?
24 |
25 | init(URL: Foundation.URL, expectedBaseContentType: BaseContentType, includingContent: Bool = false, completionHandler: @escaping CompletionHandler) {
26 | self.URL = URL
27 | self.expectedBaseContentType = expectedBaseContentType
28 | self.includingContent = includingContent
29 | self.method = includingContent ? Alamofire.HTTPMethod.get : Alamofire.HTTPMethod.head
30 | self.completionHandler = completionHandler
31 | }
32 |
33 | func copyNotIncludingContent() -> PageInfoRequest {
34 | return PageInfoRequest(URL: URL, expectedBaseContentType: expectedBaseContentType, includingContent: false, completionHandler: completionHandler)
35 | }
36 | }
37 |
38 |
39 | extension ResourceInfo {
40 | init(requestedURL: URL, includeContent: Bool, response: HTTPURLResponse, data: Data?) {
41 | let MIMEType = MIMETypeString(response.mimeType)
42 | let baseContentType: BaseContentType = MIMEType?.baseContentType ?? .unknown
43 |
44 | let byteCount: Int?
45 | let contentInfo: PageContentInfo?
46 | if let data = data , includeContent {
47 | byteCount = data.count
48 | contentInfo = PageContentInfo(data: data, localURL: requestedURL)
49 | }
50 | else {
51 | byteCount = nil
52 | contentInfo = nil
53 | }
54 |
55 | self.init(requestedURL: requestedURL, finalURL: response.url, statusCode: response.statusCode, baseContentType: baseContentType, MIMEType: MIMEType, byteCount: byteCount, contentInfo: contentInfo)
56 | }
57 |
58 | static func serializer(_ requestedURL: URL, includeContent: Bool) -> DataResponseSerializer {
59 | return DataResponseSerializer { request, response, data, error in
60 | guard error == nil else { return .failure(error!) }
61 |
62 | if let response = response {
63 | return .success(
64 | PageInfo(requestedURL: requestedURL, includeContent: includeContent, response: response, data: data)
65 | )
66 | }
67 | else {
68 | let error = AFError.responseSerializationFailed(reason: .inputDataNil)
69 | return .failure(error)
70 | }
71 | }
72 | }
73 | }
74 |
75 |
76 | public enum ResourceInfoRetrievalStage : Progression {
77 | case getInfo(url: URL, requestManager: Alamofire.SessionManager)
78 | case getContent(url: URL, requestManager: Alamofire.SessionManager)
79 |
80 | case startedRequest(request: Alamofire.DataRequest, url: URL, includeContent: Bool)
81 |
82 | case success(info: PageInfo)
83 |
84 | public typealias Result = PageInfo
85 |
86 | enum ErrorKind: Error {
87 | case requestFailed(underlyingError: Error)
88 | }
89 |
90 | public mutating func updateOrDeferNext() throws -> Deferred? {
91 | switch self {
92 | case let .getInfo(url, requestManager):
93 | let request = requestManager.request(url, method: .head)
94 | self = .startedRequest(request: request, url: url, includeContent: false)
95 |
96 | case let .getContent(url, requestManager):
97 | let request = requestManager.request(url)
98 | self = .startedRequest(request: request, url: url, includeContent: true)
99 |
100 | case let .startedRequest(request, url, includeContent):
101 | return Deferred.future{ resolve in
102 | let serializer = ResourceInfo.serializer(url, includeContent: includeContent)
103 | request.response(responseSerializer: serializer) { response in
104 | switch response.result {
105 | case let .success(info):
106 | resolve{ .success(info: info) }
107 | case let .failure(error):
108 | resolve{ throw ErrorKind.requestFailed(underlyingError: error) }
109 | }
110 | }
111 | }
112 | case .success: break
113 | }
114 | return nil
115 | }
116 |
117 | public var result: Result? {
118 | guard case let .success(result) = self else { return nil }
119 | return result
120 | }
121 | }
122 |
123 |
124 | class PageInfoRequestQueue {
125 | let requestManager: Alamofire.SessionManager
126 | let maximumActiveRequests = 5
127 | var activeRequests = [PageInfoRequest]()
128 | var pendingRequests = [PageInfoRequest]()
129 | var willPerformHTTPRedirection: ((_ redirectionInfo: RequestRedirectionInfo) -> ())?
130 | var didFinishWithRequest: ((_ infoRequest: PageInfoRequest) -> ())?
131 |
132 | init() {
133 | let manager = Alamofire.SessionManager()
134 | self.requestManager = manager
135 |
136 | let managerDelegate = manager.delegate
137 | managerDelegate.taskWillPerformHTTPRedirection = { [weak self] session, task, response, request in
138 | #if DEBUG
139 | print("taskWillPerformHTTPRedirection")
140 | #endif
141 |
142 | if let
143 | willPerformHTTPRedirection = self?.willPerformHTTPRedirection,
144 | let originalRequest = task.originalRequest
145 | {
146 | let info = RequestRedirectionInfo(sourceRequest: originalRequest, nextRequest: request, statusCode: response.statusCode, MIMEType: MIMETypeString(response.mimeType))
147 | willPerformHTTPRedirection(info)
148 | }
149 |
150 | return request
151 | }
152 | }
153 |
154 | func addRequestForURL(_ URL: Foundation.URL, expectedBaseContentType: BaseContentType, includingContent: Bool, highPriority: Bool = false, completionHandler: @escaping PageInfoRequest.CompletionHandler) -> PageInfoRequest? {
155 | let URL = URL.absoluteURL
156 | guard URL.host != nil else {
157 | return nil
158 | }
159 |
160 | let infoRequest = PageInfoRequest(URL: URL, expectedBaseContentType: expectedBaseContentType, includingContent: includingContent, completionHandler: completionHandler)
161 |
162 | if highPriority || activeRequests.count < maximumActiveRequests {
163 | performRequest(infoRequest)
164 | }
165 | else {
166 | pendingRequests.append(infoRequest)
167 | }
168 |
169 | return infoRequest
170 | }
171 |
172 | func downgradePendingRequestsToNotIncludeContent(_ decider: ((PageInfoRequest) -> Bool)) {
173 | pendingRequests = pendingRequests.map { request in
174 | if request.includingContent && decider(request) {
175 | return request.copyNotIncludingContent()
176 | }
177 | else {
178 | return request
179 | }
180 | }
181 | }
182 |
183 | func cancelRequestForURL(_ URL: Foundation.URL) {
184 | let URLAbsoluteString = URL.absoluteString
185 |
186 | func requestContainsURL(_ infoRequest: PageInfoRequest) -> Bool {
187 | return infoRequest.URL.absoluteString == URLAbsoluteString
188 | }
189 |
190 | for (index, request) in pendingRequests.enumerated() {
191 | if requestContainsURL(request) {
192 | pendingRequests.remove(at: index)
193 | break
194 | }
195 | }
196 | }
197 |
198 | fileprivate func performNextPendingRequest() {
199 | // Perform the request next at the top of the list.
200 | if let infoRequest = pendingRequests.first {
201 | pendingRequests.remove(at: 0)
202 | performRequest(infoRequest)
203 | }
204 | }
205 |
206 | fileprivate func activeRequestDidComplete(_ infoRequest: PageInfoRequest, withInfo info: PageInfo) {
207 | infoRequest.completionHandler(info, infoRequest)
208 |
209 | // Remove from active requests
210 | for (index, someRequest) in activeRequests.enumerated() {
211 | if someRequest === infoRequest {
212 | activeRequests.remove(at: index)
213 | break
214 | }
215 | }
216 |
217 | // Start the next request in queue going
218 | performNextPendingRequest()
219 | }
220 |
221 | var activeRetrievals = [ResourceInfoRetrievalStage]()
222 | var pendingRetrievals = [ResourceInfoRetrievalStage]()
223 | //let executionCustomizer = GCDExecutionCustomizer()
224 |
225 | fileprivate func performRetrieval(_ stage: ResourceInfoRetrievalStage) {
226 | stage / .utility >>= { useResult in
227 | do {
228 | let result = try useResult()
229 | }
230 | catch {
231 | // ERROR
232 | }
233 | }
234 | }
235 |
236 | fileprivate func performRequest(_ infoRequest: PageInfoRequest) {
237 | activeRequests.append(infoRequest)
238 |
239 | // An Alamofire serializer to perform the parsing etc on the request’s background queue.
240 | let serializer = DataResponseSerializer { URLRequest, response, data, error in
241 | guard error == nil else { return .failure(error!) }
242 |
243 | if let response = response {
244 | let requestedURL = infoRequest.URL
245 | let MIMEType = MIMETypeString(response.mimeType)
246 | let baseContentType: BaseContentType = MIMEType?.baseContentType ?? .unknown
247 |
248 | let byteCount: Int?
249 | let contentInfo: PageContentInfo?
250 | if let data = data , infoRequest.includingContent {
251 | byteCount = data.count
252 | contentInfo = PageContentInfo(data: data, localURL: requestedURL)
253 | }
254 | else {
255 | byteCount = nil
256 | contentInfo = nil
257 | }
258 |
259 | let info = PageInfo(requestedURL: requestedURL, finalURL: response.url, statusCode: response.statusCode, baseContentType: baseContentType, MIMEType: MIMEType, byteCount: byteCount, contentInfo: contentInfo)
260 |
261 | return .success(info)
262 | }
263 | else {
264 | let error = AFError.responseSerializationFailed(reason: .inputDataNil)
265 | return .failure(error)
266 | }
267 | }
268 |
269 | // Perform the request
270 | let requestedURL = infoRequest.URL
271 | infoRequest.request = requestManager
272 | .request(requestedURL, method: infoRequest.method)
273 | .response(responseSerializer: serializer, completionHandler: { response in
274 | if case let .success(info) = response.result {
275 | self.activeRequestDidComplete(infoRequest, withInfo: info)
276 | }
277 | })
278 | }
279 |
280 | func cancelAll(_ clearAll: Bool = true) {
281 | // Cancel any requests being performed
282 | for infoRequest in activeRequests {
283 | infoRequest.request?.cancel()
284 | infoRequest.request = nil
285 | }
286 |
287 | if clearAll {
288 | pendingRequests.removeAll()
289 | }
290 | else {
291 | // Put imcomplete requests back on the queue
292 | pendingRequests.insert(contentsOf: activeRequests, at: 0)
293 | }
294 |
295 | activeRequests.removeAll()
296 | }
297 | }
298 |
299 |
--------------------------------------------------------------------------------
/LanternModel/PageMapper+CSV.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageMapper+CSV.swift
3 | // LanternModel
4 | //
5 | // Created by Patrick Smith on 7/11/19.
6 | // Copyright © 2019 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CSV
11 |
12 | extension ValidatedStringValue {
13 | var stringForCSV: String {
14 | switch self {
15 | case .validString(let stringValue):
16 | return stringValue
17 | case .validKeyValue(let key, let value):
18 | return "\(key): \(value)"
19 | case .notRequested:
20 | return "(not requested)"
21 | case .missing:
22 | return "(none)"
23 | case .empty:
24 | return "(empty)"
25 | case .multiple(let values):
26 | return values.map { $0.stringForCSV }.joined(separator: "\t")
27 | case .invalid:
28 | return "(invalid)"
29 | }
30 | }
31 | }
32 |
33 |
34 | public struct CrawledResultsCSVCreator {
35 | public var baseContentType: BaseContentType = .localHTMLPage
36 |
37 | public init() {}
38 |
39 | public func csvData(pageMapper: PageMapper) throws -> Data {
40 | let csv = try CSVWriter(stream: .toMemory())
41 |
42 | let columnIdentifiers: [PagePresentedInfoIdentifier] = [.requestedURL, .pageTitle, .h1, .statusCode, .MIMEType, .pageByteCount, .pageByteCountBeforeBodyTag, .pageByteCountAfterBodyTag, .internalLinks, .externalLinks]
43 | try csv.write(row: ["path", "title", "h1", "status_code", "mime_type", "page_byte_count", "head_byte_count", "body_byte_count", "internal_links", "external_links"])
44 |
45 | let urls = pageMapper.copyURLsWithBaseContentType(baseContentType)
46 | for url in urls {
47 | if let resourceInfo = pageMapper.pageInfoForRequestedURL(url) {
48 | try csv.write(row: columnIdentifiers.map { identifier in
49 | identifier.validatedStringValueInPageInfo(resourceInfo, pageMapper: pageMapper).stringForCSV
50 | })
51 | }
52 | }
53 |
54 | csv.stream.close()
55 |
56 | return csv.stream.property(forKey: .dataWrittenToMemoryStreamKey) as! Data
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/LanternModel/PageMapper+Filtering.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageMapper+Problems.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 28/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | public extension PageMapper {
13 | func numberOfRequestedURLsWithBaseContentType(_ type: BaseContentType) -> Int {
14 | switch type {
15 | case .localHTMLPage:
16 | return requestedLocalPageURLsUnique.count
17 | case .image:
18 | return requestedImageURLsUnique.count
19 | case .feed:
20 | return requestedFeedURLsUnique.count
21 | default:
22 | return 0
23 | }
24 | }
25 |
26 | func numberOfLoadedURLsWithBaseContentType(_ baseContentType: BaseContentType, responseType: PageResponseType? = nil) -> UInt {
27 | if let responseType = responseType {
28 | return baseContentTypeToResponseTypeToURLCount[baseContentType]?[responseType] ?? 0
29 | }
30 | // Else any response type: tally them up.
31 | else if let responseTypeToURLCount = baseContentTypeToResponseTypeToURLCount[baseContentType] {
32 | return responseTypeToURLCount.reduce(UInt(0), { (totalSoFar, dictIndex) -> UInt in
33 | let (responseType, URLCount) = dictIndex
34 | return totalSoFar + URLCount
35 | })
36 | }
37 | else {
38 | return 0
39 | }
40 | }
41 |
42 | func copyURLsWithBaseContentType(_ type: BaseContentType) -> [URL] {
43 | switch type {
44 | case .localHTMLPage:
45 | return localPageURLsOrdered as [URL]
46 | case .image:
47 | return imageURLsOrdered as [URL]
48 | case .feed:
49 | return feedURLsOrdered as [URL]
50 | default:
51 | return []
52 | }
53 | }
54 |
55 | func copyURLsWithBaseContentType(_ type: BaseContentType, withResponseType responseType: PageResponseType) -> [URL] {
56 | let URLs: [URL] = copyURLsWithBaseContentType(type)
57 |
58 | return URLs.filter { (URL) in
59 | if
60 | let pageInfo = self.loadedURLToPageInfo[URL]
61 | {
62 | let responseTypeToCheck = PageResponseType(statusCode: pageInfo.statusCode)
63 | return responseTypeToCheck == responseType
64 | }
65 |
66 | return false
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/LanternModel/PageMapper+InfoPresenting.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageMapper+InfoPresenting.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 28/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Ono
11 |
12 |
13 | extension URL {
14 | var burnt_pathWithQuery: String {
15 | if let query = query {
16 | return "\(path)?\(query)"
17 | }
18 | else {
19 | return path
20 | }
21 | }
22 | }
23 |
24 |
25 | public enum PagePresentedInfoIdentifier: String {
26 | case requestedURL = "requestedURL"
27 | case statusCode = "statusCode"
28 | case MIMEType = "MIMEType"
29 | case pageTitle = "pageTitle"
30 | case h1 = "h1"
31 | case metaDescription = "metaDescription"
32 | case openGraphTags = "openGraphTags"
33 | case openGraphImage = "openGraphImage"
34 | case pageByteCount = "pageByteCount"
35 | case pageByteCountBeforeBodyTag = "pageBeforeBodyTagBytes"
36 | case pageByteCountAfterBodyTag = "pageAfterBodyTagBytes"
37 | case internalLinkCount = "internalLinkCount"
38 | case internalLinks = "internalLinks"
39 | case externalLinkCount = "externalLinkCount"
40 | case externalLinks = "externalLinks"
41 |
42 | public var longerFormInformation: PagePresentedInfoIdentifier? {
43 | switch self {
44 | case .internalLinkCount:
45 | return .internalLinks
46 | case .externalLinkCount:
47 | return .externalLinks
48 | default:
49 | return nil
50 | }
51 | }
52 |
53 | public func titleForBaseContentType(_ baseContentType: BaseContentType?) -> String {
54 | switch self {
55 | case .requestedURL:
56 | if let baseContentType = baseContentType {
57 | switch baseContentType {
58 | case .localHTMLPage:
59 | return "Path"
60 | case .image:
61 | return "Image URL"
62 | case .feed:
63 | return "Feed URL"
64 | default:
65 | break
66 | }
67 | }
68 |
69 | return "URL"
70 | case .statusCode:
71 | return "Status Code"
72 | case .MIMEType:
73 | return "MIME Type"
74 | case .pageTitle:
75 | return "Title"
76 | case .h1:
77 | return "H1"
78 | case .metaDescription:
79 | return "Meta Description"
80 | case .openGraphTags:
81 | return "Open Graph Tags"
82 | case .openGraphImage:
83 | return "Open Graph Image"
84 | case .pageByteCount:
85 | return "Total Bytes"
86 | case .pageByteCountBeforeBodyTag:
87 | return " Bytes"
88 | case .pageByteCountAfterBodyTag:
89 | return " Bytes"
90 | case .internalLinkCount, .internalLinks:
91 | return "Internal Links"
92 | case .externalLinkCount, .externalLinks:
93 | return "External Links"
94 | }
95 | }
96 |
97 | fileprivate func stringValueForMultipleElementsContent(_ elements: [ONOXMLElement]) -> String? {
98 | switch elements.count {
99 | case 1:
100 | let element = elements[0]
101 | var stringValue = element.stringValue() ?? ""
102 | // Conforms spaces and new lines into single spaces
103 | stringValue = stringValue.replacingOccurrences(of: "[\\s]+", with: " ", options: .regularExpression, range: nil)
104 | // Trim whitespace from ends
105 | stringValue = stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
106 | return stringValue
107 | case 0:
108 | return nil
109 | default:
110 | return "(multiple)"
111 | }
112 | }
113 |
114 | fileprivate func stringValueForMultipleElements(_ elements: [ONOXMLElement], attribute: String) -> String? {
115 | switch elements.count {
116 | case 1:
117 | let element = elements[0]
118 | if let stringValue = element[attribute] as? String {
119 | return stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
120 | }
121 | else {
122 | return nil
123 | }
124 | case 0:
125 | return nil
126 | default:
127 | return "(multiple)"
128 | }
129 | }
130 |
131 | fileprivate static var byteFormatter: ByteCountFormatter = {
132 | let byteFormatter = ByteCountFormatter()
133 | byteFormatter.countStyle = .binary
134 | byteFormatter.isAdaptive = false
135 | return byteFormatter
136 | }()
137 |
138 | public func validatedStringValueForPendingURL(_ requestedURL: URL) -> ValidatedStringValue {
139 | switch self {
140 | case .requestedURL:
141 | #if DEBUG && false
142 | return ValidatedStringValue(string: "\(requestedURL)")
143 | #endif
144 |
145 | return ValidatedStringValue(string: requestedURL.burnt_pathWithQuery)
146 | default:
147 | break
148 | }
149 |
150 | return .missing
151 | }
152 |
153 | public func validatedStringValueInPageInfo(_ pageInfo: PageInfo, pageMapper: PageMapper) -> ValidatedStringValue {
154 | switch self {
155 | case .requestedURL:
156 | let requestedURL = pageInfo.requestedURL
157 | let requestedPath = requestedURL.burnt_pathWithQuery
158 | if
159 | let finalURL = pageInfo.finalURL,
160 | requestedURL.absoluteString != finalURL.absoluteString
161 | {
162 | let finalURLPath = finalURL.burnt_pathWithQuery
163 | if pageMapper.redirectedDestinationURLToInfo[finalURL] != nil {
164 | return ValidatedStringValue(string: "\(requestedPath) (\(finalURLPath))")
165 | }
166 |
167 | let requestedScheme = requestedURL.scheme
168 | let finalScheme = finalURL.scheme
169 | if requestedScheme != finalScheme {
170 | return ValidatedStringValue(string: "\(requestedPath) (\(finalURLPath) to \(String(describing: finalScheme)))")
171 | }
172 |
173 | return ValidatedStringValue(string: "\(requestedPath) (\(finalURLPath))")
174 | }
175 |
176 | #if DEBUG && false
177 | return ValidatedStringValue(string: "\(requestedURL) \(pageInfo.finalURL)")
178 | #endif
179 |
180 | return ValidatedStringValue(string: requestedPath)
181 | case .statusCode:
182 | return ValidatedStringValue(string: String(pageInfo.statusCode))
183 | case .MIMEType:
184 | if let MIMEType = pageInfo.MIMEType?.stringValue {
185 | return ValidatedStringValue(
186 | string: MIMEType
187 | )
188 | }
189 | case .pageTitle:
190 | if let pageTitleElements = pageInfo.contentInfo?.pageTitleElements {
191 | return ValidatedStringValue.validateContentOfElements(pageTitleElements)
192 | }
193 | case .h1:
194 | if let h1Elements = pageInfo.contentInfo?.h1Elements {
195 | return ValidatedStringValue.validateContentOfElements(h1Elements)
196 | }
197 | case .metaDescription:
198 | if let metaDescriptionElements = pageInfo.contentInfo?.metaDescriptionElements {
199 | return ValidatedStringValue.validateAttribute("content", ofElements: metaDescriptionElements)
200 | }
201 | case .openGraphTags:
202 | if let openGraphElements = pageInfo.contentInfo?.openGraphElements {
203 | return ValidatedStringValue.validate(keyAttribute: "property", valueAttribute: "content", of: openGraphElements)
204 | }
205 | case .openGraphImage:
206 | guard let openGraphElements = pageInfo.contentInfo?.openGraphElements else { return .missing }
207 | guard let imageElement = openGraphElements.first(where: { $0["property"] as? String == "og:image" }) else { return .missing }
208 | return ValidatedStringValue.validate(keyAttribute: "property", valueAttribute: "content", of: imageElement)
209 | case .pageByteCount:
210 | if let byteCount = pageInfo.byteCount {
211 | return ValidatedStringValue(
212 | string: PagePresentedInfoIdentifier.byteFormatter.string(fromByteCount: Int64(byteCount))
213 | )
214 | }
215 | else {
216 | return .notRequested
217 | }
218 | case .pageByteCountBeforeBodyTag:
219 | if let byteCountBeforeBody = pageInfo.contentInfo?.preBodyByteCount {
220 | return ValidatedStringValue(
221 | string: PagePresentedInfoIdentifier.byteFormatter.string(fromByteCount: Int64(byteCountBeforeBody))
222 | )
223 | }
224 | case .pageByteCountAfterBodyTag:
225 | if let
226 | byteCount = pageInfo.byteCount,
227 | let byteCountBeforeBody = pageInfo.contentInfo?.preBodyByteCount
228 | {
229 | return ValidatedStringValue(
230 | string: PagePresentedInfoIdentifier.byteFormatter.string(fromByteCount: Int64(byteCount - byteCountBeforeBody))
231 | )
232 | }
233 | case .internalLinkCount:
234 | if let localPageURLs = pageInfo.contentInfo?.localPageURLs {
235 | return ValidatedStringValue(
236 | string: String(localPageURLs.count)
237 | )
238 | }
239 | case .internalLinks:
240 | if let localPageURLs = pageInfo.contentInfo?.localPageURLs {
241 | return ValidatedStringValue.multiple(
242 | localPageURLs.map { URL in
243 | ValidatedStringValue(
244 | string: URL.absoluteString
245 | )
246 | }
247 | )
248 | }
249 | case .externalLinkCount:
250 | if let externalPageURLs = pageInfo.contentInfo?.externalPageURLs {
251 | return ValidatedStringValue(
252 | string: String(externalPageURLs.count)
253 | )
254 | }
255 | case .externalLinks:
256 | if let externalPageURLs = pageInfo.contentInfo?.externalPageURLs {
257 | return ValidatedStringValue.multiple(
258 | externalPageURLs.map { URL in
259 | ValidatedStringValue(
260 | string: URL.absoluteString
261 | )
262 | }
263 | )
264 | }
265 | }
266 |
267 | return .missing
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/LanternModel/PageMapper+LinkFiltering.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageMapper+LinkFiltering.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 3/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Ono
11 |
12 |
13 | public enum PageLinkFilter {
14 | case isLinkedByURL(URL)
15 | case containsLinkToURL(URL)
16 | }
17 |
18 |
19 | extension PageMapper {
20 | public func copyHTMLPageURLsFilteredBy(_ linkFilter: PageLinkFilter) -> [URL] {
21 | let URLs = copyURLsWithBaseContentType(.localHTMLPage, withResponseType: .successful)
22 |
23 | switch linkFilter {
24 | case .isLinkedByURL(let linkedByURL):
25 | if let contentInfo = self.pageInfoForRequestedURL(linkedByURL)?.contentInfo {
26 | return URLs.filter { (URLToCheck) in
27 | return contentInfo.containsLocalPageURL(URLToCheck)
28 | }
29 | }
30 | else {
31 | return []
32 | }
33 | case .containsLinkToURL(let childURL):
34 | return URLs.filter { (URLToCheck) in
35 | if let contentInfo = self.pageInfoForRequestedURL(URLToCheck)?.contentInfo {
36 | return contentInfo.containsLocalPageURL(childURL)
37 | }
38 |
39 | return false
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LanternModel/PageMapper+Validating.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageInfoValidationResult.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 28/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Ono
11 |
12 |
13 | public enum PageInfoValidationResult {
14 | case valid
15 | case notRequested
16 | case missing
17 | case empty
18 | case multiple
19 | case invalid
20 | }
21 |
22 | private let whitespaceCharacterSet = CharacterSet.whitespacesAndNewlines
23 | private let nonWhitespaceCharacterSet = whitespaceCharacterSet.inverted
24 |
25 | private func stringIsJustWhitespace(_ string: String) -> Bool {
26 | // Return range if non-whitespace characters are present, nil if no non-whitespace characters are present.
27 | return string.rangeOfCharacter(from: nonWhitespaceCharacterSet, options: [], range: nil) == nil
28 | }
29 |
30 | extension PageInfoValidationResult {
31 | init(validatedStringValue: ValidatedStringValue) {
32 | switch validatedStringValue{
33 | case .validString, .validKeyValue:
34 | self = .valid
35 | case .notRequested:
36 | self = .notRequested
37 | case .missing:
38 | self = .missing
39 | case .empty:
40 | self = .empty
41 | case .multiple:
42 | self = .multiple
43 | case .invalid:
44 | self = .invalid
45 | }
46 | }
47 |
48 | static func validateContentsOfElements(_ elements: [ONOXMLElement]) -> PageInfoValidationResult {
49 | let validatedStringValue = ValidatedStringValue.validateContentOfElements(elements)
50 | return self.init(validatedStringValue: validatedStringValue)
51 | }
52 |
53 | static func validateMIMEType(_ MIMEType: String) -> PageInfoValidationResult {
54 | if stringIsJustWhitespace(MIMEType) {
55 | return .missing
56 | }
57 |
58 | return .valid
59 | }
60 | }
61 |
62 |
63 | public enum PageInfoValidationArea: Int {
64 | case mimeType = 1
65 | case Title = 2
66 | case h1 = 3
67 | case metaDescription = 4
68 |
69 | var title: String {
70 | switch self {
71 | case .mimeType:
72 | return "MIME Type"
73 | case .Title:
74 | return "Title"
75 | case .h1:
76 | return "H1"
77 | case .metaDescription:
78 | return "Meta Description"
79 | }
80 | }
81 |
82 | var isRequired: Bool {
83 | return true
84 | }
85 |
86 | static var allAreas = Set([.mimeType, .Title, .h1, .metaDescription])
87 | }
88 |
89 | extension PageInfo {
90 | public func validateArea(_ validationArea: PageInfoValidationArea) -> PageInfoValidationResult {
91 | switch validationArea {
92 | case .mimeType:
93 | if let MIMEType = MIMEType {
94 | return PageInfoValidationResult.validateMIMEType(MIMEType.stringValue)
95 | }
96 | case .Title:
97 | if let pageTitleElements = contentInfo?.pageTitleElements {
98 | return PageInfoValidationResult.validateContentsOfElements(pageTitleElements)
99 | }
100 | case .h1:
101 | if let h1Elements = contentInfo?.h1Elements {
102 | return PageInfoValidationResult.validateContentsOfElements(h1Elements)
103 | }
104 | case .metaDescription:
105 | if let metaDescriptionElements = self.contentInfo?.metaDescriptionElements {
106 | switch metaDescriptionElements.count {
107 | case 1:
108 | let element = metaDescriptionElements[0]
109 | let validatedStringValue = ValidatedStringValue.validateAttribute("content", ofElement: element)
110 | return PageInfoValidationResult(validatedStringValue: validatedStringValue)
111 | case 0:
112 | return .missing
113 | default:
114 | return .multiple
115 | }
116 | }
117 | }
118 |
119 | return .missing
120 | }
121 | }
122 |
123 |
124 | public extension PageMapper {
125 | func copyHTMLPageURLsWhichCompletelyValidateForType(_ type: BaseContentType) -> [URL] {
126 | let validationAreas = PageInfoValidationArea.allAreas
127 | let URLs = copyURLsWithBaseContentType(type, withResponseType: .successful)
128 |
129 | return URLs.filter { URL in
130 | guard let pageInfo = self.loadedURLToPageInfo[URL] else { return false }
131 |
132 | let containsInvalidResult = validationAreas.contains { validationArea in
133 | return pageInfo.validateArea(validationArea) != .valid
134 | }
135 |
136 | return !containsInvalidResult
137 | }
138 | }
139 |
140 | func copyHTMLPageURLsForType(_ type: BaseContentType, failingToValidateInArea validationArea: PageInfoValidationArea) -> [URL] {
141 | let URLs = copyURLsWithBaseContentType(type, withResponseType: .successful)
142 |
143 | return URLs.filter { URL in
144 | guard let pageInfo = self.loadedURLToPageInfo[URL] else { return false }
145 |
146 | switch pageInfo.validateArea(validationArea) {
147 | case .valid:
148 | return false
149 | case .missing:
150 | return validationArea.isRequired // Only invalid if it is required
151 | default:
152 | return true
153 | }
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/LanternModel/Site.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Site.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 30/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | private enum Error: Int {
13 | case nameIsEmpty = 10
14 |
15 | case homePageURLIsEmpty = 20
16 | case homePageURLIsInvalid
17 |
18 | static let domain = "LanternApp.SiteSettingsViewController.errorDomain"
19 |
20 | var errorCode: Int {
21 | return rawValue
22 | }
23 |
24 | var description: String {
25 | switch self {
26 | case .nameIsEmpty:
27 | return NSLocalizedString("Please enter a name for your site", comment: "Site NameIsEmpty error description")
28 | case .homePageURLIsEmpty:
29 | return NSLocalizedString("Please enter a URL for your site’s home page", comment: "Site HomePageURLIsEmpty error description")
30 | case .homePageURLIsInvalid:
31 | return NSLocalizedString("Please enter a valid URL for your site’s home page", comment: "Site HomePageURLIsInvalid error description")
32 | }
33 | }
34 |
35 | var cocoaError: NSError {
36 | let userInfo = [
37 | NSLocalizedDescriptionKey: self.description
38 | ]
39 | return NSError(domain: Error.domain, code: errorCode, userInfo: userInfo)
40 | }
41 | }
42 |
43 |
44 | public struct SiteValues: Equatable {
45 | public let UUID: Foundation.UUID
46 |
47 | public var name: String
48 | public var homePageURL: URL
49 |
50 | public init(name: String, homePageURL: URL, UUID: Foundation.UUID = Foundation.UUID()) {
51 | self.name = name
52 | self.homePageURL = homePageURL
53 | self.UUID = UUID
54 | }
55 | }
56 |
57 | public func ==(lhs: SiteValues, rhs: SiteValues) -> Bool {
58 | return
59 | lhs.name == rhs.name &&
60 | lhs.homePageURL == rhs.homePageURL
61 | }
62 |
63 | extension SiteValues {
64 | fileprivate init(fromStoredValues values: ValueStorable) {
65 | name = values["name"] as! String
66 | homePageURL = URL(string: values["homePageURL"] as! String)!
67 | UUID = Foundation.UUID(uuidString: values["UUID"] as! String)!
68 | }
69 |
70 | fileprivate func updateStoredValues(_ values: ValueStorable) -> ValueStorable {
71 | var values = values
72 | values["name"] = name
73 | values["homePageURL"] = homePageURL.absoluteString
74 | values["UUID"] = UUID.uuidString
75 |
76 | return values
77 | }
78 | }
79 |
80 | extension SiteValues {
81 | init(fromJSON json: [String: Any]) {
82 | let jsonValues = RecordJSON(dictionary: json)
83 | self.init(fromStoredValues: jsonValues)
84 | }
85 |
86 | func toJSON() -> [String: Any] {
87 | return [
88 | "name": name,
89 | "homePageURL": homePageURL.absoluteString,
90 | "UUID": UUID.uuidString
91 | ]
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/LanternModel/SiteEssentialsInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SiteEssentialsInfo.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 7/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | public enum SiteEssentialType {
13 | case siteMapXML
14 | case robotsTxt
15 | case valid404Page
16 | //case WWWRedirecting(hasWWW: Bool)
17 | //case HTTPS
18 | //case HTTPToHTTPSRedirection
19 | case favIconAtRoot
20 | //case TouchIcon
21 |
22 |
23 | public enum Need {
24 | case required
25 | case recommended
26 | case optional
27 | }
28 |
29 |
30 | public var need: Need {
31 | switch self {
32 | case .favIconAtRoot: //, .TouchIcon:
33 | return .recommended
34 | //case .HTTPS:
35 | // return .Optional
36 | default:
37 | return .required
38 | }
39 | }
40 | }
41 |
42 |
43 | public struct SiteEssentialInfo {
44 | public let type: SiteEssentialType
45 |
46 | public let wasFound: Bool?
47 | public let isValid: Bool?
48 |
49 | public let resourceInfos: [PageInfo]
50 | }
51 |
52 |
53 | open class SiteEssentialInfoRequest {
54 | public typealias CompletionHandler = (_ info: SiteEssentialInfo) -> ()
55 |
56 | public let type: SiteEssentialType
57 | public let completionHandler: CompletionHandler
58 |
59 | var resourceRequests: [String: PageInfoRequest]
60 | var resourceInfos: [String: PageInfo]
61 |
62 | init(type: SiteEssentialType, baseURL: URL, completionHandler: @escaping CompletionHandler) {
63 | self.type = type
64 | self.completionHandler = completionHandler
65 |
66 | resourceRequests = [String: PageInfoRequest]()
67 | resourceInfos = [String: PageInfo]()
68 |
69 | func addResourceRequest(_ pathComponent: String, identifier: String? = nil) {
70 | let identifier = identifier ?? pathComponent
71 |
72 | func requestCompletionHandler(_ info: PageInfo) {
73 |
74 | }
75 |
76 | #if false
77 | var infoRequest = PageInfoRequest(URL: baseURL.URLByAppendingPathComponent(pathComponent)) { [weak self] (resourceInfo, infoRequest) in
78 | self?.didCompleteResourceInfoRequest(resourceInfo, identifier: identifier)
79 | }
80 | resourceRequests[identifier] = infoRequest
81 | #endif
82 | }
83 |
84 | switch type {
85 | case .siteMapXML:
86 | addResourceRequest("sitemap.xml")
87 | case .robotsTxt:
88 | addResourceRequest("robots.txt")
89 | case .valid404Page:
90 | // Use UUID to create a unique request every time.
91 | addResourceRequest(UUID().uuidString, identifier: "UUID")
92 | case .favIconAtRoot:
93 | addResourceRequest("favicon.ico")
94 | }
95 | }
96 |
97 | func didCompleteResourceInfoRequest(_ resourceInfo: PageInfo, identifier: String) {
98 | resourceInfos[identifier] = resourceInfo
99 | }
100 |
101 | open var currentInfo: SiteEssentialInfo {
102 | var finished = resourceRequests.count == resourceInfos.count
103 | return SiteEssentialInfo(type: type, wasFound: nil, isValid: nil, resourceInfos: [])
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/LanternModel/SystemDirectory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SystemDirectory.swift
3 | // BurntFoundation
4 | //
5 | // Created by Patrick Smith on 26/07/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 |
13 | public class SystemDirectory {
14 | public typealias ErrorReceiver = (NSError) -> ()
15 |
16 | public let pathComponents: [String]
17 | public let domainMask: FileManager.SearchPathDomainMask
18 | public let directoryBase: FileManager.SearchPathDirectory
19 | public let errorReceiver: ErrorReceiver
20 | fileprivate let group: DispatchGroup
21 | fileprivate var createdDirectoryURL: URL?
22 |
23 | public init(pathComponents: [String], inUserDirectory directoryBase: FileManager.SearchPathDirectory, errorReceiver: @escaping ErrorReceiver, useBundleIdentifier: Bool = true) {
24 | var pathComponents = pathComponents
25 | if useBundleIdentifier {
26 | if let bundleIdentifier = Bundle.main.bundleIdentifier {
27 | pathComponents.insert(bundleIdentifier, at: 0)
28 | }
29 | }
30 |
31 | self.pathComponents = pathComponents
32 | self.domainMask = [.userDomainMask]
33 | self.directoryBase = directoryBase
34 | self.errorReceiver = errorReceiver
35 |
36 | group = DispatchGroup()
37 |
38 | createDirectory()
39 | }
40 |
41 | fileprivate func createDirectory() {
42 | let queue = DispatchQueue.global(qos: .default)
43 | queue.async(group: group) {
44 | let fm = FileManager.default
45 |
46 | do {
47 | let baseDirectoryURL = try fm.url(for: self.directoryBase, in: self.domainMask, appropriateFor: nil, create: true)
48 |
49 | // Convert path to its components, so we can add more components
50 | // and convert back into a URL.
51 | var pathComponents = baseDirectoryURL.pathComponents
52 | pathComponents.append(contentsOf: self.pathComponents)
53 |
54 | // Convert components back into a URL.
55 | guard let directoryURL = NSURL.fileURL(withPathComponents: pathComponents)
56 | else { return }
57 |
58 | try fm.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
59 |
60 | self.createdDirectoryURL = directoryURL
61 | }
62 | catch let error as NSError {
63 | self.errorReceiver(error)
64 | }
65 | }
66 | }
67 |
68 | public func useOnQueue(_ queue: DispatchQueue, closure: @escaping (_ directoryURL: URL) -> ()) {
69 | group.notify(queue: queue) {
70 | if let createdDirectoryURL = self.createdDirectoryURL {
71 | closure(createdDirectoryURL)
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/LanternModel/UniqueURLArray.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UniqueURLArray.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 28/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | func conformURL(_ URL: Foundation.URL, requireHost: Bool = true) -> Foundation.URL? {
13 | if var urlComponents = URLComponents(url: URL, resolvingAgainstBaseURL: true) {
14 | if requireHost && urlComponents.host == nil {
15 | return nil
16 | }
17 | // Remove #fragments
18 | urlComponents.fragment = nil
19 | // Home page should always have trailing slash
20 | if urlComponents.path == "" {
21 | urlComponents.path = "/"
22 | }
23 | // Return adjusted URL
24 | return urlComponents.url
25 | }
26 | else {
27 | return nil
28 | }
29 | }
30 |
31 |
32 | class UniqueURLArray: Sequence {
33 | fileprivate var uniqueURLs = Set()
34 | var orderedURLs = [URL]()
35 |
36 | typealias Iterator = Array.Iterator
37 | func makeIterator() -> Iterator {
38 | return orderedURLs.makeIterator()
39 | }
40 |
41 | typealias Index = Array.Index
42 | subscript(position: Index) -> Iterator.Element {
43 | return orderedURLs[position]
44 | }
45 |
46 | var count: Int {
47 | return uniqueURLs.count
48 | }
49 |
50 | func contains(_ URL: Foundation.URL) -> Bool {
51 | if let URL = conformURL(URL) {
52 | return uniqueURLs.contains(URL)
53 | }
54 |
55 | return false
56 | }
57 |
58 | func insertReturningConformedURLIfNew(_ URL: Foundation.URL) -> Foundation.URL? {
59 | if let URL = conformURL(URL) {
60 | if !uniqueURLs.contains(URL) {
61 | uniqueURLs.insert(URL)
62 | orderedURLs.append(URL)
63 | return URL
64 | }
65 | }
66 |
67 | return nil
68 | }
69 |
70 | func remove(_ URL: Foundation.URL) {
71 | if let URL = conformURL(URL) {
72 | if let setIndex = uniqueURLs.firstIndex(of: URL) {
73 | uniqueURLs.remove(at: setIndex)
74 |
75 | if let arrayIndex = orderedURLs.firstIndex(of: URL) {
76 | orderedURLs.remove(at: arrayIndex)
77 | }
78 | }
79 | }
80 | }
81 |
82 | func removeAll() {
83 | uniqueURLs.removeAll()
84 | orderedURLs.removeAll()
85 | }
86 | }
87 |
88 | extension UniqueURLArray: CustomStringConvertible {
89 | var description: String {
90 | return uniqueURLs.description
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/LanternModel/ValidatedStringValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ElementConvenience.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 28/04/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Ono
11 |
12 |
13 |
14 | public enum ValidatedStringValue {
15 | case validString(string: String)
16 | case validKeyValue(key: String, value: String)
17 | case missing
18 | case empty
19 | case notRequested
20 | case multiple([ValidatedStringValue])
21 | case invalid
22 |
23 | init(string: String?) {
24 | if let string = string {
25 | if string == "" {
26 | self = .empty
27 | }
28 | else {
29 | self = .validString(string: string)
30 | }
31 | }
32 | else {
33 | self = .missing
34 | }
35 | }
36 | }
37 |
38 | private let whitespaceCharacterSet = CharacterSet.whitespacesAndNewlines
39 |
40 | extension ValidatedStringValue {
41 | init(string: String, trimmingSpace: Bool, combineSpaces: Bool = false) {
42 | var string = string
43 | if combineSpaces {
44 | string = string.replacingOccurrences(of: "[\\s]+", with: " ", options: .regularExpression, range: nil)
45 | }
46 | // Trim whitespace from ends
47 | string = string.trimmingCharacters(in: whitespaceCharacterSet)
48 |
49 | self.init(string: string)
50 | }
51 | }
52 |
53 | extension ValidatedStringValue {
54 | static func validateContentOfElement(_ element: ONOXMLElement) -> ValidatedStringValue {
55 | let stringValue = element.stringValue()
56 |
57 | return self.init(string: stringValue!, trimmingSpace: true, combineSpaces: true)
58 | }
59 |
60 | static func validateContentOfElements(_ elements: [ONOXMLElement]) -> ValidatedStringValue {
61 | switch elements.count {
62 | case 1:
63 | let element = elements[0]
64 | return validateContentOfElement(element)
65 | case 0:
66 | return .missing
67 | default:
68 | return .multiple(elements.map { element in
69 | return self.validateContentOfElement(element)
70 | })
71 | }
72 | }
73 |
74 | static func validateAttribute(_ attribute: String, ofElement element: ONOXMLElement) -> ValidatedStringValue {
75 | if let stringValue = element[attribute] as? String {
76 | return self.init(string: stringValue, trimmingSpace: true)
77 | }
78 | else {
79 | return .missing
80 | }
81 | }
82 |
83 | static func validate(keyAttribute: String, valueAttribute: String, of element: ONOXMLElement) -> ValidatedStringValue {
84 | let key = element[keyAttribute] as? String ?? ""
85 | let value = element[valueAttribute] as? String ?? ""
86 | return .validKeyValue(key: key, value: value)
87 | }
88 |
89 | static func validateAttribute(_ attribute: String, ofElements elements: [ONOXMLElement]) -> ValidatedStringValue {
90 | switch elements.count {
91 | case 1:
92 | let element = elements[0]
93 | return validateAttribute(attribute, ofElement: element)
94 | case 0:
95 | return .missing
96 | default:
97 | let values: [ValidatedStringValue] = elements.map { element in
98 | return self.validateAttribute(attribute, ofElement: element)
99 | }
100 | return .multiple(values)
101 | }
102 | }
103 |
104 | static func validate(keyAttribute: String, valueAttribute: String, of elements: [ONOXMLElement]) -> ValidatedStringValue {
105 | switch elements.count {
106 | case 1:
107 | return validate(keyAttribute: keyAttribute, valueAttribute: valueAttribute, of: elements[0])
108 | case 0:
109 | return .missing
110 | default:
111 | return .multiple(elements.map {
112 | validate(keyAttribute: keyAttribute, valueAttribute: valueAttribute, of: $0)
113 | })
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/LanternModel/ValueStorable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ValueStorable.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 26/07/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | protocol ValueStorable {
13 | subscript(key: String) -> Any? { get set }
14 | }
15 |
16 | internal protocol ValueStorableUpdater {
17 | init?(fromStorable storable: ValueStorable)
18 |
19 | func updateStorable(_ storable: inout ValueStorable)
20 | }
21 |
22 |
23 |
24 | struct RecordJSON: ValueStorable {
25 | typealias Dictionary = [String: Any]
26 | var dictionary: Dictionary
27 |
28 | subscript(key: String) -> Any? {
29 | get {
30 | return dictionary[key]
31 | }
32 | set {
33 | dictionary[key] = newValue
34 | }
35 | }
36 | }
37 |
38 | extension RecordJSON {
39 | init() {
40 | self.init(dictionary: Dictionary())
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LanternModel/ValueValidation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ValueValidation.swift
3 | // Hoverlytics
4 | //
5 | // Created by Patrick Smith on 31/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | private let whitespaceCharacterSet = CharacterSet.whitespacesAndNewlines
13 |
14 | public enum ValidationError : Error, LocalizedError, CustomNSError {
15 | case stringIsEmpty(string: String, identifier: String)
16 |
17 | case urlStringIsInvalid(string: String, identifier: String)
18 |
19 | public static let errorDomain = "LanternModel.ValueValidation.errorDomain"
20 |
21 | fileprivate enum ErrorCode : Int {
22 | case stringIsEmpty = 10
23 |
24 | case urlStringIsInvalid = 20
25 | }
26 |
27 | public var errorCode: Int {
28 | switch self {
29 | case .stringIsEmpty:
30 | return ErrorCode.stringIsEmpty.rawValue
31 | case .urlStringIsInvalid:
32 | return ErrorCode.urlStringIsInvalid.rawValue
33 | }
34 | }
35 |
36 | public var errorDescription: String? {
37 | switch self {
38 | case .stringIsEmpty(_, let identifier):
39 | return "Please enter something for \"\(identifier)\""
40 | case .urlStringIsInvalid(_, let identifier):
41 | return "Please enter a valid URL for \"\(identifier)\""
42 | }
43 | }
44 |
45 | public static func validateString(_ string: String, identifier: String) throws -> String {
46 | let string = string.trimmingCharacters(in: whitespaceCharacterSet)
47 | if string.isEmpty {
48 | throw self.stringIsEmpty(string: string, identifier: identifier)
49 | }
50 |
51 | return string
52 | }
53 |
54 | public static func validate(urlString: String, identifier: String) throws -> URL {
55 | do {
56 | let urlString = try validateString(urlString, identifier: identifier)
57 |
58 | guard let url = detectWebURL(fromString: urlString) else {
59 | throw self.urlStringIsInvalid(string: urlString, identifier: identifier)
60 | }
61 |
62 | return url
63 | }
64 | }
65 | }
66 |
67 |
68 | public func detectWebURL(fromString URLString: String) -> URL? {
69 | let dataDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
70 |
71 | if let result = dataDetector?.firstMatch(in: URLString, options: NSRegularExpression.MatchingOptions(), range: NSMakeRange(0, (URLString as NSString).length)) {
72 | return result.url
73 | }
74 | else {
75 | return nil
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/LanternModelTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.burntcaramel.Lantern.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/LanternModelTests/LanternModelTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LanternModelTests.swift
3 | // LanternModelTests
4 | //
5 | // Created by Patrick Smith on 30/03/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import XCTest
11 | import LanternModel
12 |
13 | class LanternModelTests: XCTestCase {
14 |
15 | override func setUp() {
16 | super.setUp()
17 | // Put setup code here. This method is called before the invocation of each test method in the class.
18 | }
19 |
20 | override func tearDown() {
21 | // Put teardown code here. This method is called after the invocation of each test method in the class.
22 | super.tearDown()
23 | }
24 |
25 | func testDetectWebURL() {
26 | XCTAssertEqual(detectWebURL(fromString: "https://www.burntcaramel.com/"), URL(string: "https://www.burntcaramel.com/"))
27 |
28 | XCTAssertEqual(detectWebURL(fromString: "https://www.burntcaramel.com"), URL(string: "https://www.burntcaramel.com"))
29 |
30 | XCTAssertEqual(detectWebURL(fromString: "www.burntcaramel.com"), URL(string: "http://www.burntcaramel.com"))
31 |
32 | XCTAssertEqual(detectWebURL(fromString: "burntcaramel.com"), URL(string: "http://burntcaramel.com"))
33 | }
34 |
35 | func testPerformanceExample() {
36 | // This is an example of a performance test case.
37 | self.measure() {
38 | // Put the code you want to measure the time of here.
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/LanternTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/LanternTests/LanternTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LanternTests.swift
3 | // LanternTests
4 | //
5 | // Created by Patrick Smith on 2/05/2015.
6 | // Copyright (c) 2015 Burnt Caramel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import XCTest
11 |
12 | class LanternTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | XCTAssert(true, "Pass")
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure() {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: update
2 | update:
3 | carthage update --platform OSX
4 |
5 | .PHONY: test
6 | test:
7 | xcodebuild test -scheme "Lantern Model"
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lantern
2 |
3 | Crawl and audit websites. Written in Swift 5.
4 |
5 | ## Links
6 |
7 | [Download on the Mac App Store](https://itunes.apple.com/us/app/lantern-website-crawler-for/id991526452?ls=1&mt=12)
8 |
9 | [Home Website](https://icing.space/tools/lantern/)
10 |
11 | [Back on Open Collective](https://opencollective.com/lantern)
12 |
13 | ## Libraries
14 |
15 | Uses:
16 | - [Grain data flow for Swift](https://github.com/BurntCaramel/Grain)
17 | - [BurntCocoaUI](https://github.com/BurntCaramel/BurntCocoaUI)
18 | - [BurntFoundation](https://github.com/BurntCaramel/BurntFoundation)
19 | - [WebKit](https://www.webkit.org)
20 | - [Alamofire](https://github.com/Alamofire/Alamofire)
21 | - [Ono](https://github.com/mattt/Ono)
22 |
--------------------------------------------------------------------------------