├── WebShell
├── Assets.xcassets
│ ├── Contents.json
│ ├── MenuIcon.imageset
│ │ ├── -webpage.png
│ │ ├── -webpage-1.png
│ │ └── Contents.json
│ ├── Udemy.appiconset
│ │ ├── icon_16x16.png
│ │ ├── icon_32x32.png
│ │ ├── icon_128x128.png
│ │ ├── icon_16x16@2x.png
│ │ ├── icon_256x256.png
│ │ ├── icon_32x32@2x.png
│ │ ├── icon_512x512.png
│ │ ├── icon_128x128@2x.png
│ │ ├── icon_256x256@2x.png
│ │ ├── icon_512x512@2x.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ ├── fileicon-1024.png
│ │ ├── fileicon-514.png
│ │ └── Contents.json
├── Core
│ ├── WSWindow.swift
│ ├── WSEventMonitor.swift
│ ├── WSCore.swift
│ ├── WSFileHandler.swift
│ ├── WSStringExtension.swift
│ ├── WSViewController.swift
│ ├── WSDownloadManager.swift
│ ├── WSPasswordManager.swift
│ ├── WSBaseSettings.swift
│ ├── WSPageActions.swift
│ ├── WSWebViewFunctions.swift
│ ├── WSApplication.swift
│ ├── WSCustomInject.swift
│ ├── WSTrackpadGestures.swift
│ ├── WSAppDelegate.swift
│ ├── WSInjector.swift
│ └── WSDebug.swift
├── Sites
│ ├── Udemy
│ │ └── Settings.swift
│ └── WebShell
│ │ └── Settings.swift
├── Credits.rtf
├── Udemy-Info.plist
├── WebShell-Info.plist
├── WSCore.swift
├── WSFileHandler.swift
├── WSStringExtension.swift
├── Support
│ ├── Battery.swift
│ ├── Notifications.swift
│ └── navigator_geolocation_getCurrentPosition.swift
├── Settings.swift
├── WSDownloadManager.swift
├── WSTrackpadGestures.swift
├── WSPageActions.swift
├── nl.lproj
│ └── Main.strings
├── WSInjector.swift
└── WSDebug.swift
├── contributing.md
├── readme.md
├── .gitignore
└── WebShell.xcodeproj
└── project.pbxproj
/WebShell/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/MenuIcon.imageset/-webpage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/MenuIcon.imageset/-webpage.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_16x16.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_32x32.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/MenuIcon.imageset/-webpage-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/MenuIcon.imageset/-webpage-1.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_128x128.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_256x256.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_512x512.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/AppIcon.appiconset/fileicon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/AppIcon.appiconset/fileicon-1024.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/AppIcon.appiconset/fileicon-514.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/AppIcon.appiconset/fileicon-514.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/WebShell/HEAD/WebShell/Assets.xcassets/Udemy.appiconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/WebShell/Core/WSWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WSWindow.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 04/01/2018.
6 | // Copyright © 2018 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Cocoa
11 | import AppKit
12 |
13 | class WSNSWindow : NSWindow {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/MenuIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "filename" : "-webpage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "filename" : "-webpage-1.png",
11 | "scale" : "2x"
12 | }
13 | ],
14 | "info" : {
15 | "version" : 1,
16 | "author" : "xcode"
17 | },
18 | "properties" : {
19 | "template-rendering-intent" : "template"
20 | }
21 | }
--------------------------------------------------------------------------------
/WebShell/Sites/Udemy/Settings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Settings.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 23-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Settings: WSBaseSettings {
12 | static let shared = Settings()
13 |
14 | override private init() {
15 | super.init()
16 | // Override default settings for this particular target
17 | self.url = "http://udemy.com"
18 |
19 | // Save last URL
20 | self.openLastUrl = true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing / Making issues
2 | If you experience problems with a specific web(app)
3 |
4 | *(visible by running via Xcode or by running with parameter -debug true)*
5 |
6 | Please use the `Debug` -> `Report an issue on this page` button, so we know the full url.
7 |
8 | 
9 |
10 | When pressed the report your **default** webbrowser will open and redirect you to the github issues page, with some pre-filled fields, then you can enter what exactly the problem is.
--------------------------------------------------------------------------------
/WebShell/Credits.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf200
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\expandedcolortbl;;}
5 | \margl1440\margr1440\vieww9000\viewh8400\viewkind0
6 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
7 |
8 | \f0\fs24 \cf0 WebShell is a powerful application which makes it easy to integrate your web-app to a fully working app.\
9 | WebShell is created with help of:\
10 | Randy - https://github.com/djyde \
11 | Wesley - https://github.com/wdg\
12 | DreamPiggy - https://github.com/lizhuoli1126\
13 | Viralis - https://github.com/viralis\
14 | Visoar - https://github.com/Visoar}
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "512x512",
45 | "idiom" : "mac",
46 | "filename" : "fileicon-514.png",
47 | "scale" : "1x"
48 | },
49 | {
50 | "size" : "512x512",
51 | "idiom" : "mac",
52 | "filename" : "fileicon-1024.png",
53 | "scale" : "2x"
54 | }
55 | ],
56 | "info" : {
57 | "version" : 1,
58 | "author" : "xcode"
59 | }
60 | }
--------------------------------------------------------------------------------
/WebShell/Core/WSEventMonitor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellEventMonitor.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 26-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | // @wdg Merge Statut with WebShell.
12 | // Issue: #56
13 | class EventMonitor {
14 | fileprivate var monitor: Any?
15 | fileprivate let mask: NSEvent.EventTypeMask
16 | fileprivate let handler: (NSEvent?) -> ()
17 |
18 | /**
19 | Init monitoring for events
20 |
21 | - Parameter mask: under which mask?
22 | - Parameter handler: with which handler?
23 | */
24 | internal init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> ()) {
25 | self.mask = mask
26 | self.handler = handler
27 | }
28 |
29 | deinit {
30 | stop()
31 | }
32 |
33 | /**
34 | Starts monitoring for events
35 | */
36 | internal func start() {
37 | monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler)
38 | }
39 |
40 | /**
41 | Removes event monitor
42 | */
43 | internal func stop() {
44 | if monitor != nil {
45 | NSEvent.removeMonitor(monitor!)
46 | monitor = nil
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/WebShell/Udemy-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 | 0.2.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 201605242200
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 |
32 | NSHumanReadableCopyright
33 | Copyright © 2015 RandyLu. All rights reserved.
34 | NSMainStoryboardFile
35 | Main
36 | NSPrincipalClass
37 | WSApplication
38 |
39 |
40 |
--------------------------------------------------------------------------------
/WebShell/WebShell-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 | 0.2.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 201605242200
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 |
32 | NSHumanReadableCopyright
33 | Copyright © 2015 RandyLu. All rights reserved.
34 | NSMainStoryboardFile
35 | Main
36 | NSPrincipalClass
37 | WSApplication
38 |
39 |
40 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # WebShell
2 |
3 | WebShell is an OS X WebView shell, which help you easily bundle the Web Apps to native OS X app without coding.
4 |
5 | 
6 |
7 | 
8 |
9 | ## Requirements
10 |
11 | - Xcode 7+ (Swift 2.0+ support)
12 |
13 | ## Quick Start
14 |
15 | ```bash
16 |
17 | $ git clone https://github.com/djyde/WebShell.git APP_NAME
18 |
19 | $ cd APP_NAME && open WebShell.xcodeproj
20 |
21 | ```
22 |
23 | Edit `Sites/WebShell/Settings.swiftt` and change the url whatever you like.
24 |
25 |
26 | Finally click the `run` button to run the app.
27 |
28 | ## Bonus Features
29 |
30 | - Standard [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/notification) support
31 |
32 | - Standard [Battery Status API](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API) support
33 |
34 | ## Demo
35 |
36 | - [JS Bin](http://7mnoy7.com1.z0.glb.clouddn.com/github/JSBin.zip)
37 |
38 | - [StackEdit](http://7mnoy7.com1.z0.glb.clouddn.com/github/StackEdit.zip) - A markdown editor
39 |
40 | ## Document
41 |
42 | For more detail configurations, please see [document](https://github.com/djyde/WebShell/wiki/How-to-build-a-WebShell-based-application)
43 |
44 | ## Who's using WebShell
45 |
46 | If you built any wonderful app with `WebShell`, just let me know!
47 |
48 | # License
49 |
50 | MIT License
51 |
--------------------------------------------------------------------------------
/WebShell/Assets.xcassets/Udemy.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "Iconizer",
4 | "version" : "1"
5 | },
6 | "images" : [
7 | {
8 | "size" : "512x512",
9 | "filename" : "icon_512x512@2x.png",
10 | "idiom" : "mac",
11 | "scale" : "2x"
12 | },
13 | {
14 | "size" : "512x512",
15 | "filename" : "icon_512x512.png",
16 | "idiom" : "mac",
17 | "scale" : "1x"
18 | },
19 | {
20 | "size" : "256x256",
21 | "filename" : "icon_256x256@2x.png",
22 | "idiom" : "mac",
23 | "scale" : "2x"
24 | },
25 | {
26 | "size" : "256x256",
27 | "filename" : "icon_256x256.png",
28 | "idiom" : "mac",
29 | "scale" : "1x"
30 | },
31 | {
32 | "size" : "128x128",
33 | "filename" : "icon_128x128@2x.png",
34 | "idiom" : "mac",
35 | "scale" : "2x"
36 | },
37 | {
38 | "size" : "128x128",
39 | "filename" : "icon_128x128.png",
40 | "idiom" : "mac",
41 | "scale" : "1x"
42 | },
43 | {
44 | "size" : "32x32",
45 | "filename" : "icon_32x32@2x.png",
46 | "idiom" : "mac",
47 | "scale" : "2x"
48 | },
49 | {
50 | "size" : "32x32",
51 | "filename" : "icon_32x32.png",
52 | "idiom" : "mac",
53 | "scale" : "1x"
54 | },
55 | {
56 | "size" : "16x16",
57 | "filename" : "icon_16x16@2x.png",
58 | "idiom" : "mac",
59 | "scale" : "2x"
60 | },
61 | {
62 | "size" : "16x16",
63 | "filename" : "icon_16x16.png",
64 | "idiom" : "mac",
65 | "scale" : "1x"
66 | }
67 | ]
68 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Swift ###
2 | # Xcode
3 | #
4 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
5 |
6 | ## Build generated
7 | build/
8 | DerivedData/
9 |
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata/
20 |
21 | ## Other
22 | *.moved-aside
23 | *.xccheckout
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 | *.dSYM.zip
30 | *.dSYM
31 |
32 | ## Playgrounds
33 | timeline.xctimeline
34 | playground.xcworkspace
35 |
36 | # Swift Package Manager
37 | #
38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
39 | # Packages/
40 | # Package.pins
41 | .build/
42 |
43 | # CocoaPods - Refactored to standalone file
44 |
45 | # Carthage - Refactored to standalone file
46 |
47 | # fastlane
48 | #
49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
50 | # screenshots whenever they are needed.
51 | # For more information about the recommended setup visit:
52 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
53 |
54 | fastlane/report.xml
55 | fastlane/Preview.html
56 | fastlane/screenshots
57 | fastlane/test_output
58 |
59 | ### Xcode ###
60 | # Xcode
61 | #
62 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
63 |
64 | ## Build generated
65 |
66 | ## Various settings
67 |
68 | ## Other
69 |
70 | ### Xcode Patch ###
71 | *.xcodeproj/*
72 | !*.xcodeproj/project.pbxproj
73 | !*.xcodeproj/xcshareddata/
74 | !*.xcworkspace/contents.xcworkspacedata
75 | /*.gcno
--------------------------------------------------------------------------------
/WebShell/Core/WSCore.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellCore.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Cocoa
11 |
12 | extension WSViewController {
13 | /**
14 | Quit the app (there must be a better way)
15 | */
16 | func Quit(_ sender: AnyObject) {
17 | exit(0)
18 | }
19 |
20 | /**
21 | Function to call for the window.open (popup)
22 |
23 | - Parameter url: The url to open
24 | - Parameter height: The height for the window
25 | - Parameter width: The width for the window
26 | */
27 | func openNewWindow(url: String, height: String, width: String) -> Void {
28 | // @wdg Replaced NSPipe for NSWorkspace
29 | // Issue: #48
30 | let ws = NSWorkspace.shared
31 | do {
32 | if settings.debugmode {
33 | try ws.launchApplication(at: URL(string: "file://\(CommandLine.arguments[0])")!, options: NSWorkspace.LaunchOptions.newInstance, configuration: [NSWorkspace.LaunchConfigurationKey.arguments: ["-NSDocumentRevisionsDebugMode", "YES", "-url", url, "-height", height, "-width", width]])
34 | } else {
35 | try ws.launchApplication(at: URL(string: CommandLine.arguments[0])!, options: NSWorkspace.LaunchOptions.newInstance, configuration: [NSWorkspace.LaunchConfigurationKey.arguments: ["-url", url, "-height", height, "-width", width]])
36 | }
37 | }
38 | catch { /* we'll never get this. */ }
39 | }
40 |
41 | /**
42 | Noop a.k.a. No operation.
43 |
44 | - Parameter ob: Any ...
45 | */
46 | func noop(_ ob: Any ...) -> Void { }
47 |
48 | /**
49 | Delay a function
50 |
51 | - Parameter delay: Time to delay
52 | - Parameter closure: Code to run (in a escaping block)
53 | */
54 | func delay(_ delay: Double, _ closure: @escaping () -> ()) {
55 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
56 | }
57 |
58 | /**
59 | Run on main thread.
60 |
61 | - Parameter run: Code to run (in a escaping block)
62 | */
63 | func runOnMain(_ run: @escaping () -> ()) {
64 | DispatchQueue.main.async(execute: run)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/WebShell/WSCore.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellCore.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Cocoa
11 |
12 | extension ViewController {
13 | /**
14 | Quit the app (there must be a better way)
15 | */
16 | func Quit(_ sender: AnyObject) {
17 | exit(0)
18 | }
19 |
20 | /**
21 | Function to call for the window.open (popup)
22 |
23 | - Parameter url: The url to open
24 | - Parameter height: The height for the window
25 | - Parameter width: The width for the window
26 | */
27 | func openNewWindow(url: String, height: String, width: String) -> Void {
28 | // @wdg Replaced NSPipe for NSWorkspace
29 | // Issue: #48
30 | let ws = NSWorkspace.shared
31 | do {
32 | if (WebShellSettings["debugmode"] as! Bool) {
33 | try ws.launchApplication(at: URL(string: "file://\(CommandLine.arguments[0])")!, options: NSWorkspace.LaunchOptions.newInstance, configuration: [NSWorkspace.LaunchConfigurationKey.arguments: ["-NSDocumentRevisionsDebugMode", "YES", "-url", url, "-height", height, "-width", width]])
34 | } else {
35 | try ws.launchApplication(at: URL(string: CommandLine.arguments[0])!, options: NSWorkspace.LaunchOptions.newInstance, configuration: [NSWorkspace.LaunchConfigurationKey.arguments: ["-url", url, "-height", height, "-width", width]])
36 | }
37 | }
38 | catch { /* we'll never get this. */ }
39 | }
40 |
41 | /**
42 | Noop a.k.a. No operation.
43 |
44 | - Parameter ob: Any ...
45 | */
46 | func noop(_ ob: Any ...) -> Void { }
47 |
48 | /**
49 | Delay a function
50 |
51 | - Parameter delay: Time to delay
52 | - Parameter closure: Code to run (in a escaping block)
53 | */
54 | func delay(_ delay: Double, _ closure: @escaping () -> ()) {
55 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
56 | }
57 |
58 | /**
59 | Run on main thread.
60 |
61 | - Parameter run: Code to run (in a escaping block)
62 | */
63 | func runOnMain(_ run: @escaping () -> ()) {
64 | DispatchQueue.main.async(execute: run)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/WebShell/Core/WSFileHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellFileHandler.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 | // @wdg: Enable file uploads.
13 | // Issue: #29
14 | // This extension will handle up & downloads
15 | extension WSViewController {
16 |
17 | // @wdg: Enable file uploads.
18 | // Issue: #29
19 | @objc(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:) func webView(_ sender: WebView!, runOpenPanelForFileButtonWith resultListener: WebOpenPanelResultListener!, allowMultipleFiles: Bool) {
20 | // Init panel with options
21 | let panel = NSOpenPanel()
22 | panel.allowsMultipleSelection = allowMultipleFiles
23 | panel.canChooseDirectories = false
24 | panel.canCreateDirectories = false
25 | panel.canChooseFiles = true
26 |
27 | // On clicked on ok then...
28 | panel.begin {(result) -> Void in
29 | // User clicked OK
30 | if result.rawValue == NSFileHandlingPanelOKButton {
31 |
32 | // make the upload qeue named 'uploadQeue'
33 | let uploadQeue: NSMutableArray = NSMutableArray()
34 | for i in 0 ..< panel.urls.count
35 | {
36 | // Add to upload qeue, needing relativePath.
37 | uploadQeue.add(panel.urls[i].relativePath)
38 | }
39 |
40 | if (panel.urls.count == 1) {
41 | // One file
42 | resultListener.chooseFilename(String(describing: uploadQeue[0]))
43 | } else {
44 | // Multiple files
45 | resultListener.chooseFilenames(uploadQeue as [AnyObject])
46 | }
47 | }
48 | }
49 |
50 | }
51 |
52 | /**
53 | Download window.
54 |
55 | - Parameter download: WebDownload!
56 | */
57 | func downloadWindow(forAuthenticationSheet download: WebDownload!) -> NSWindow! {
58 | print("I'd like to download something")
59 | print(download)
60 |
61 | return NSWindow()
62 | }
63 |
64 | // Usefull for debugging..
65 | @nonobjc func webView(_ sender: WebView!,mouseDidMoveOverElement elementInformation: [NSObject : Any]!, modifierFlags: Int) {
66 | //print("Sender=\(sender)\nEleInfo=\(elementInformation)\nModifier=\(modifierFlags)")
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/WebShell/WSFileHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellFileHandler.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 | // @wdg: Enable file uploads.
13 | // Issue: #29
14 | // This extension will handle up & downloads
15 | extension ViewController {
16 |
17 | // @wdg: Enable file uploads.
18 | // Issue: #29
19 | @objc(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:) func webView(_ sender: WebView!, runOpenPanelForFileButtonWith resultListener: WebOpenPanelResultListener!, allowMultipleFiles: Bool) {
20 | // Init panel with options
21 | let panel = NSOpenPanel()
22 | panel.allowsMultipleSelection = allowMultipleFiles
23 | panel.canChooseDirectories = false
24 | panel.canCreateDirectories = false
25 | panel.canChooseFiles = true
26 |
27 | // On clicked on ok then...
28 | panel.begin {(result) -> Void in
29 | // User clicked OK
30 | if result.rawValue == NSFileHandlingPanelOKButton {
31 |
32 | // make the upload qeue named 'uploadQeue'
33 | let uploadQeue: NSMutableArray = NSMutableArray()
34 | for i in 0 ..< panel.urls.count
35 | {
36 | // Add to upload qeue, needing relativePath.
37 | uploadQeue.add(panel.urls[i].relativePath)
38 | }
39 |
40 | if (panel.urls.count == 1) {
41 | // One file
42 | resultListener.chooseFilename(String(describing: uploadQeue[0]))
43 | } else {
44 | // Multiple files
45 | resultListener.chooseFilenames(uploadQeue as [AnyObject])
46 | }
47 | }
48 | }
49 |
50 | }
51 |
52 | /**
53 | Download window.
54 |
55 | - Parameter download: WebDownload!
56 | */
57 | func downloadWindow(forAuthenticationSheet download: WebDownload!) -> NSWindow! {
58 | print("I'd like to download something")
59 | print(download)
60 |
61 | return NSWindow.init()
62 | }
63 |
64 | // Usefull for debugging..
65 | @nonobjc func webView(_ sender: WebView!,mouseDidMoveOverElement elementInformation: [NSObject : Any]!, modifierFlags: Int) {
66 | //print("Sender=\(sender)\nEleInfo=\(elementInformation)\nModifier=\(modifierFlags)")
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/WebShell/Core/WSStringExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtension.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 17-01-16.
6 | // Copyright © 2016 WDGWV. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | Extensions for Strings
13 | */
14 | public extension String {
15 | /**
16 | get string length
17 | */
18 | public var length: Int {
19 | get {
20 | return self.count
21 | }
22 | }
23 |
24 | /**
25 | contains
26 | - Parameter s: String to check
27 | - Returns: true/false
28 | */
29 | public func contains(_ s: String) -> Bool {
30 | return self.range(of: s) != nil ? true : false
31 | }
32 |
33 | /**
34 | Replace
35 | - Parameter target: String
36 | - Parameter withString: Replacement
37 | - Returns: Replaced string
38 | */
39 | public func replace(_ target: String, withString: String) -> String {
40 | return self.replacingOccurrences(of: target, with: withString, options: NSString.CompareOptions.literal, range: nil)
41 | }
42 |
43 | /**
44 | Character At Index
45 | - Parameter index: The index
46 | - Returns Character
47 | */
48 | func characterAtIndex(_ index: Int) -> Character! {
49 | var cur = 0
50 | for char in self {
51 | if cur == index {
52 | return char
53 | }
54 | cur += 1
55 | }
56 | return nil
57 | }
58 |
59 | /**
60 | Add subscript
61 |
62 | - Parameter i: Index
63 | */
64 | public subscript(i: Int) -> Character {
65 | get {
66 | let index = self.index(self.startIndex, offsetBy: i)
67 | return self[index]
68 | }
69 | }
70 | /**
71 | Add subscript
72 |
73 | - Parameter r: Range
74 | */
75 | public subscript(r: Range) -> String {
76 | get {
77 | let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
78 | let endIndex = self.index(self.startIndex, offsetBy: r.upperBound - 1)
79 |
80 | return String(self[startIndex..) -> String {
91 | get {
92 | let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
93 | let endIndex = self.index(self.startIndex, offsetBy: r.upperBound - 1)
94 |
95 | return String(self[startIndex.. Bool {
30 | return self.range(of: s) != nil ? true : false
31 | }
32 |
33 | /**
34 | Replace
35 | - Parameter target: String
36 | - Parameter withString: Replacement
37 | - Returns: Replaced string
38 | */
39 | public func replace(_ target: String, withString: String) -> String {
40 | return self.replacingOccurrences(of: target, with: withString, options: NSString.CompareOptions.literal, range: nil)
41 | }
42 |
43 | /**
44 | Character At Index
45 | - Parameter index: The index
46 | - Returns Character
47 | */
48 | func characterAtIndex(_ index: Int) -> Character! {
49 | var cur = 0
50 | for char in self.characters {
51 | if cur == index {
52 | return char
53 | }
54 | cur += 1
55 | }
56 | return nil
57 | }
58 |
59 | /**
60 | Add subscript
61 |
62 | - Parameter i: Index
63 | */
64 | public subscript(i: Int) -> Character {
65 | get {
66 | let index = self.characters.index(self.startIndex, offsetBy: i)
67 | return self[index]
68 | }
69 | }
70 | /**
71 | Add subscript
72 |
73 | - Parameter r: Range
74 | */
75 | public subscript(r: Range) -> String {
76 | get {
77 | let startIndex = self.characters.index(self.startIndex, offsetBy: r.lowerBound)
78 | let endIndex = self.characters.index(self.startIndex, offsetBy: r.upperBound - 1)
79 |
80 | return String(self[startIndex..) -> String {
91 | get {
92 | let startIndex = self.characters.index(self.startIndex, offsetBy: r.lowerBound)
93 | let endIndex = self.characters.index(self.startIndex, offsetBy: r.upperBound - 1)
94 |
95 | return String(self[startIndex.. BatteryManager
19 | }
20 |
21 | @objc class BatteryManager : NSObject, BatteryManagerJSExports {
22 | internal var level: Double
23 | internal var chargingTime: Int
24 |
25 | dynamic var charging: Bool
26 | // dynamic var chargingTime: Int
27 | dynamic var dischargingTime: Int
28 | // dynamic var level: Double
29 |
30 | override init() {
31 | self.charging = true
32 | self.chargingTime = 0
33 | self.dischargingTime = 999
34 | self.level = 1.0
35 | }
36 |
37 | class func getBattery() -> BatteryManager {
38 | // Object to export to JavaScript
39 | let battery = BatteryManager()
40 |
41 | // Use IOKit to get battery infomation
42 | let blob = IOPSCopyPowerSourcesInfo().takeRetainedValue()
43 | let sources = IOPSCopyPowerSourcesList(blob).takeRetainedValue()
44 | let sourceArray:NSArray = sources
45 | if (sourceArray.count == 0) { // Could not retrieve battery information.
46 | return battery
47 | } else {
48 | let batterySource = sourceArray.object(at: 0) // just use first battery
49 | let pSource = IOPSGetPowerSourceDescription(blob, batterySource as CFTypeRef!).takeUnretainedValue()
50 |
51 | let batteryDic:NSDictionary = pSource
52 |
53 | let isCharge = batteryDic.object(forKey: kIOPSIsChargingKey) as! Int // 1 for charging, 0 for not
54 | let curCapacity = batteryDic.object(forKey: kIOPSCurrentCapacityKey) as! Int // current capacity
55 | let maxCapacity = batteryDic.object(forKey: kIOPSMaxCapacityKey) as! Int // max capacity
56 | let chargingTime = batteryDic.object(forKey: kIOPSTimeToEmptyKey) as! Int // time to empty(not charging)
57 | let dischargingTime = batteryDic.object(forKey: kIOPSTimeToFullChargeKey) as! Int // time to full(charging)
58 | let level = Double(curCapacity) / Double(maxCapacity) // current level
59 |
60 | battery.charging = isCharge == 1 ? true : false
61 | battery.chargingTime = chargingTime
62 | battery.dischargingTime = dischargingTime
63 | battery.level = level
64 |
65 | return battery
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/WebShell/Support/Notifications.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Notifications.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 | import AudioToolbox
12 |
13 | // @wdg Add Notification Support
14 | // Issue: #2
15 | // This extension will handle the HTML5 Notification API.
16 | extension WSViewController {
17 | func clearNotificationCount() -> Void {
18 | notificationCount = 0
19 | }
20 |
21 | // @wdg Add Notification Support
22 | // Issue: #2
23 | func makeNotification(_ title: NSString, message: NSString, icon: NSString) -> Void {
24 | let notification: NSUserNotification = NSUserNotification() // Set up Notification
25 |
26 | // If has no message (title = message)
27 | if (message.isEqual(to: "undefined")) {
28 | notification.title = Bundle.main.infoDictionary!["CFBundleName"] as? String // Use App name!
29 | notification.informativeText = title as String // Title = string
30 | } else {
31 | notification.title = title as String // Title = string
32 | notification.informativeText = message as String // Message = string
33 | }
34 |
35 |
36 | notification.soundName = NSUserNotificationDefaultSoundName // Default sound
37 | notification.deliveryDate = Date(timeIntervalSinceNow: 0) // Now!
38 | notification.actionButtonTitle = "Close"
39 |
40 | // Notification has a icon, so add it!
41 | if (!icon.isEqual(to: "undefined")) {
42 | notification.contentImage = NSImage(contentsOf: URL(string: icon as String)!) ;
43 | }
44 |
45 | let notificationcenter: NSUserNotificationCenter? = NSUserNotificationCenter.default // Notification centre
46 | notificationcenter?.scheduleNotification(notification) // Pushing to notification centre
47 |
48 | notificationCount += 1
49 |
50 | NSApplication.shared.dockTile.badgeLabel = String(notificationCount)
51 | }
52 |
53 | // @wdg Add Notification Support
54 | // Issue: #2
55 | func flashScreen(_ data: NSString) -> Void {
56 | if ((Int(data as String)) != nil || data.isEqual(to: "undefined")) {
57 | AudioServicesPlaySystemSound(kSystemSoundID_FlashScreen) ;
58 | } else {
59 | let time: [String] = (data as String).components(separatedBy: ",")
60 | for i in 0 ..< time.count {
61 | // @wdg Fix flashScreen(...)
62 | // Issue: #66
63 | let timeAsNumber = NumberFormatter().number(from: time[i])?.intValue
64 | Timer.scheduledTimer(timeInterval: TimeInterval(timeAsNumber!), target: self, selector: #selector(WSViewController.flashScreenNow), userInfo: nil, repeats: false)
65 | }
66 | }
67 | }
68 |
69 | // @wdg Add Notification Support
70 | // Issue: #2
71 | @objc func flashScreenNow() -> Void {
72 | AudioServicesPlaySystemSound(kSystemSoundID_FlashScreen) ;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/WebShell/Core/WSViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WSViewController.swift
3 | // WebShell
4 | //
5 | // Created by Randy on 15/12/19.
6 | // Copyright © 2015 RandyLu. All rights reserved.
7 | //
8 | // Wesley de Groot (@wdg), Added Notification and console.log Support
9 |
10 | import Cocoa
11 | import WebKit
12 | import Foundation
13 | import AppKit
14 | import AudioToolbox
15 | import IOKit.ps
16 | import Darwin
17 | import CoreLocation
18 |
19 | // @wdg Clean up code base
20 | // Issue: #43
21 | class WSViewController: NSViewController, WebFrameLoadDelegate, WebUIDelegate, WebResourceLoadDelegate, WebPolicyDelegate, CLLocationManagerDelegate, WebDownloadDelegate, NSURLDownloadDelegate, WebEditingDelegate {
22 |
23 | @IBOutlet var mainWindow: NSView!
24 | @IBOutlet weak var mainWebview: WebView!
25 | @IBOutlet weak var launchingLabel: NSTextField!
26 | @IBOutlet weak var progressBar: NSProgressIndicator!
27 |
28 | var settings = Settings.shared
29 | var firstLoadingStarted = false
30 | var firstAppear = true
31 | var notificationCount = 0
32 | var lastURL:URL!
33 | var IElement = NSMenuItem()
34 | let locationManager = CLLocationManager()
35 | var MustCloseWindow = true
36 | var WSgestureLog: [CGFloat] = [0, 0]
37 | var twoFingersTouches: [String: NSTouch]?
38 |
39 | override func viewDidAppear() {
40 | if (firstAppear) {
41 | initWindow()
42 | }
43 | }
44 |
45 | // @wdg Possible fix for Mavericks
46 | // Issue: #18
47 | override func awakeFromNib() {
48 | // if (![self respondsToSelector:@selector(viewWillAppear)]) {
49 |
50 | if (!NSViewController().responds(to: #selector(NSViewController.viewWillAppear))) {
51 | checkSettings()
52 |
53 | let myPopup: NSAlert = NSAlert()
54 | myPopup.messageText = "Hello!"
55 | myPopup.informativeText = "You are running mavericks?"
56 | myPopup.alertStyle = NSAlert.Style.informational
57 | myPopup.addButton(withTitle: "Yes")
58 | myPopup.addButton(withTitle: "No")
59 |
60 | let res = myPopup.runModal()
61 |
62 |
63 | print("MAVERICKS \(res)")
64 |
65 | // OS X 10.9
66 | if (firstAppear) {
67 | initWindow()
68 | }
69 |
70 | mainWebview.uiDelegate = self
71 | mainWebview.resourceLoadDelegate = self
72 | mainWebview.downloadDelegate = self
73 | mainWebview.editingDelegate = self
74 | mainWebview.policyDelegate = self
75 | //WebUIDelegate
76 |
77 | addObservers()
78 | initSettings()
79 | loadUrl(settings.startURL())
80 | WSMediaLoop(self)
81 | WSinitSwipeGestures()
82 | }
83 | }
84 |
85 | override func viewDidLoad() {
86 | checkSettings()
87 | //self.view = goodView()
88 | super.viewDidLoad()
89 |
90 | mainWebview.uiDelegate = self
91 | mainWebview.resourceLoadDelegate = self
92 | mainWebview.downloadDelegate = self
93 | mainWebview.editingDelegate = self
94 | mainWebview.policyDelegate = self
95 |
96 | // WebShellSettings["WSMW"] = mainWebview;
97 |
98 | checkSettings()
99 | addObservers()
100 | initSettings()
101 | loadUrl(settings.startURL())
102 | WSMediaLoop(self)
103 | WSinitSwipeGestures()
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/WebShell/Settings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Settings.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 23-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | WebShell Class
13 |
14 | This class is the main class for WebShell.
15 | */
16 | class WebShell {
17 | /**
18 | The settings Dictionary
19 | */
20 | var Settings: [String: Any] = [
21 | // Url to browse to.
22 | "url": "http://djyde.github.io/WebShell/WebShell/",
23 |
24 | // set the app title
25 | "title": Bundle.main.infoDictionary!["CFBundleName"] as! String,
26 |
27 | // if you want to use the default one then leave it default || default = title/version based on Safari/AppleWebKit (KHTML, like Gecko)
28 | // otherwise change it to a useragent you want. (Default: "default")
29 | "useragent": "default",
30 |
31 | // Do you want to use the document title? (Default: true)
32 | "useDocumentTitle": true,
33 |
34 | // Multilanguage loading text!
35 | "launchingText": NSLocalizedString("Launching...", comment: "Launching..."),
36 |
37 | // Note that the window min height is 640 and min width is 1000 by default. You could change it in Main.storyboard
38 | "initialWindowHeight": 640,
39 | "initialWindowWidth": 1000,
40 |
41 | // Open target=_blank in a new screen? (Default: false)
42 | "openInNewScreen": false,
43 |
44 | // Do you want a loading bar? (Default: true)
45 | "showLoadingBar": true,
46 |
47 | // Add console.log support? (Default: false)
48 | "consoleSupport": false,
49 |
50 | // Does the app needs Location support (Default: false)
51 | // note: if true, then WebShell always uses location, whenever it is used or not
52 | "needLocation": false,
53 |
54 | // run the app in debug mode? (Default: false)
55 | // will be overridden by Xcode (runs with -NSDocumentRevisionsDebugMode YES)
56 | "debugmode": false,
57 |
58 | // Please paste here the JavaScript you want to load on a website (Default: "")
59 | "JSInject": "",
60 |
61 | // Please paste here the CSS you want to load on a website (Default: "")
62 | "CSSInject": "",
63 |
64 | // Enable (inject) import (JS/CSS) Folder. (Default: true)
65 | "EnableInjectImport": true,
66 |
67 | // Menubar app (right side next to clock) (Default: true)
68 | "MenuBarApp": false,
69 |
70 | // Navigate trough trackpad (back/forward) (Default: true)
71 | "navigateViaTrackpad": true,
72 |
73 | // Use a password manager (Default: true).
74 | "passwordManager": true,
75 |
76 | // Media keys settings
77 | "MediaKeys": [
78 | // Enable "Back" & "Forward"
79 | "BackAndForward": true,
80 |
81 | // Media Player support (experimental)
82 | "MediaPlayers": false
83 | ],
84 |
85 | // Contextmenu settings
86 | "Contextmenu": [
87 | // Enable "Back" & "Forward" (Default: true)
88 | "BackAndForward": true,
89 |
90 | // Enable "Download" (Default: true)
91 | "Download": true,
92 |
93 | // Enable "Reload" (Default: true)
94 | "Reload": true,
95 |
96 | // Enable "Open in a new window" (Default: true)
97 | "newWindow": true
98 | ],
99 |
100 | // Just a placeholder. (Default: true)
101 | "fake": true
102 | ]
103 | }
104 |
--------------------------------------------------------------------------------
/WebShell/Sites/WebShell/Settings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Settings.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 23-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Settings: WSBaseSettings {
12 | static let shared = Settings()
13 |
14 | override private init() {
15 | super.init()
16 | // Override default settings for this particular target
17 | self.url = "http://djyde.github.io/WebShell/WebShell/"
18 |
19 | // set the app title
20 | self.title = Bundle.main.infoDictionary!["CFBundleName"] as! String
21 |
22 | // if you want to use the default one then leave it default || default = title/version based on Safari/AppleWebKit (KHTML, like Gecko)
23 | // otherwise change it to a useragent you want. (Default: "default")
24 | self.useragent = "default"
25 |
26 | // Do you want to use the document title? (Default: true)
27 | self.useDocumentTitle = true
28 |
29 | // Multilanguage loading text!
30 | self.launchingText = NSLocalizedString("Launching...", comment: "Launching...")
31 |
32 | // Open target=_blank in a new screen? (Default: false)
33 | self.openInNewScreen = false
34 |
35 | // Do you want a loading bar? (Default: true)
36 | self.showLoadingBar = true
37 |
38 | // Add console.log support? (Default: false)
39 | self.consoleSupport = false
40 |
41 | // Does the app needs Location support (Default: false)
42 | // note: if true, then WebShell always uses location, whenever it is used or not
43 | self.needLocation = false
44 |
45 | // run the app in debug mode? (Default: false)
46 | // will be overridden by Xcode (runs with -NSDocumentRevisionsDebugMode YES)
47 | self.debugmode = false
48 |
49 | // Please paste here the JavaScript you want to load on a website (Default: "")
50 | self.jsInject = ""
51 |
52 | // Please paste here the CSS you want to load on a website (Default: "")
53 | self.cssInject = ""
54 |
55 | // Enable (inject) import (JS/CSS) Folder. (Default: true)
56 | self.enableInjectImport = true
57 |
58 | // Menubar app (right side next to clock) (Default: false)
59 | self.menuBarApp = false
60 |
61 | // Navigate trough trackpad (back/forward) (Default: true)
62 | self.navigateViaTrackpad = true
63 |
64 | // Use a password manager (Default: true).
65 | self.passwordManager = true
66 |
67 | // Media keys settings - Enable "Back" & "Forward"
68 | self.mkBackAndForward = true
69 |
70 | // Media Player support (experimental)
71 | self.mkMediaPlayers = false
72 |
73 | // Contextmenu settings - // Enable "Back" & "Forward" (Default: true)
74 | self.cmBackAndForward = true
75 |
76 | // Enable "Download" (Default: true)
77 | self.cmDownload = true
78 |
79 | // Enable "Reload" (Default: true)
80 | self.cmReload = true
81 |
82 | // Enable "Open in a new window" (Default: true)
83 | self.cmNewWindow = true
84 |
85 | // open with last url? (Default: false)
86 | self.openLastUrl = false
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/WebShell/Core/WSDownloadManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShelllDownloadManager.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 18-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 |
12 |
13 | /**
14 | @wdg Add Download support
15 |
16 | Issue: #31
17 |
18 | This class will handle WebShell Downloads.
19 |
20 | It's a basic download manager, so no progress, and nothing else, just download.
21 | */
22 | class WebShelllDownloadManager {
23 | var TURL: URL
24 | var Fname: String = ""
25 | var Session: URLSession = URLSession()
26 | var DFolder: URL
27 |
28 | /**
29 | init
30 | - Parameter url: URL to download
31 | */
32 | init(url: URL) {
33 | TURL = url
34 | Fname = url.lastPathComponent
35 |
36 | DFolder = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
37 |
38 | let downloadsURL = String(describing: DFolder) + url.lastPathComponent
39 |
40 | self.startDownload(TURL, savePath: URL(string: downloadsURL))
41 | }
42 |
43 | /**
44 | Start the download
45 | - Parameter URL: The URL to download
46 | - Parameter savePath: The savePath
47 | */
48 | func startDownload(_ URL: Foundation.URL, savePath: Foundation.URL!) {
49 | let sessionConfig = URLSessionConfiguration.default
50 | let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
51 | let request = NSMutableURLRequest(url: URL)
52 | request.httpMethod = "GET"
53 |
54 | noop(session) // temporary we want no stupid "fix-it" warnings.
55 |
56 | let task = session.dataTask(with: request as URLRequest, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
57 | if (error == nil) {
58 | let statusCode = (response as! HTTPURLResponse).statusCode
59 | self.noop(statusCode as AnyObject) // For further use HTTP Status code.
60 |
61 | let saveData = NSData(data: data!) as Data
62 | try? saveData.write(to: savePath!, options: [.atomic])
63 |
64 | // Ask the question on the main queue.
65 | OperationQueue.main.addOperation({
66 | if (self.dialog("Download of \"\(self.Fname)\" complete", text: "Would you like to open the downloads folder?")) {
67 | NSWorkspace.shared.open(self.DFolder)
68 | }
69 | })
70 | }
71 | else {
72 | // Failure
73 | print("Faulure: %@", error!.localizedDescription);
74 | }
75 | })
76 |
77 | task.resume()
78 | }
79 |
80 | /**
81 | Display a nice dialog with a question.\
82 | Please remember to use it only on the mainQueue
83 |
84 | - Parameter question: The question
85 | - Parameter text: The text you want to ask
86 | - Returns: Bool
87 | */
88 | func dialog(_ question: String, text: String) -> Bool {
89 | let myPopup: NSAlert = NSAlert()
90 | myPopup.messageText = question
91 | myPopup.informativeText = text
92 | myPopup.alertStyle = NSAlert.Style.informational
93 | myPopup.addButton(withTitle: "Yes")
94 | myPopup.addButton(withTitle: "No")
95 |
96 | let res = myPopup.runModal()
97 |
98 | if res == NSApplication.ModalResponse.alertFirstButtonReturn {
99 | return true
100 | }
101 |
102 | return false
103 | }
104 |
105 | /**
106 | End the download task
107 | */
108 | func endDownloadTask() -> Void { }
109 |
110 | /**
111 | Noop!
112 | */
113 | func noop(_ ob: AnyObject) -> Void { }
114 | }
115 |
--------------------------------------------------------------------------------
/WebShell/WSDownloadManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShelllDownloadManager.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 18-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 |
12 |
13 | /**
14 | @wdg Add Download support
15 |
16 | Issue: #31
17 |
18 | This class will handle WebShell Downloads.
19 |
20 | It's a basic download manager, so no progress, and nothing else, just download.
21 | */
22 | class WebShelllDownloadManager {
23 | var TURL: URL
24 | var Fname: String = ""
25 | var Session: URLSession = URLSession()
26 | var DFolder: URL
27 |
28 | /**
29 | init
30 | - Parameter url: URL to download
31 | */
32 | init(url: URL) {
33 | TURL = url
34 | Fname = url.lastPathComponent
35 |
36 | DFolder = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
37 |
38 | let downloadsURL = String(describing: DFolder) + url.lastPathComponent
39 |
40 | self.startDownload(TURL, savePath: URL(string: downloadsURL))
41 | }
42 |
43 | /**
44 | Start the download
45 | - Parameter URL: The URL to download
46 | - Parameter savePath: The savePath
47 | */
48 | func startDownload(_ URL: Foundation.URL, savePath: Foundation.URL!) {
49 | let sessionConfig = URLSessionConfiguration.default
50 | let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
51 | let request = NSMutableURLRequest(url: URL)
52 | request.httpMethod = "GET"
53 |
54 | noop(session) // temporary we want no stupid "fix-it" warnings.
55 |
56 | let task = session.dataTask(with: request as URLRequest, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
57 | if (error == nil) {
58 | let statusCode = (response as! HTTPURLResponse).statusCode
59 | self.noop(statusCode as AnyObject) // For further use HTTP Status code.
60 |
61 | let saveData = NSData.init(data: data!) as Data
62 | try? saveData.write(to: savePath!, options: [.atomic])
63 |
64 | // Ask the question on the main queue.
65 | OperationQueue.main.addOperation({
66 | if (self.dialog("Download of \"\(self.Fname)\" complete", text: "Would you like to open the downloads folder?")) {
67 | NSWorkspace.shared.open(self.DFolder)
68 | }
69 | })
70 | }
71 | else {
72 | // Failure
73 | print("Faulure: %@", error!.localizedDescription);
74 | }
75 | })
76 |
77 | task.resume()
78 | }
79 |
80 | /**
81 | Display a nice dialog with a question.\
82 | Please remember to use it only on the mainQueue
83 |
84 | - Parameter question: The question
85 | - Parameter text: The text you want to ask
86 | - Returns: Bool
87 | */
88 | func dialog(_ question: String, text: String) -> Bool {
89 | let myPopup: NSAlert = NSAlert()
90 | myPopup.messageText = question
91 | myPopup.informativeText = text
92 | myPopup.alertStyle = NSAlert.Style.informational
93 | myPopup.addButton(withTitle: "Yes")
94 | myPopup.addButton(withTitle: "No")
95 |
96 | let res = myPopup.runModal()
97 |
98 | if res == NSApplication.ModalResponse.alertFirstButtonReturn {
99 | return true
100 | }
101 |
102 | return false
103 | }
104 |
105 | /**
106 | End the download task
107 | */
108 | func endDownloadTask() -> Void { }
109 |
110 | /**
111 | Noop!
112 | */
113 | func noop(_ ob: AnyObject) -> Void { }
114 | }
115 |
--------------------------------------------------------------------------------
/WebShell/Core/WSPasswordManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WSPasswordManager.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 02-11-17.
6 | // Copyright © 2017 Wesley de Groot. All rights reserved.
7 | //
8 | // TODO: Create a safe password storage, instead of UserDefaults.
9 |
10 | import Foundation
11 | import WebKit
12 |
13 | extension WSViewController {
14 | /**
15 | Inject the password manager for a website (*via self.injectWebhooks*)
16 |
17 | @wdg memorize credentials (*Issue: #74*)
18 |
19 | - Parameter jsContext: JSContext!
20 | - Parameter website: String (site host)
21 | */
22 | internal func _injectPasswordFor(_ jsContext: JSContext!, website: String) -> Void {
23 | let database = UserDefaults(suiteName: website)
24 | if let savedUsername = database?.object(forKey: "username") {
25 | if let savedPassword = database?.object(forKey: "password") {
26 | let loadUsernameJS = "var inputFields = document.querySelectorAll(\"input[name='username']\"); for (var i = inputFields.length >>> 0; i--;) { inputFields[i].value = \'\(savedUsername)\';}"
27 | let loadPasswordJS = "var inputFields = document.querySelectorAll(\"input[name='password']\"); for (var i = inputFields.length >>> 0; i--;) { inputFields[i].value = \'\(savedPassword)\';}"
28 |
29 | jsContext.evaluateScript(loadUsernameJS)
30 | jsContext.evaluateScript(loadPasswordJS)
31 | }
32 | }
33 |
34 | database?.synchronize()
35 | }
36 |
37 | /**
38 | Inject the password manager for a website (*grabber part*)
39 |
40 | @wdg memorize credentials (*Issue: #74*)
41 |
42 | - Parameter jsContext: JSContext!
43 | - Parameter website: String (site host)
44 | */
45 | internal func _injectPasswordListener(_ jsContext: JSContext!, website: String) -> Void {
46 | let database = UserDefaults(suiteName: website)
47 | let listener = "var WSPasswordManager={currentSite:document.location.host,initialize:function(e,a){WSPasswordManager.checkForms()},checkForms:function(){for(var e=document.getElementsByTagName(\"form\"),a=0,t=e.length;t>a;a++)\"post\"===e[a].method.toLowerCase()&&e[a].setAttribute(\"onsubmit\",\"event.preventDefault();return WSPasswordManager.validate(this);\")},validate:function(e){var a=e.querySelectorAll(\"input[name='username']\"),t=e.querySelectorAll(\"input[name='password']\");return a.length>0&&t.length>0&&(\"undefined\"!=typeof WSApp?WSApp.savePassword(a[0].value,t[0].value):window.alert(\"Internal error\nPassword manager failed to initialize\")),!1}};WSPasswordManager.initialize();"
48 | if settings.passwordManager {
49 | jsContext.evaluateScript(listener)
50 | }
51 | database?.synchronize()
52 | }
53 |
54 | /**
55 | Save a password to the database (*via _injectPasswordListener*)
56 |
57 | @wdg memorize credentials (*Issue: #74*)
58 |
59 | - Parameter jsContext: JSContext!
60 | - Parameter website: String (site host)
61 | - Parameter username: String
62 | - Parameter password: String
63 | */
64 | internal func _savePasswordFor(_ jsContext: JSContext!, website: String, username: String, password: String) -> Void {
65 | let database = UserDefaults(suiteName: website)
66 | database?.set(website, forKey: "website")
67 | database?.set(username, forKey: "username")
68 | database?.set(password, forKey: "password")
69 | database?.synchronize()
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/WebShell/Core/WSBaseSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WSBaseSettings.swift
3 | // WebShell
4 | //
5 | // Created by Fahim Farook on 9/12/17.
6 | // Copyright © 2017 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class WSBaseSettings {
12 | // URL to browse to
13 | var url = "http://djyde.github.io/WebShell/WebShell/"
14 |
15 | // The last URL the app was on
16 | var lastURL: String {
17 | set {
18 | let def = UserDefaults.standard
19 | def.set(newValue, forKey: title + "-LastURL")
20 | def.synchronize()
21 | }
22 | get {
23 | let def = UserDefaults.standard
24 | if let url = def.value(forKey: title + "-LastURL") as? String {
25 | return url
26 | }
27 | return ""
28 | }
29 | }
30 |
31 | // set the app title
32 | var title = Bundle.main.infoDictionary!["CFBundleName"] as! String
33 |
34 | // if you want to use the default one then leave it default || default = title/version based on Safari/AppleWebKit (KHTML, like Gecko)
35 | // otherwise change it to a useragent you want. (Default: "default")
36 | var useragent = "default"
37 |
38 | // Do you want to use the document title? (Default: true)
39 | var useDocumentTitle = true
40 |
41 | // Multilanguage loading text!
42 | var launchingText = NSLocalizedString("Launching...", comment: "Launching...")
43 |
44 | // Note that the window min height is 640 and min width is 1000 by default. You could change it in Main.storyboard
45 | var initialWindowHeight = 640
46 | var initialWindowWidth = 1000
47 |
48 | // Open target=_blank in a new screen? (Default: false)
49 | var openInNewScreen = false
50 |
51 | // Do you want a loading bar? (Default: true)
52 | var showLoadingBar = true
53 |
54 | // Add console.log support? (Default: false)
55 | var consoleSupport = false
56 |
57 | // Does the app needs Location support (Default: false)
58 | // note: if true, then WebShell always uses location, whenever it is used or not
59 | var needLocation = false
60 |
61 | // run the app in debug mode? (Default: false)
62 | // will be overridden by Xcode (runs with -NSDocumentRevisionsDebugMode YES)
63 | var debugmode = false
64 |
65 | // Please paste here the JavaScript you want to load on a website (Default: "")
66 | var jsInject = ""
67 |
68 | // Please paste here the CSS you want to load on a website (Default: "")
69 | var cssInject = ""
70 |
71 | // Enable (inject) import (JS/CSS) Folder. (Default: true)
72 | var enableInjectImport = true
73 |
74 | // Menubar app (right side next to clock) (Default: false)
75 | var menuBarApp = false
76 |
77 | // Navigate trough trackpad (back/forward) (Default: true)
78 | var navigateViaTrackpad = true
79 |
80 | // Use a password manager (Default: true).
81 | var passwordManager = true
82 |
83 | // Media keys settings - Enable "Back" & "Forward"
84 | var mkBackAndForward = true
85 |
86 | // Media Player support (experimental)
87 | var mkMediaPlayers = false
88 |
89 | // Contextmenu settings - // Enable "Back" & "Forward" (Default: true)
90 | var cmBackAndForward = true
91 |
92 | // Enable "Download" (Default: true)
93 | var cmDownload = true
94 |
95 | // Enable "Reload" (Default: true)
96 | var cmReload = true
97 |
98 | // Enable "Open in a new window" (Default: true)
99 | var cmNewWindow = true
100 |
101 | // open with last url? (Default: false)
102 | var openLastUrl = false
103 |
104 | // The URL to start with - the last page you were on or the base URL
105 | func startURL() -> String {
106 | if lastURL.isEmpty {
107 | return url
108 | }
109 | return openLastUrl ? lastURL : url
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/WebShell/Core/WSPageActions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellPageActions.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 |
12 | extension WSViewController {
13 | /**
14 | Add Observers for menu items
15 | */
16 | func addObservers() {
17 | // add menu action observers
18 | let observers = ["goHome", "reload", "copyUrl", "clearNotificationCount", "printThisPage"]
19 |
20 | for observer in observers {
21 | NotificationCenter.default.addObserver(self, selector: NSSelectorFromString(observer), name: NSNotification.Name(rawValue: observer), object: nil)
22 | }
23 | }
24 |
25 | /**
26 | Go to the home url
27 | */
28 | func goHome() {
29 | loadUrl(settings.url)
30 | }
31 |
32 | /**
33 | Reload the current webpage
34 | */
35 | func reload() {
36 | mainWebview.mainFrame.reload()
37 | }
38 |
39 | /**
40 | Copy the URL
41 | */
42 | func copyUrl() {
43 | let currentUrl: String = (mainWebview.mainFrame.dataSource?.request.url?.absoluteString)!
44 | let clipboard: NSPasteboard = NSPasteboard.general
45 | clipboard.clearContents()
46 |
47 | clipboard.setString(currentUrl, forType: .string)
48 | }
49 |
50 | /**
51 | Initialize settings
52 | */
53 | func initSettings() {
54 | // controll the progress bar
55 | if !settings.showLoadingBar {
56 | progressBar.isHidden = true // @wdg: Better progress indicator | Issue: #37
57 | }
58 |
59 | // @wdg Add Custom useragent support
60 | // Issue: #52
61 | if settings.useragent.lowercased() == "default" {
62 | var UA = Bundle.main.infoDictionary!["CFBundleName"] as! String
63 | UA = UA + "/"
64 | UA = UA + (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String)
65 | UA = UA + " based on Safari/AppleWebKit (KHTML, like Gecko)"
66 |
67 | UserDefaults.standard.register(defaults: ["UserAgent": UA]) // For iOS
68 | mainWebview.customUserAgent = UA // For Mac OS X
69 | } else {
70 | let UA = settings.useragent
71 | UserDefaults.standard.register(defaults: ["UserAgent": UA]) // For iOS
72 | mainWebview.customUserAgent = UA // For Mac OS X
73 | }
74 |
75 | // set launching text
76 | launchingLabel.stringValue = settings.launchingText
77 | }
78 |
79 | /**
80 | Initialize window
81 | */
82 | func initWindow() {
83 | firstAppear = false
84 | // set window title
85 | mainWindow.window?.title = settings.title
86 |
87 | // Force some preferences before loading...
88 | mainWebview.preferences.isJavaScriptEnabled = true
89 | mainWebview.preferences.javaScriptCanOpenWindowsAutomatically = true
90 | mainWebview.preferences.arePlugInsEnabled = true
91 | }
92 |
93 | /**
94 | Load a specific URL
95 |
96 | - Parameter url: The url to load
97 | */
98 |
99 | func loadUrl(_ url: String) {
100 | if settings.showLoadingBar {
101 | progressBar.isHidden = false
102 | progressBar.startAnimation(self)
103 | progressBar.maxValue = 100;
104 | progressBar.minValue = 1;
105 | progressBar.increment(by: 24)
106 | }
107 | let URL = Foundation.URL(string: url)
108 | mainWebview.mainFrame.load(URLRequest(url: URL!))
109 | }
110 |
111 | /**
112 | Add Print Support (#39) [@wdg]
113 |
114 | - Parameter Sender: The sending object
115 | */
116 | func printThisPage(_ Sender: AnyObject?) -> Void {
117 | let url = mainWebview.mainFrame.dataSource?.request?.url?.absoluteString
118 |
119 | let operation: NSPrintOperation = NSPrintOperation(view: mainWebview)
120 | operation.jobTitle = "Printing \(url!)"
121 |
122 | // If want to print landscape
123 | operation.printInfo.orientation = NSPrintInfo.PaperOrientation.landscape
124 | operation.printInfo.scalingFactor = 0.7
125 |
126 | if operation.run() {
127 | print("Printed?")
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/WebShell/Core/WSWebViewFunctions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebViewFunctions.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 | // See: #43
13 | extension WSViewController {
14 | func webView(_ sender: WebView!, runJavaScriptAlertPanelWithMessage message: String!, initiatedBy frame: WebFrame!) {
15 | let alert = NSAlert()
16 | alert.addButton(withTitle: "OK")
17 | alert.messageText = "Message"
18 | alert.informativeText = message
19 | alert.runModal()
20 | }
21 |
22 | // webview settings
23 | func webView(_ sender: WebView!, didStartProvisionalLoadFor frame: WebFrame!) {
24 | // @wdg: Better progress indicator | Issue: #37
25 | if settings.showLoadingBar {
26 | print("Started loading animation")
27 | progressBar.startAnimation(self)
28 | progressBar.maxValue = 100;
29 | progressBar.minValue = 1;
30 | progressBar.increment(by: 24)
31 | }
32 |
33 | if (!firstLoadingStarted) {
34 | firstLoadingStarted = true
35 | launchingLabel.isHidden = false
36 | }
37 | }
38 |
39 | // @wdg: Better progress indicator
40 | // Issue: #37
41 | func webView(_ sender: WebView!, willPerformClientRedirectTo URL: URL!, delay seconds: TimeInterval, fire date: Date!, for frame: WebFrame!) {
42 | if settings.showLoadingBar {
43 | progressBar.isHidden = false
44 | progressBar.startAnimation(self)
45 | progressBar.maxValue = 100;
46 | progressBar.minValue = 1;
47 | progressBar.increment(by: 24)
48 | }
49 | }
50 |
51 | // @wdg: Better progress indicator
52 | // Issue: #37
53 | func webView(_ webView: WebView!, decidePolicyForMIMEType type: String!, request: URLRequest!, frame: WebFrame!, decisionListener listener: WebPolicyDecisionListener!) {
54 | if settings.showLoadingBar {
55 | progressBar.isHidden = false
56 | progressBar.startAnimation(self)
57 | progressBar.maxValue = 100;
58 | progressBar.minValue = 1;
59 | progressBar.increment(by: 24)
60 | }
61 | }
62 |
63 | // @wdg: Better progress indicator
64 | // Issue: #37
65 | func webView(_ webView: WebView!, didFailLoadWithError error: NSError) {
66 | progressBar.increment(by: 50)
67 | progressBar.stopAnimation(self)
68 | progressBar.isHidden = true
69 | progressBar.doubleValue = 1;
70 | }
71 |
72 | // @wdg: Better progress indicator
73 | // Issue: #37
74 | func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!) {
75 | progressBar.increment(by: 50)
76 | progressBar.stopAnimation(self)
77 | progressBar.isHidden = true // Hide after we're done.
78 | progressBar.doubleValue = 1;
79 | if (!launchingLabel.isHidden) {
80 | launchingLabel.isHidden = true
81 | }
82 | // Save URL for last navigated page
83 | if let url = mainWebview.mainFrame.dataSource?.request.url?.absoluteString {
84 | settings.lastURL = url
85 | }
86 |
87 | // Inject Webhooks
88 | self.injectWebhooks(mainWebview.mainFrame.javaScriptContext)
89 | self.loopThroughiFrames()
90 |
91 | // @wdg Add location support
92 | // Issue: #41
93 | if settings.needLocation {
94 | self.websiteWantsLocation()
95 | } else {
96 | self.locationInjector(false) // Says i don't have a location!
97 | }
98 | }
99 |
100 | func webView(_ sender: WebView!, didReceiveTitle title: String!, for frame: WebFrame!) {
101 | if settings.useDocumentTitle {
102 | mainWindow.window?.title = title
103 | }
104 |
105 | self.injectLocalStorage(jsContext: frame.javaScriptContext)
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/WebShell/Core/WSApplication.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WSApplication.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 20-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import AppKit
10 | import Cocoa
11 |
12 | /**
13 | Class WSApplication
14 |
15 | This is an NSApplication sub-class to support the WebShell media keys. \
16 | \
17 | !important note, this class can not communicate with the ViewController.\
18 | The communication goes via NSUserDefaults.
19 | */
20 | @objc(WSApplication)
21 | class WSApplication: NSApplication {
22 | override func sendEvent(_ event: NSEvent) {
23 | if event.type == .systemDefined && event.subtype.rawValue == 8 {
24 | let keyCode = ((event.data1 & 0xFFFF0000) >> 16)
25 | let keyFlags = (event.data1 & 0x0000FFFF)
26 | // Get the key state. 0xA is KeyDown, OxB is KeyUp
27 | let keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA
28 | let keyRepeat = NSNumber(value: (keyFlags & 0x1))
29 | mediaKeyEvent(Int32(keyCode), state: keyState, keyRepeat: Bool(truncating: keyRepeat))
30 | }
31 | super.sendEvent(event)
32 | }
33 |
34 | func mediaKeyEvent(_ key: Int32, state: Bool, keyRepeat: Bool) {
35 | // Only send events on KeyDown. Without this check, these events will happen twice
36 | if (state) {
37 | switch (key) {
38 | case NX_KEYTYPE_PLAY: // F8 / Play
39 | if Settings.shared.mkBackAndForward {
40 | self.goReloadPage()
41 | } else {
42 | let _ = self.playPausePressed()
43 | }
44 | break
45 | case NX_KEYTYPE_FAST, NX_KEYTYPE_NEXT: // F9 / Forward
46 | if Settings.shared.mkBackAndForward {
47 | self.goForwardIfPossible()
48 | } else {
49 | let _ = self.nextItem()
50 | }
51 | break
52 | case NX_KEYTYPE_REWIND, NX_KEYTYPE_PREVIOUS: // F7 / Backward
53 | if Settings.shared.mkBackAndForward {
54 | self.goBackIfPossible()
55 | } else {
56 | let _ = self.previousItem()
57 | }
58 | break
59 | default:
60 | break
61 | }
62 | }
63 | }
64 |
65 | /**
66 | goBackIfPossible
67 |
68 | Since we can't communicate with the ViewController.\
69 | We'll set a NSUserDefaults, and the `WSMediaLoop` does the Job for us.
70 | */
71 | func goBackIfPossible() {
72 | UserDefaults.standard.set(true, forKey: "WSGoBack")
73 | UserDefaults.standard.synchronize()
74 | }
75 |
76 | /**
77 | goForwardIfPossible
78 |
79 | Since we can't communicate with the ViewController.\
80 | We'll set a NSUserDefaults, and the `WSMediaLoop` does the Job for us.
81 | */
82 | func goForwardIfPossible() {
83 | UserDefaults.standard.set(true, forKey: "WSGoForward")
84 | UserDefaults.standard.synchronize()
85 | }
86 |
87 | /**
88 | goReloadPage
89 |
90 | Since we can't communicate with the ViewController.\
91 | We'll set a NSUserDefaults, and the `WSMediaLoop` does the Job for us.
92 | */
93 | func goReloadPage() {
94 | UserDefaults.standard.set(true, forKey: "WSGoReload")
95 | UserDefaults.standard.synchronize()
96 | }
97 |
98 | func nextItem() -> Bool {
99 | // ...
100 | return false
101 | }
102 |
103 | func previousItem() -> Bool {
104 | // ...
105 | return false
106 | }
107 |
108 | func playPausePressed() -> Bool {
109 | // ...
110 | return false
111 | }
112 | }
113 |
114 | extension WSViewController {
115 | /**
116 | Communication for the WSApplication class
117 |
118 | - Parameter Sender: AnyObject (used for #selector use self)
119 | */
120 | @objc func WSMediaLoop(_ Sender: AnyObject) -> Void {
121 | self.perform(#selector(WSViewController.WSMediaLoop(_:)), with: nil, afterDelay: 0.5)
122 |
123 | if (UserDefaults.standard.bool(forKey: "WSGoBack")) {
124 | UserDefaults.standard.set(false, forKey: "WSGoBack")
125 | UserDefaults.standard.synchronize()
126 | self._goBack(self)
127 | }
128 |
129 | if (UserDefaults.standard.bool(forKey: "WSGoForward")) {
130 | UserDefaults.standard.set(false, forKey: "WSGoForward")
131 | UserDefaults.standard.synchronize()
132 | self._goForward(self)
133 | }
134 |
135 | if (UserDefaults.standard.bool(forKey: "WSGoReload")) {
136 | UserDefaults.standard.set(false, forKey: "WSGoReload")
137 | UserDefaults.standard.synchronize()
138 | self._reloadPage(self)
139 | }
140 |
141 | // @wdg Merge Statut with WebShell.
142 | // Issue: #56
143 | if settings.menuBarApp {
144 | if ((NSApplication.shared.keyWindow) != nil) {
145 | if (self.MustCloseWindow) {
146 | NSApplication.shared.keyWindow?.close()
147 | self.MustCloseWindow = false
148 | }
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/WebShell/Core/WSCustomInject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellCustomInject.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 14-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 | extension WSViewController {
13 | /**
14 | _WSInjectJS
15 |
16 | Injects JavaScript in to a frame, or other position
17 |
18 | - Parameter jsContext: JSContext!
19 |
20 | - Note: @wdg #36
21 | */
22 | internal func _WSInjectJS(_ jsContext: JSContext!) {
23 | // JSInject
24 | if !settings.jsInject.isEmpty {
25 | jsContext.evaluateScript(settings.jsInject)
26 | }
27 | _WSFindJS(jsContext)
28 | }
29 |
30 | /**
31 | _WSInjectCSS
32 |
33 | Injects CSS in to a frame, or other position
34 |
35 | - Parameter jsContext: JSContext!
36 |
37 | - Note: @wdg #36
38 | */
39 | internal func _WSInjectCSS(_ jsContext: JSContext!) {
40 | // CSSInject
41 | if !settings.cssInject.isEmpty {
42 | let css = settings.cssInject
43 | .replacingOccurrences(of: "\n", with: "")
44 | .replacingOccurrences(of: "\r", with: "")
45 | .replacingOccurrences(of: "'", with: "\\'")
46 |
47 | jsContext.evaluateScript("var css='\(css)',head=document.head,style=document.createElement('style');style.type='text/css';if (style.styleSheet){style.styleSheet.cssText = css;}else{style.appendChild(document.createTextNode(css));}head.appendChild(style);")
48 | }
49 | _WSFindCSS(jsContext)
50 | }
51 |
52 | /**
53 | _WSFindCSS
54 |
55 | Find CSS to inject
56 |
57 | - Parameter jsContext: JSContext!
58 |
59 | - Note: @wdg #36
60 | */
61 | internal func _WSFindCSS(_ jsContext: JSContext!) {
62 | if settings.enableInjectImport { // (EII)
63 |
64 | let Seperated = (CommandLine.arguments[0]).components(separatedBy: "/")
65 | var newPath = ""
66 |
67 | for i in 0 ... (Seperated.count - 3) {
68 | newPath = newPath + Seperated[i] + "/"
69 | }
70 |
71 | newPath = newPath + "Resources/CSS"
72 |
73 | if FileManager().fileExists(atPath: newPath) {
74 | do {
75 | let fm = FileManager.default
76 | let contents = try fm.contentsOfDirectory(atPath: newPath)
77 | let filter = NSPredicate(format: "self ENDSWITH '.css'", argumentArray: nil)
78 | let fileList = contents.filter { filter.evaluate(with: $0) }
79 | for injectFile in fileList {
80 | let fc = try String(contentsOfFile: newPath + "/" + injectFile, encoding: String.Encoding.utf8)
81 | .replacingOccurrences(of: "\n", with: "")
82 | .replacingOccurrences(of: "\r", with: "")
83 | .replacingOccurrences(of: "'", with: "\\'")
84 | jsContext.evaluateScript("var css='\(fc)',head=document.head,style=document.createElement('style');style.type='text/css';if (style.styleSheet){style.styleSheet.cssText = css;}else{style.appendChild(document.createTextNode(css));}head.appendChild(style);")
85 | }
86 | }
87 | catch { }
88 | // Look
89 | } else {
90 | // Create Directory
91 | do {
92 | try FileManager().createDirectory(atPath: newPath, withIntermediateDirectories: true, attributes: nil)
93 | }
94 | catch {
95 | print("Failed to create path.")
96 | }
97 | }
98 | }
99 | }
100 |
101 | /**
102 | _WSFindJS
103 |
104 | Find JS to inject
105 |
106 | - Parameter jsContext: JSContext!
107 |
108 | - Note: @wdg #36
109 | */
110 | internal func _WSFindJS(_ jsContext: JSContext!) {
111 | if settings.enableInjectImport { // (EII)
112 |
113 | let Seperated = (CommandLine.arguments[0]).components(separatedBy: "/")
114 | var newPath = ""
115 |
116 | for i in 0 ... (Seperated.count - 3) {
117 | newPath = newPath + Seperated[i] + "/"
118 | }
119 |
120 | newPath = newPath + "Resources/JavaScript"
121 |
122 | if FileManager().fileExists(atPath: newPath) {
123 | do {
124 | let fm = FileManager.default
125 | let contents = try fm.contentsOfDirectory(atPath: newPath)
126 | let filter = NSPredicate(format: "self ENDSWITH '.js'", argumentArray: nil)
127 | let fileList = contents.filter { filter.evaluate(with: $0) }
128 | for injectFile in fileList {
129 | let fc = try String(contentsOfFile: newPath + "/" + injectFile, encoding: String.Encoding.utf8)
130 | jsContext.evaluateScript(fc)
131 | }
132 | }
133 | catch { }
134 | // Look
135 | } else {
136 | // Create Directory
137 | do {
138 | try FileManager().createDirectory(atPath: newPath, withIntermediateDirectories: true, attributes: nil)
139 | }
140 | catch {
141 | print("Failed to create path.")
142 | }
143 | }
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/WebShell/Core/WSTrackpadGestures.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellTrackpadGestures.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 20-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 | import WebKit
12 |
13 | /**
14 | @wdg: This extension will support the swipe gestures
15 | Issue: #44
16 | */
17 | extension WSViewController: NSGestureRecognizerDelegate {
18 | /**
19 | WSinitSwipeGestures
20 |
21 | Initialize Swipe Gestures!!!
22 |
23 | @wdg #44: Support Trackpad gestures
24 | */
25 | func WSinitSwipeGestures() {
26 | mainWebview.acceptsTouchEvents = true
27 | self.view.acceptsTouchEvents = true
28 | }
29 |
30 | override var acceptsFirstResponder: Bool {
31 | return true
32 | }
33 |
34 | override func touchesBegan(with event: NSEvent) {
35 | twoFingersTouches = GestureUtils.twoFingersTouches(mainWebview, event)
36 | }
37 |
38 | override func touchesMoved(with event: NSEvent) {
39 | let swipeType: SwipeType = settings.navigateViaTrackpad ? GestureUtils.swipe(mainWebview, event, twoFingersTouches) : .none
40 | if swipeType == .right {
41 | if mainWebview.canGoForward {
42 | if mainWebview.isLoading {
43 | mainWebview.stopLoading(nil)
44 | }
45 | mainWebview.goForward(nil)
46 | }
47 | } else if swipeType == .left {
48 | if mainWebview.canGoBack {
49 | if mainWebview.isLoading {
50 | mainWebview.stopLoading(nil)
51 | }
52 | mainWebview.goBack(nil)
53 | }
54 | }
55 | }
56 | }
57 |
58 | enum SwipeType { /*Two finger swipe type*/
59 | case left, right, none
60 | }
61 |
62 | class GestureUtils {
63 | /**
64 | * To avoid duplicate code we could extract the content of this method into an GestureUtils method. Return nil if there isn't 2 touches and set the array only if != nil
65 | */
66 | static func twoFingersTouches(_ view: NSView, _ event: NSEvent) -> [String: NSTouch]? {
67 | var twoFingersTouches: [String: NSTouch]? = nil // NSMutualDictionary was used before and didn't require casting id to string, revert if side-effects manifest
68 | if (event.type == NSEvent.EventType.gesture) { // could maybe be: EventTypeBeginGesture
69 | let touches: Set = event.touches(matching: NSTouch.Phase.any, in: view) // touchesMatchingPhase:NSTouchPhaseAny inView:self
70 | if (touches.count == 2){
71 | twoFingersTouches = [String: NSTouch]()
72 | for touch in touches {//
73 | twoFingersTouches!["\((touch).identity)"] = touch //assigns each touch to the identity of the same touch
74 | }
75 | }
76 | }
77 |
78 | return twoFingersTouches
79 | }
80 | /**
81 | * Detects 2 finger (left/right) swipe gesture
82 | * NOTE: either of 3 enums is returned: .leftSwipe, .rightSwipe .none
83 | * TODO: also make up and down swipe detectors, and do more research into how this could be done easier. Maybe you even have some clues in the notes about gestures etc.
84 | * Conceptually:
85 | * 1. Record 2 .began touchEvents
86 | * 2. Record 2 .ended touchEvents
87 | * 3. Measure the distance between .began and .ended and assert if it is within threshold
88 | */
89 | static func swipe(_ view: NSView, _ event: NSEvent, _ twoFingersTouches: [String: NSTouch]?) -> SwipeType {
90 | let endingTouches: Set = event.touches(matching: NSTouch.Phase.ended, in: view)
91 | if (endingTouches.count > 0 && twoFingersTouches != nil) {
92 | let beginningTouches: [String: NSTouch] = twoFingersTouches! // copy the twoFingerTouches data
93 | var magnitudes: [CGFloat] = [] // magnitude definition: the great size or extent of something
94 | for endingTouch in endingTouches {
95 | let beginningTouch:NSTouch? = beginningTouches["\(endingTouch.identity)"]
96 | if (beginningTouch == nil) { // skip if endingTouch doesn't have a matching beginningTouch
97 | continue
98 | }
99 | let magnitude: CGFloat = endingTouch.normalizedPosition.x - beginningTouch!.normalizedPosition.x
100 | magnitudes.append(magnitude)
101 | }
102 | var sum: CGFloat = 0
103 | for magnitude in magnitudes{
104 | sum += magnitude
105 | }
106 | let absoluteSum: CGFloat = abs(sum) // force value to be positive
107 | let kSwipeMinimumLength: CGFloat = 0.1
108 | if (absoluteSum < kSwipeMinimumLength) { // Assert if the absolute sum is long enough to be considered a complete gesture
109 | return .none
110 | }
111 | if (sum > 0) {
112 | return .right
113 | }else /*if(sum < 0)*/ {
114 | return .left
115 | }
116 | }
117 | return .none // no swipe direction detected
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/WebShell/WSTrackpadGestures.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellTrackpadGestures.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 20-04-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 | import WebKit
12 |
13 | /**
14 | @wdg: This extension will support the swipe gestures
15 | Issue: #44
16 | */
17 | extension ViewController: NSGestureRecognizerDelegate {
18 | /**
19 | WSinitSwipeGestures
20 |
21 | Initialize Swipe Gestures!!!
22 |
23 | @wdg #44: Support Trackpad gestures
24 | */
25 | func WSinitSwipeGestures() {
26 | mainWebview.acceptsTouchEvents = true
27 | self.view.acceptsTouchEvents = true
28 | }
29 |
30 | override var acceptsFirstResponder: Bool {
31 | return true
32 | }
33 |
34 | override func touchesBegan(with event: NSEvent) {
35 | twoFingersTouches = GestureUtils.twoFingersTouches(mainWebview, event)
36 | }
37 |
38 | override func touchesMoved(with event: NSEvent) {
39 | let swipeType: SwipeType = (WebShellSettings["navigateViaTrackpad"] as! Bool) ? GestureUtils.swipe(mainWebview, event, twoFingersTouches) : .none
40 | if (swipeType == .right) {
41 | if (mainWebview.canGoForward) {
42 | if (mainWebview.isLoading) {
43 | mainWebview.stopLoading(nil)
44 | }
45 | mainWebview.goForward(nil)
46 | }
47 | } else if(swipeType == .left) {
48 | if (mainWebview.canGoBack) {
49 | if (mainWebview.isLoading) {
50 | mainWebview.stopLoading(nil)
51 | }
52 | mainWebview.goBack(nil)
53 | }
54 | }
55 | }
56 | }
57 |
58 | enum SwipeType { /*Two finger swipe type*/
59 | case left, right, none
60 | }
61 |
62 | class GestureUtils {
63 | /**
64 | * To avoid duplicate code we could extract the content of this method into an GestureUtils method. Return nil if there isn't 2 touches and set the array only if != nil
65 | */
66 | static func twoFingersTouches(_ view: NSView, _ event: NSEvent) -> [String: NSTouch]? {
67 | var twoFingersTouches: [String: NSTouch]? = nil // NSMutualDictionary was used before and didn't require casting id to string, revert if side-effects manifest
68 | if (event.type == NSEvent.EventType.gesture) { // could maybe be: EventTypeBeginGesture
69 | let touches: Set = event.touches(matching: NSTouch.Phase.any, in: view) // touchesMatchingPhase:NSTouchPhaseAny inView:self
70 | if (touches.count == 2){
71 | twoFingersTouches = [String: NSTouch]()
72 | for touch in touches {//
73 | twoFingersTouches!["\((touch).identity)"] = touch //assigns each touch to the identity of the same touch
74 | }
75 | }
76 | }
77 |
78 | return twoFingersTouches
79 | }
80 | /**
81 | * Detects 2 finger (left/right) swipe gesture
82 | * NOTE: either of 3 enums is returned: .leftSwipe, .rightSwipe .none
83 | * TODO: also make up and down swipe detectors, and do more research into how this could be done easier. Maybe you even have some clues in the notes about gestures etc.
84 | * Conceptually:
85 | * 1. Record 2 .began touchEvents
86 | * 2. Record 2 .ended touchEvents
87 | * 3. Measure the distance between .began and .ended and assert if it is within threshold
88 | */
89 | static func swipe(_ view: NSView, _ event: NSEvent, _ twoFingersTouches: [String: NSTouch]?) -> SwipeType {
90 | let endingTouches: Set = event.touches(matching: NSTouch.Phase.ended, in: view)
91 | if (endingTouches.count > 0 && twoFingersTouches != nil) {
92 | let beginningTouches: [String: NSTouch] = twoFingersTouches! // copy the twoFingerTouches data
93 | var magnitudes: [CGFloat] = [] // magnitude definition: the great size or extent of something
94 | for endingTouch in endingTouches {
95 | let beginningTouch:NSTouch? = beginningTouches["\(endingTouch.identity)"]
96 | if (beginningTouch == nil) { // skip if endingTouch doesn't have a matching beginningTouch
97 | continue
98 | }
99 | let magnitude: CGFloat = endingTouch.normalizedPosition.x - beginningTouch!.normalizedPosition.x
100 | magnitudes.append(magnitude)
101 | }
102 | var sum: CGFloat = 0
103 | for magnitude in magnitudes{
104 | sum += magnitude
105 | }
106 | let absoluteSum: CGFloat = abs(sum) // force value to be positive
107 | let kSwipeMinimumLength: CGFloat = 0.1
108 | if (absoluteSum < kSwipeMinimumLength) { // Assert if the absolute sum is long enough to be considered a complete gesture
109 | return .none
110 | }
111 | if (sum > 0) {
112 | return .right
113 | }else /*if(sum < 0)*/ {
114 | return .left
115 | }
116 | }
117 | return .none // no swipe direction detected
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/WebShell/WSPageActions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellPageActions.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 |
12 | extension ViewController {
13 | /**
14 | Add Observers for menu items
15 | */
16 | func addObservers() {
17 | // add menu action observers
18 | let observers = ["goHome", "reload", "copyUrl", "clearNotificationCount", "printThisPage"]
19 |
20 | for observer in observers {
21 | NotificationCenter.default.addObserver(self, selector: NSSelectorFromString(observer), name: NSNotification.Name(rawValue: observer), object: nil)
22 | }
23 | }
24 |
25 | /**
26 | Go to the home url
27 | */
28 | func goHome() {
29 | loadUrl((WebShellSettings["url"] as? String)!)
30 | }
31 |
32 | /**
33 | Reload the current webpage
34 | */
35 | func reload() {
36 | mainWebview.mainFrame.reload()
37 | }
38 |
39 | /**
40 | Copy the URL
41 | */
42 | func copyUrl() {
43 | let currentUrl: String = (mainWebview.mainFrame.dataSource?.request.url?.absoluteString)!
44 | let clipboard: NSPasteboard = NSPasteboard.general
45 | clipboard.clearContents()
46 |
47 | clipboard.setString(currentUrl, forType: .string)
48 | }
49 |
50 | /**
51 | Initialize settings
52 | */
53 | func initSettings() {
54 | // controll the progress bar
55 | if (!(WebShellSettings["showLoadingBar"] as? Bool)!) {
56 | progressBar.isHidden = true // @wdg: Better progress indicator | Issue: #37
57 | }
58 |
59 | // @wdg Add Custom useragent support
60 | // Issue: #52
61 | if ((WebShellSettings["useragent"] as! String).lowercased() == "default") {
62 | var UA: String = Bundle.main.infoDictionary!["CFBundleName"] as! String
63 | UA = UA + "/"
64 | UA = UA + (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String)
65 | UA = UA + " based on Safari/AppleWebKit (KHTML, like Gecko)"
66 |
67 | UserDefaults.standard.register(defaults: ["UserAgent": UA]) // For iOS
68 | mainWebview.customUserAgent = UA // For Mac OS X
69 | } else {
70 | let UA: String = WebShellSettings["useragent"] as! String
71 | UserDefaults.standard.register(defaults: ["UserAgent": UA]) // For iOS
72 | mainWebview.customUserAgent = UA // For Mac OS X
73 | }
74 |
75 | // set launching text
76 | launchingLabel.stringValue = (WebShellSettings["launchingText"] as? String)!
77 | }
78 |
79 | /**
80 | Initialize window
81 | */
82 | func initWindow() {
83 | firstAppear = false
84 |
85 | // set window size
86 | var frame: NSRect = mainWindow.frame
87 |
88 | let WIDTH: CGFloat = CGFloat(WebShellSettings["initialWindowWidth"] as! Int),
89 | HEIGHT: CGFloat = CGFloat(WebShellSettings["initialWindowHeight"] as! Int)
90 |
91 | frame.size.width = WIDTH
92 | frame.size.height = HEIGHT
93 |
94 | // @wdg Fixed screen position (now it centers)
95 | // Issue: #19
96 | // Note: do not use HEIGHT, WIDTH for some strange reason the window will be positioned 25px from bottom!
97 | let ScreenHeight: CGFloat = (NSScreen.main?.frame.size.width)!,
98 | WindowHeight: CGFloat = CGFloat(WebShellSettings["initialWindowWidth"] as! Int), // do not use HEIGHT!
99 | ScreenWidth: CGFloat = (NSScreen.main?.frame.size.height)!,
100 | WindowWidth: CGFloat = CGFloat(WebShellSettings["initialWindowHeight"] as! Int) // do not use WIDTH!
101 | frame.origin.x = (ScreenHeight / 2 - WindowHeight / 2)
102 | frame.origin.y = (ScreenWidth / 2 - WindowWidth / 2)
103 |
104 | // @froge-xyz Fixed initial window size
105 | // Issue: #1, #45
106 | mainWindow.window?.setFrame(frame, display: true)
107 |
108 | // defims Fixed the initial window size.
109 | mainWindow.frame = frame
110 |
111 | // set window title
112 | mainWindow.window?.title = WebShellSettings["title"] as! String
113 |
114 | // Force some preferences before loading...
115 | mainWebview.preferences.isJavaScriptEnabled = true
116 | mainWebview.preferences.javaScriptCanOpenWindowsAutomatically = true
117 | mainWebview.preferences.arePlugInsEnabled = true
118 | }
119 |
120 | /**
121 | Load a specific URL
122 |
123 | - Parameter url: The url to load
124 | */
125 |
126 | func loadUrl(_ url: String) {
127 | if ((WebShellSettings["showLoadingBar"] as? Bool)!) {
128 | progressBar.isHidden = false
129 | progressBar.startAnimation(self)
130 | progressBar.maxValue = 100;
131 | progressBar.minValue = 1;
132 | progressBar.increment(by: 24)
133 | }
134 | let URL = Foundation.URL(string: url)
135 | mainWebview.mainFrame.load(URLRequest(url: URL!))
136 | }
137 |
138 | /**
139 | Add Print Support (#39) [@wdg]
140 |
141 | - Parameter Sender: The sending object
142 | */
143 | func printThisPage(_ Sender: AnyObject?) -> Void {
144 | let url = mainWebview.mainFrame.dataSource?.request?.url?.absoluteString
145 |
146 | let operation: NSPrintOperation = NSPrintOperation.init(view: mainWebview)
147 | operation.jobTitle = "Printing \(url!)"
148 |
149 | // If want to print landscape
150 | operation.printInfo.orientation = NSPrintInfo.PaperOrientation.landscape
151 | operation.printInfo.scalingFactor = 0.7
152 |
153 | if operation.run() {
154 | print("Printed?")
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/WebShell/Support/navigator_geolocation_getCurrentPosition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // navigator_geolocation_getCurrentPosition.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 29-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | // @wdg Add location support
13 | // Issue: #41
14 | // This extension will handle all the location services.
15 | extension WSViewController {
16 |
17 | /**
18 | websiteWantsLocation
19 |
20 | the requested website wants/needs location services, so start it up
21 | */
22 | func websiteWantsLocation() -> Void {
23 | locationManager.delegate = self
24 | locationManager.desiredAccuracy = kCLLocationAccuracyBest
25 | locationManager.startUpdatingLocation()
26 | }
27 |
28 | /**
29 | locationManager got some locations for us!
30 |
31 | - Parameter manager: CLLocationManager
32 | - Parameter locations: AnyObject
33 | */
34 | func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
35 | let location: CLLocation = locations[0]
36 | self.locationInjector(true, location)
37 | }
38 |
39 | /**
40 | locationManager got a error!
41 |
42 | - Parameter manager: CLLocationManager
43 | - Parameter error: NSError
44 | */
45 | func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
46 | self.locationInjector(false)
47 | }
48 |
49 | /**
50 | locationManager got some locations for us!
51 |
52 | - Parameter haveLocation: (Bool) have the location?
53 | - Parameter location: (CLLocation) the location
54 | */
55 | func locationInjector(_ haveLocation: Bool, _ location: CLLocation? = CLLocation()) {
56 | // Ok inject new java Thing! (Cool!)
57 | let navigatorGeolocationGetCurrentPosition: @convention(block)(String?, String?, String?) -> String = {(correct: String?, invalid: String?, extra: String?) in
58 | // Checked with.
59 | // navigator.geolocation.getCurrentPosition(function(position) {console.log(position);},function(position) {console.log(position);});
60 |
61 | // X.coords.longitude
62 | // Safari Demo: > var x={coords: {longitude: 2, latitude: 1}}; console.log(x.coords.longitude);
63 | // Safari Demo: [Log] 2
64 |
65 | if (haveLocation) {
66 | let check_correct: String = correct!.lowercased()[0...8]
67 | let returnAs: String = "{coords: {coords: 'Coordinates', accuracy: 10, altitude: \(location!.altitude), altitudeAccuracy: 10, heading: '\(location!.course)', longitude: \(location!.coordinate.longitude), latitude: \(location!.coordinate.latitude), speed: \(location!.speed)}}"
68 |
69 | if (check_correct == "function") {
70 | // Begin with function (all lowercase)
71 | var newFunction = correct!.lowercased()[0...8]
72 | // Make the function named _WSLRD (WebShell Location Return Data)
73 | newFunction = newFunction + " _WSLRD"
74 | // Check if has space or not, otherwise begin 1 character later
75 | newFunction = newFunction + (correct?[(correct?.lowercased()[8] == " " ? 8 : 7)...(correct?.count)!])!
76 | // Call the function
77 | newFunction = newFunction + "\n;_WSLRD(\(returnAs))" // Insert what to return
78 |
79 | self.mainWebview.mainFrame.javaScriptContext.evaluateScript(newFunction) // Call & Done.
80 | } else {
81 | // call something else if it is a function, otherwise throw a error.
82 |
83 | let checkAndRun: String = String(describing: "if (typeof \(String(describing: correct)) === 'function'){\(String(describing: correct))(\(returnAs))}else{console.error('\(String(describing: correct)) is not a function')}")
84 | self.mainWebview.mainFrame.javaScriptContext.evaluateScript(checkAndRun) // Call & Done.
85 | }
86 | } else {
87 | if (invalid != nil) {
88 | let check_invalid: String = invalid!.lowercased()[0...8]
89 | let returnAs: String = "{coords: {coords: null, accuracy: null, altitude: null, altitudeAccuracy: null, heading: null, longitude: null, latitude: null, speed: null}}"
90 |
91 | if (check_invalid == "function") {
92 | // Begin with function (all lowercase)
93 | var newFunction = invalid!.lowercased()[0...8]
94 | // Make the function named _WSLRD (WebShell Location Return Data)
95 | newFunction = newFunction + " _WSLRD"
96 | // Check if has space or not, otherwise begin 1 character later
97 | newFunction = newFunction + invalid![(invalid!.lowercased()[8] == " " ? 8 : 7)...(invalid!.count)]
98 | // Call the function
99 | newFunction = newFunction + "\n;_WSLRD(\(returnAs))" // Insert what to return
100 |
101 | self.mainWebview.mainFrame.javaScriptContext.evaluateScript(newFunction) // Call & Done.
102 | } else {
103 | // call something else if it is a function, otherwise throw a error.
104 |
105 | let checkAndRun: String = "if (typeof \(invalid!) === 'function'){\(invalid!)(\(returnAs))}else{console.error('\(invalid!) is not a function')}"
106 | self.mainWebview.mainFrame.javaScriptContext.evaluateScript(checkAndRun) // Call & Done.
107 | }
108 | }
109 | }
110 |
111 | return "undefined"
112 | }
113 | self.mainWebview.mainFrame.javaScriptContext.objectForKeyedSubscript("navigator").objectForKeyedSubscript("geolocation").setObject(unsafeBitCast(navigatorGeolocationGetCurrentPosition, to: AnyObject.self), forKeyedSubscript: "getCurrentPosition" as (NSCopying & NSObjectProtocol)!)
114 | }
115 | }
116 | // TEST
117 | /*
118 | Inject via webinfo
119 |
120 | > navigator.geolocation.getCurrentPosition(function(position) {console.log(position.coords.latitude);},function(position) {console.log(position);});
121 | < "undefined" = $2
122 |
123 | Xcode:
124 | JS: 52.3593145320769 (Your current latitude)
125 | */
126 |
--------------------------------------------------------------------------------
/WebShell/Core/WSAppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WebShell
4 | //
5 | // Created by Randy on 15/12/19.
6 | // Copyright © 2015 RandyLu. All rights reserved.
7 | //
8 | // Wesley de Groot (@wdg), Added Notification and console.log Support
9 |
10 | import Cocoa
11 | import Foundation
12 | import NotificationCenter
13 |
14 | @NSApplicationMain
15 | class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
16 |
17 | var mainWindow: NSWindow!
18 | let popover = NSPopover()
19 | let statusItem = NSStatusBar.system.statusItem(withLength: -2)
20 | var eventMonitor: EventMonitor?
21 |
22 | func applicationDidFinishLaunching(_ aNotification: Notification) {
23 | // @wdg Merge Statut with WebShell.
24 | // Issue: #56
25 | if Settings.shared.menuBarApp {
26 | if let button = statusItem.button {
27 | button.image = NSImage(named: NSImage.Name(rawValue: "MenuIcon")) // StatusBarButtonImage
28 | button.action = #selector(AppDelegate.togglePopover(_:))
29 | }
30 |
31 | let sb = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main") , bundle: nil)
32 | popover.contentViewController = sb.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "WSView")) as? NSViewController
33 |
34 | initialPopupSize()
35 |
36 | eventMonitor = EventMonitor(mask: [NSEvent.EventTypeMask.leftMouseDown, NSEvent.EventTypeMask.rightMouseDown]) { [unowned self] event in
37 | if self.popover.isShown {
38 | self.closePopover(event)
39 | }
40 | }
41 | eventMonitor?.start()
42 | } else {
43 | // Add Notification center to the app delegate.
44 | NSUserNotificationCenter.default.delegate = self
45 | mainWindow = NSApplication.shared.windows[0]
46 | }
47 | // Change menus
48 | let title = Settings.shared.title
49 | let app = NSApplication.shared
50 | let mainMenu = app.mainMenu
51 | if let items = mainMenu?.items {
52 | // App menu
53 | let itm0 = items[0]
54 | itm0.title = title
55 | // About
56 | let itm1 = itm0.submenu!.items[0]
57 | itm1.title = "About " + title
58 | // Hide
59 | let itm2 = itm0.submenu!.items[2]
60 | itm2.title = "Hide " + title
61 | // Quit
62 | let itm3 = itm0.submenu!.items[8]
63 | itm3.title = "Quit " + title
64 | }
65 | }
66 |
67 | // @wdg close app if window closes
68 | // Issue: #40
69 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
70 | if !Settings.shared.menuBarApp {
71 | return true
72 | } else {
73 | return false
74 | }
75 | }
76 |
77 | func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
78 | if !flag {
79 | if !Settings.shared.menuBarApp {
80 | mainWindow!.makeKeyAndOrderFront(self)
81 | }
82 | }
83 |
84 | // clear badge
85 | NSApplication.shared.dockTile.badgeLabel = ""
86 | // @wdg Clear notification count
87 | // Issue: #34
88 | NSUserNotificationCenter.default.removeAllDeliveredNotifications()
89 | return true
90 | }
91 |
92 | // @wdg Add Notification Support
93 | // Issue: #2
94 | func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {
95 | // We (i) want Notifications support
96 | return true
97 | }
98 |
99 | // @wdg Add 'click' on notification support
100 | // Issue: #26
101 | func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
102 | // Open window if user clicked on notification!
103 | if !Settings.shared.menuBarApp {
104 | mainWindow!.makeKeyAndOrderFront(self)
105 | }
106 |
107 | // @wdg Clear badge
108 | NSApplication.shared.dockTile.badgeLabel = ""
109 | // @wdg Clear notification count
110 | // Issue: #34
111 | NSUserNotificationCenter.default.removeAllDeliveredNotifications()
112 | }
113 |
114 | /**
115 | Print the current page
116 |
117 | - Parameter sender: the sender object
118 | */
119 | @IBAction func printThisPage(_ sender: AnyObject) -> Void {
120 | NotificationCenter.default.post(name: Notification.Name(rawValue: "printThisPage"), object: nil)
121 | }
122 |
123 | /**
124 | Go to the given homepage as set in `Settings.swift`
125 |
126 | - Parameter sender: the sender object
127 | */
128 | @IBAction func goHome(_ sender: AnyObject) -> Void {
129 | NotificationCenter.default.post(name: Notification.Name(rawValue: "goHome"), object: nil)
130 | }
131 |
132 | /**
133 | Reload the current page
134 |
135 | - Parameter sender: the sender object
136 | */
137 | @IBAction func reload(_ sender: AnyObject) -> Void {
138 | NotificationCenter.default.post(name: Notification.Name(rawValue: "reload"), object: nil)
139 | }
140 |
141 | /**
142 | Copy the url of the current page
143 |
144 | - Parameter sender: the sender object
145 | */
146 | @IBAction func copyUrl(_ sender: AnyObject) -> Void {
147 | NotificationCenter.default.post(name: Notification.Name(rawValue: "copyUrl"), object: nil)
148 | }
149 |
150 | /**
151 | Popover initial popup size
152 | */
153 | func initialPopupSize() -> Void {
154 | popover.contentSize.width = CGFloat(Settings.shared.initialWindowWidth)
155 | popover.contentSize.height = CGFloat(Settings.shared.initialWindowHeight)
156 | }
157 |
158 | func applicationWillTerminate(_ aNotification: Notification) {
159 | // Insert code here to tear down your application
160 | }
161 |
162 | /**
163 | Show the popover screen
164 |
165 | - Parameter sender: the sender object
166 | */
167 | func showPopover(_ sender: AnyObject?) {
168 | if let button = statusItem.button {
169 | popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
170 | }
171 | eventMonitor?.start()
172 | }
173 |
174 | /**
175 | Close the popover screen
176 |
177 | - Parameter sender: the sender object
178 | */
179 | func closePopover(_ sender: AnyObject?) {
180 | popover.performClose(sender)
181 | eventMonitor?.stop()
182 | }
183 |
184 | /**
185 | Toggle the popover screen
186 |
187 | - Parameter sender: the sender object
188 | */
189 | @objc func togglePopover(_ sender: AnyObject?) {
190 | if (popover.isShown) {
191 | closePopover(sender)
192 | } else {
193 | showPopover(sender)
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/WebShell/nl.lproj/Main.strings:
--------------------------------------------------------------------------------
1 |
2 | /* Class = "NSMenuItem"; title = "Smart Copy/Paste"; ObjectID = "0cq-vj-EeT"; */
3 | "0cq-vj-EeT.title" = "Slim Kopiëren/plakken";
4 |
5 | /* Class = "NSMenuItem"; title = "WebShell"; ObjectID = "1Xt-HY-uBw"; */
6 | "1Xt-HY-uBw.title" = "WebShell";
7 |
8 | /* Class = "NSMenuItem"; title = "Quit WebShell"; ObjectID = "4sb-4s-VLi"; */
9 | "4sb-4s-VLi.title" = "Verlaat WebShell";
10 |
11 | /* Class = "NSMenuItem"; title = "About WebShell"; ObjectID = "5kV-Vb-QxS"; */
12 | "5kV-Vb-QxS.title" = "Over WebShell";
13 |
14 | /* Class = "NSMenuItem"; title = "Smart Dashes"; ObjectID = "AYs-Jm-OWX"; */
15 | "AYs-Jm-OWX.title" = "Slimme Dashes";
16 |
17 | /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */
18 | "AYu-sK-qS6.title" = "Menu";
19 |
20 | /* Class = "NSMenuItem"; title = "Control"; ObjectID = "Ca3-nV-juN"; */
21 | "Ca3-nV-juN.title" = "Control";
22 |
23 | /* Class = "NSMenuItem"; title = "Substitutions"; ObjectID = "Clr-cV-qUy"; */
24 | "Clr-cV-qUy.title" = "Vervangen";
25 |
26 | /* Class = "NSMenu"; title = "Transformations"; ObjectID = "DpD-Qi-s84"; */
27 | "DpD-Qi-s84.title" = "Transformaties";
28 |
29 | /* Class = "NSMenuItem"; title = "Close"; ObjectID = "E0V-gE-2XR"; */
30 | "E0V-gE-2XR.title" = "Sluit";
31 |
32 | /* Class = "NSMenuItem"; title = "Copy current URL"; ObjectID = "F42-Mv-oVO"; */
33 | "F42-Mv-oVO.title" = "Sluit huidige URL";
34 |
35 | /* Class = "NSMenuItem"; title = "Show Spelling and Grammar"; ObjectID = "H1w-Pb-bQf"; */
36 | "H1w-Pb-bQf.title" = "Laat spellingscontrole zien";
37 |
38 | /* Class = "NSMenuItem"; title = "Paste"; ObjectID = "HAk-2k-oTs"; */
39 | "HAk-2k-oTs.title" = "Plakken";
40 |
41 | /* Class = "NSWindow"; title = "WebShell"; ObjectID = "IQv-IB-iLA"; */
42 | "IQv-IB-iLA.title" = "WebShell";
43 |
44 | /* Class = "NSMenuItem"; title = "Transformations"; ObjectID = "Jms-Vz-UXk"; */
45 | "Jms-Vz-UXk.title" = "Transformaties";
46 |
47 | /* Class = "NSMenuItem"; title = "Select All"; ObjectID = "KZX-VJ-OIl"; */
48 | "KZX-VJ-OIl.title" = "Alles Selecteren";
49 |
50 | /* Class = "NSMenuItem"; title = "Show All"; ObjectID = "Kd2-mp-pUS"; */
51 | "Kd2-mp-pUS.title" = "Laat alles zien";
52 |
53 | /* Class = "NSMenuItem"; title = "Check Grammar With Spelling"; ObjectID = "NQd-4F-F7u"; */
54 | "NQd-4F-F7u.title" = "Spellingscontrole";
55 |
56 | /* Class = "NSMenuItem"; title = "Hide WebShell"; ObjectID = "Olw-nP-bQN"; */
57 | "Olw-nP-bQN.title" = "Verberg WebShell";
58 |
59 | /* Class = "NSMenuItem"; title = "Jump to Selection"; ObjectID = "Owh-gY-yb3"; */
60 | "Owh-gY-yb3.title" = "Ga naar sectie";
61 |
62 | /* Class = "NSMenuItem"; title = "Find…"; ObjectID = "Q1d-Fv-HmW"; */
63 | "Q1d-Fv-HmW.title" = "Zoeken…";
64 |
65 | /* Class = "NSMenuItem"; title = "Smart Quotes"; ObjectID = "QFF-U6-rj3"; */
66 | "QFF-U6-rj3.title" = "Slimme Quotes";
67 |
68 | /* Class = "NSMenuItem"; title = "Data Detectors"; ObjectID = "RbA-n6-mv4"; */
69 | "RbA-n6-mv4.title" = "Data Detectors";
70 |
71 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "T3K-M0-pds"; */
72 | "T3K-M0-pds.title" = "Undo";
73 |
74 | /* Class = "NSMenu"; title = "Speech"; ObjectID = "Ukk-qQ-spw"; */
75 | "Ukk-qQ-spw.title" = "Spraak";
76 |
77 | /* Class = "NSMenuItem"; title = "Check Spelling While Typing"; ObjectID = "VYp-LM-iqJ"; */
78 | "VYp-LM-iqJ.title" = "Spellingscontrole";
79 |
80 | /* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "Vdr-fp-XzO"; */
81 | "Vdr-fp-XzO.title" = "Verberg andere";
82 |
83 | /* Class = "NSMenu"; title = "Edit"; ObjectID = "Wah-OC-UhV"; */
84 | "Wah-OC-UhV.title" = "Bewerk";
85 |
86 | /* Class = "NSMenuItem"; title = "Paste and Match Style"; ObjectID = "Wah-va-YHm"; */
87 | "Wah-va-YHm.title" = "Plakken en stijl matchen";
88 |
89 | /* Class = "NSMenuItem"; title = "Text Replacement"; ObjectID = "XKU-CU-oCg"; */
90 | "XKU-CU-oCg.title" = "Tekst vervangen";
91 |
92 | /* Class = "NSMenuItem"; title = "Delete"; ObjectID = "XfS-gH-udC"; */
93 | "XfS-gH-udC.title" = "Verwijder";
94 |
95 | /* Class = "NSMenuItem"; title = "Find Next"; ObjectID = "Xx1-fz-mt3"; */
96 | "Xx1-fz-mt3.title" = "Vind Volgende";
97 |
98 | /* Class = "NSMenuItem"; title = "Make Lower Case"; ObjectID = "Zqd-ID-Z6K"; */
99 | "Zqd-ID-Z6K.title" = "Maak Lower Case";
100 |
101 | /* Class = "NSMenu"; title = "Find"; ObjectID = "aYU-p3-vbI"; */
102 | "aYU-p3-vbI.title" = "Vind";
103 |
104 | /* Class = "NSMenuItem"; title = "Spelling and Grammar"; ObjectID = "bcd-NF-0kD"; */
105 | "bcd-NF-0kD.title" = "Spellingscontrole";
106 |
107 | /* Class = "NSMenuItem"; title = "Copy"; ObjectID = "cHO-Y8-CkV"; */
108 | "cHO-Y8-CkV.title" = "Kopiëren";
109 |
110 | /* Class = "NSMenuItem"; title = "Correct Spelling Automatically"; ObjectID = "caM-nD-RQd"; */
111 | "caM-nD-RQd.title" = "Spellingscontrole";
112 |
113 | /* Class = "NSMenuItem"; title = "Start Speaking"; ObjectID = "dG3-Ol-VGb"; */
114 | "dG3-Ol-VGb.title" = "Start Spreken";
115 |
116 | /* Class = "NSMenuItem"; title = "Stop Speaking"; ObjectID = "eZE-iS-Gbc"; */
117 | "eZE-iS-Gbc.title" = "Stop Spreken";
118 |
119 | /* Class = "NSMenuItem"; title = "Check Document Now"; ObjectID = "elw-9g-erD"; */
120 | "elw-9g-erD.title" = "Controleer document nu";
121 |
122 | /* Class = "NSMenuItem"; title = "Home"; ObjectID = "fWh-O7-fkt"; */
123 | "fWh-O7-fkt.title" = "Home";
124 |
125 | /* Class = "NSMenu"; title = "Substitutions"; ObjectID = "fcK-mU-woF"; */
126 | "fcK-mU-woF.title" = "Vervanginen";
127 |
128 | /* Class = "NSMenuItem"; title = "Capitalize"; ObjectID = "hGZ-Nz-DIt"; */
129 | "hGZ-Nz-DIt.title" = "Hoofdletters";
130 |
131 | /* Class = "NSMenu"; title = "Spelling"; ObjectID = "hbN-l2-7R3"; */
132 | "hbN-l2-7R3.title" = "Spelling";
133 |
134 | /* Class = "NSMenuItem"; title = "Cut"; ObjectID = "i4s-n8-wXz"; */
135 | "i4s-n8-wXz.title" = "Knippen";
136 |
137 | /* Class = "NSMenuItem"; title = "Use Selection for Find"; ObjectID = "i9D-5H-yVn"; */
138 | "i9D-5H-yVn.title" = "Gebruik selectie om te zoeken";
139 |
140 | /* Class = "NSMenuItem"; title = "Edit"; ObjectID = "kM9-IE-xVd"; */
141 | "kM9-IE-xVd.title" = "Bewerken";
142 |
143 | /* Class = "NSMenuItem"; title = "Find Previous"; ObjectID = "p8c-hB-c6m"; */
144 | "p8c-hB-c6m.title" = "Vind vorige";
145 |
146 | /* Class = "NSMenuItem"; title = "Make Upper Case"; ObjectID = "qER-IV-GIL"; */
147 | "qER-IV-GIL.title" = "Maak Upper Case";
148 |
149 | /* Class = "NSMenuItem"; title = "Speech"; ObjectID = "qMm-Ld-wUf"; */
150 | "qMm-Ld-wUf.title" = "Spraak";
151 |
152 | /* Class = "NSMenu"; title = "Control"; ObjectID = "qkf-xU-qKS"; */
153 | "qkf-xU-qKS.title" = "Control";
154 |
155 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "qqN-o9-bKr"; */
156 | "qqN-o9-bKr.title" = "Opnieuw";
157 |
158 | /* Class = "NSMenuItem"; title = "Show Substitutions"; ObjectID = "tgC-ra-RuK"; */
159 | "tgC-ra-RuK.title" = "Laat vervangingen zien";
160 |
161 | /* Class = "NSMenu"; title = "WebShell"; ObjectID = "uQy-DD-JDr"; */
162 | "uQy-DD-JDr.title" = "WebShell";
163 |
164 | /* Class = "NSMenuItem"; title = "Smart Links"; ObjectID = "vSL-Er-Dzn"; */
165 | "vSL-Er-Dzn.title" = "Slimme links";
166 |
167 | /* Class = "NSMenuItem"; title = "Find"; ObjectID = "vur-Ee-IsR"; */
168 | "vur-Ee-IsR.title" = "Vind";
169 |
170 | /* Class = "NSTextFieldCell"; title = "Launching..."; ObjectID = "y40-R0-LYw"; */
171 | "y40-R0-LYw.title" = "Opstarten...";
172 |
173 | /* Class = "NSMenuItem"; title = "Reload"; ObjectID = "ynQ-Ma-mpv"; */
174 | "ynQ-Ma-mpv.title" = "Herladen";
175 |
176 | /* Class = "NSMenuItem"; title = "Find and Replace…"; ObjectID = "zd7-xr-xod"; */
177 | "zd7-xr-xod.title" = "Zoek en vervang…";
178 |
--------------------------------------------------------------------------------
/WebShell/WSInjector.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellInjector.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 |
13 | /**
14 | This extension will catch up with the webhooks!
15 | - Note: @wdg: Iframes, Webhooks, and more. (Issue: #23, #5, #2, #35, #38, #39 & More)
16 | */
17 | @objc extension ViewController {
18 | /**
19 | Loop Trough iFrames
20 |
21 | This function will loop trough different frames (if they exists) and will inject all the javascript what they want!
22 |
23 | - Note: @wdg Fix for iFrames (Issue #23)
24 | */
25 | func loopThroughiFrames() {
26 | if (mainWebview.subviews.count > 0) {
27 | // We've got subViews!
28 |
29 | if (mainWebview.subviews[0].subviews.count > 0) {
30 | // mainWebview.subviews[0] = WebFrameView
31 |
32 | let goodKids = mainWebview.subviews[0].subviews[0]
33 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView (= goodKids)
34 |
35 | var children = goodKids.subviews[0]
36 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView.subviews[0] = WebClipView (= children)
37 |
38 | // We need > 0 subviews here, otherwise don't add them. and the script will continue
39 | if children.subviews.count > 0 {
40 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView.subviews[0] = WebClipView.subviews[0] = WebHTMLView
41 | children = goodKids.subviews[0].subviews[0]
42 | }
43 |
44 | // Finally. parsing those good old iframes
45 | // We don't check them for iframes, somewhere the fun must be ended.
46 | for child in children.subviews {
47 | Dprint("Found a Child...")
48 | Ddump(child)
49 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView.subviews[0] = WebClipView.subviews[0] = WebHTMLView.subviews[x] = WebFrameView (Finally) (name = child)
50 | if (child.isKind(of: WebFrameView.self)) {
51 | let frame: NSView = child
52 | let context: JSContext = frame.webFrame.javaScriptContext
53 |
54 | Dprint("Injecting hooks!")
55 | injectWebhooks(context)
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
62 | /**
63 | InjectWebhooks
64 |
65 | Injects javascript in to a frame, or other position
66 |
67 | - Parameter jsContext: JSContext!
68 |
69 | - Note: @wdg Fixes a lot (Issues #23, #5, #2, #35, #38, #39 & More.)
70 | */
71 | func injectWebhooks(_ jsContext: JSContext!) {
72 | // Injecting javascript (via jsContext)
73 |
74 | // @wdg Hack URL's if settings is set.
75 | // Issue: #5
76 | if ((WebShellSettings["openInNewScreen"] as? Bool) != false) {
77 | // _blank to external
78 | // JavaScript -> Select all
79 | jsContext.evaluateScript("var links=document.querySelectorAll('a');for(var i=0;i Select all
83 | jsContext.evaluateScript("var links=document.querySelectorAll('a');for(var i=0;i Void = {(title: NSString?, message: NSString?, icon: NSString?) in
90 | self.makeNotification(title!, message: message!, icon: icon!)
91 | }
92 | jsContext.objectForKeyedSubscript("Notification").setObject(unsafeBitCast(myNofification, to: AnyObject.self), forKeyedSubscript: "note" as (NSCopying & NSObjectProtocol)!)
93 |
94 | // Add console.log ;)
95 | // Add Console.log (and console.error, and console.warn)
96 | if (WebShellSettings["consoleSupport"] as! Bool) {
97 | jsContext.evaluateScript("var console = {log: function () {var message = '';for (var i = 0; i < arguments.length; i++) {message += arguments[i] + ' '};console.print(message)},warn: function () {var message = '';for (var i = 0; i < arguments.length; i++) {message += arguments[i] + ' '};console.print(message)},error: function () {var message = '';for (var i = 0; i < arguments.length; i++){message += arguments[i] + ' '};console.print(message)}};")
98 | let logFunction: @convention(block)(NSString!) -> Void = {(message: NSString!) in
99 | print("JS: \(message)")
100 | }
101 | jsContext.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, to: AnyObject.self), forKeyedSubscript: "print" as (NSCopying & NSObjectProtocol)!)
102 | }
103 |
104 | // @wdg Add support for target=_blank
105 | // Issue: #5
106 | // Fake window.app Library.
107 | jsContext.evaluateScript("var WSApp={};") ;
108 |
109 | // _blank external
110 | let openInBrowser: @convention(block)(NSString!) -> Void = {(url: NSString!) in
111 | NSWorkspace.shared.open(URL(string: (url as String))!)
112 | }
113 |
114 | // _blank internal
115 | let openNow: @convention(block)(NSString!) -> Void = {(url: NSString!) in
116 | self.loadUrl((url as String))
117 | }
118 | // _blank external
119 | jsContext.objectForKeyedSubscript("WSApp").setObject(unsafeBitCast(openInBrowser, to: AnyObject.self), forKeyedSubscript: "openExternal" as (NSCopying & NSObjectProtocol)!)
120 |
121 | // _blank internal
122 | jsContext.objectForKeyedSubscript("WSApp").setObject(unsafeBitCast(openNow, to: AnyObject.self), forKeyedSubscript: "openInternal" as (NSCopying & NSObjectProtocol)!)
123 |
124 | // @wdg Add Print Support
125 | // Issue: #39
126 | // window.print()
127 | let printMe: @convention(block)(NSString?) -> Void = {(url: NSString?) in self.printThisPage(self)}
128 | jsContext.objectForKeyedSubscript("window").setObject(unsafeBitCast(printMe, to: AnyObject.self), forKeyedSubscript: "print" as (NSCopying & NSObjectProtocol)!)
129 |
130 | // navigator.getBattery()
131 | jsContext.objectForKeyedSubscript("navigator").setObject(BatteryManager.self, forKeyedSubscript: "battery" as (NSCopying & NSObjectProtocol)!)
132 |
133 | jsContext.evaluateScript("window.navigator.getBattery = window.navigator.battery.getBattery;")
134 |
135 | // navigator.vibrate
136 | let vibrateNow: @convention(block)(NSString!) -> Void = {(data: NSString!) in
137 | self.flashScreen(data)
138 | }
139 | jsContext.objectForKeyedSubscript("navigator").setObject(unsafeBitCast(vibrateNow, to: AnyObject.self), forKeyedSubscript: "vibrate" as (NSCopying & NSObjectProtocol)!)
140 |
141 | // @wdg Add localstorage Support
142 | // Issue: #25
143 | let saveToLocal: @convention(block)(NSString?, NSString?) -> Void = {(key: NSString?, value: NSString?) in
144 | let host: String = (self.mainWebview.mainFrame.dataSource?.request.url?.host)!
145 | let newKey = String(describing: "WSLS:\(host):\(key ?? "?")")
146 |
147 | UserDefaults.standard.setValue(value, forKey: newKey)
148 | }
149 |
150 | let getFromLocal: @convention(block)(NSString!) -> String = {(key: NSString!) in
151 | let host: String = (self.mainWebview.mainFrame.dataSource?.request.url?.host)!
152 | if let LSvalue = key {
153 | let newKey = "WSLS:\(host):\(LSvalue)"
154 |
155 | let val = UserDefaults.standard.value(forKey: newKey as String)
156 |
157 | if let myVal = val as? String {
158 | return String(myVal)
159 | }
160 | else {
161 | return "null"
162 | }
163 | } else {
164 | return "null"
165 | }
166 | }
167 |
168 | jsContext.objectForKeyedSubscript("localStorage").setObject(unsafeBitCast(saveToLocal, to: AnyObject.self), forKeyedSubscript: "setItem" as (NSCopying & NSObjectProtocol)!)
169 | jsContext.objectForKeyedSubscript("localStorage").setObject(unsafeBitCast(getFromLocal, to: AnyObject.self), forKeyedSubscript: "getItem" as (NSCopying & NSObjectProtocol)!)
170 |
171 | // @wdg Support for window.open (popup)
172 | // Issue: #21
173 | // openNewWindow(url: "THEURL", height: "0", width: "0")
174 | // window.open(URL, name, specs, replace)
175 | let windowOpen: @convention(block)(NSString?, NSString?, NSString?, NSString?) -> Void = {(url: NSString?, target: NSString?, specs: NSString?, replace: NSString?) in
176 | self.parseWindowOpen(url! as String, options: specs! as String)
177 | }
178 | jsContext.objectForKeyedSubscript("window").setObject(unsafeBitCast(windowOpen, to: AnyObject.self), forKeyedSubscript: "open" as (NSCopying & NSObjectProtocol)!)
179 |
180 | // Get window.webshell
181 | let nsObject: Any? = Bundle.main.infoDictionary!["CFBundleShortVersionString"]
182 | jsContext.evaluateScript("window.webshell={version:'\(nsObject as! String)'};webshell=window.webshell;")
183 |
184 | // @wdg memorize credentials?
185 | // Issue: #74
186 | _injectPasswordFor(jsContext, website: jsContext.evaluateScript("window.location.host").toString())
187 | _injectPasswordListener(jsContext, website: jsContext.evaluateScript("window.location.host").toString())
188 | let savePassword: @convention(block)(NSString?, NSString?) -> Void = {(username: NSString!, password: NSString!) in
189 | self._savePasswordFor(jsContext,
190 | website: jsContext.evaluateScript("window.location.host").toString(),
191 | username: (username as String),
192 | password: (password as String)
193 | )
194 | }
195 | jsContext.objectForKeyedSubscript("WSApp").setObject(unsafeBitCast(savePassword, to: AnyObject.self), forKeyedSubscript: "savePassword" as (NSCopying & NSObjectProtocol)!)
196 |
197 | _WSInjectCSS(jsContext)
198 | _WSInjectJS(jsContext)
199 | }
200 |
201 | /**
202 | Add Localstorage Support
203 |
204 | - Parameter Sender: AnyObject?
205 |
206 | - Note: @wdg #25
207 | */
208 | func resetLocalStorage(_ Sender: AnyObject?) -> Void {
209 | UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
210 | }
211 |
212 | /**
213 | Support for window.open (popup)
214 |
215 | - Parameter url: Url to open
216 | - Parameter options: Custom options, width, height
217 |
218 | - Note: @wdg #25
219 | */
220 | func parseWindowOpen(_ url: String, options: String) -> Void {
221 | // We ignore x and y. (initial position on the screen)
222 | // Using specifications of W3Schools: http://www.w3schools.com/jsref/met_win_open.asp
223 | // "Open a new window called "MsgWindow", and write some text into it" is not (yet) supported!
224 | var width = "0"
225 | var height = "0"
226 | let options = Array(options.components(separatedBy: ","))
227 |
228 | for i in 0 ..< options.count {
229 | var tmp = Array(options[i].components(separatedBy: "="))
230 |
231 | if (tmp[0] == "width" || tmp[0] == " width") {
232 | width = tmp[1]
233 | print("width=\(tmp[1])")
234 | }
235 | if (tmp[0] == "height" || tmp[0] == " height") {
236 | height = tmp[1]
237 | print("height=\(tmp[1])")
238 | }
239 | }
240 |
241 | // After parsing call
242 | openNewWindow(url: url, height: "\(height)", width: "\(width)")
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/WebShell/Core/WSInjector.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellInjector.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 |
12 |
13 | /**
14 | This extension will catch up with the webhooks!
15 | - Note: @wdg: Iframes, Webhooks, and more. (Issue: #23, #5, #2, #35, #38, #39 & More)
16 | */
17 | @objc extension WSViewController {
18 | /**
19 | Loop Trough iFrames
20 |
21 | This function will loop trough different frames (if they exists) and will inject all the javascript what they want!
22 |
23 | - Note: @wdg Fix for iFrames (Issue #23)
24 | */
25 | func loopThroughiFrames() {
26 | if (mainWebview.subviews.count > 0) {
27 | // We've got subViews!
28 |
29 | if (mainWebview.subviews[0].subviews.count > 0) {
30 | // mainWebview.subviews[0] = WebFrameView
31 |
32 | let goodKids = mainWebview.subviews[0].subviews[0]
33 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView (= goodKids)
34 |
35 | var children = goodKids.subviews[0]
36 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView.subviews[0] = WebClipView (= children)
37 |
38 | // We need > 0 subviews here, otherwise don't add them. and the script will continue
39 | if children.subviews.count > 0 {
40 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView.subviews[0] = WebClipView.subviews[0] = WebHTMLView
41 | children = goodKids.subviews[0].subviews[0]
42 | }
43 |
44 | // Finally. parsing those good old iframes
45 | // We don't check them for iframes, somewhere the fun must be ended.
46 | for child in children.subviews {
47 | Dprint("Found a Child...")
48 | Ddump(child)
49 | // mainWebview.subviews[0] = WebFrameView.subviews[0] = WebDynamicScrollBarsView.subviews[0] = WebClipView.subviews[0] = WebHTMLView.subviews[x] = WebFrameView (Finally) (name = child)
50 | if (child.isKind(of: WebFrameView.self)) {
51 | let frame: NSView = child
52 | let context: JSContext = frame.webFrame.javaScriptContext
53 |
54 | Dprint("Injecting hooks!")
55 | injectWebhooks(context)
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
62 | func injectLocalStorage (jsContext: JSContext!) {
63 | Dprint("Injecting localStorage hooks!")
64 |
65 | // @wdg Add localstorage Support
66 | // Issue: #25
67 | let saveToLocal: @convention(block)(NSString?, NSString?) -> Void = {(key: NSString?, value: NSString?) in
68 | let host: String = (self.mainWebview.mainFrame.dataSource?.request.url?.host)!
69 | let newKey = String(describing: "WSLS:\(host):\(key ?? "?")")
70 |
71 | UserDefaults.standard.setValue(value, forKey: newKey)
72 | }
73 |
74 | let getFromLocal: @convention(block)(NSString?) -> String = {(key: NSString!) in
75 | let host: String = (self.mainWebview.mainFrame.dataSource?.request.url?.host)!
76 | if let LSvalue = key {
77 | let newKey = "WSLS:\(host):\(LSvalue)"
78 |
79 | let val = UserDefaults.standard.value(forKey: newKey as String)
80 |
81 | if let myVal = val as? String {
82 | return String(myVal)
83 | }
84 | else {
85 | return "null"
86 | }
87 | } else {
88 | return "null"
89 | }
90 | }
91 |
92 | jsContext.objectForKeyedSubscript("localStorage").setObject(unsafeBitCast(saveToLocal, to: AnyObject.self), forKeyedSubscript: "setItem" as (NSCopying & NSObjectProtocol)!)
93 | jsContext.objectForKeyedSubscript("localStorage").setObject(unsafeBitCast(getFromLocal, to: AnyObject.self), forKeyedSubscript: "getItem" as (NSCopying & NSObjectProtocol)!)
94 | }
95 |
96 | /**
97 | InjectWebhooks
98 |
99 | Injects javascript in to a frame, or other position
100 |
101 | - Parameter jsContext: JSContext!
102 |
103 | - Note: @wdg Fixes a lot (Issues #23, #5, #2, #35, #38, #39 & More.)
104 | */
105 | func injectWebhooks(_ jsContext: JSContext!) {
106 | // Injecting javascript (via jsContext)
107 |
108 | // @wdg Hack URL's if settings is set.
109 | // Issue: #5
110 | if settings.openInNewScreen {
111 | // _blank to external
112 | // JavaScript -> Select all
113 | jsContext.evaluateScript("document.querySelector('body').addEventListener('click', function(evt) { if ( evt.target.target==='_blank') { WSApp.openExternal(evt.target.href); }}, true);")
114 | } else {
115 | // _blank to internal
116 | // JavaScript -> Select all
117 | jsContext.evaluateScript("document.querySelector('body').addEventListener('click', function(evt) { if ( evt.target.target==='_blank') { WSApp.openInternal(evt.target.href); }}, true);")
118 | }
119 |
120 | // @wdg Add support for target=_blank
121 | // Issue: #5
122 | // Fake window.app Library.
123 | jsContext.evaluateScript("var WSApp={};") ;
124 |
125 | // _blank external
126 | let openInBrowser: @convention(block)(NSString?) -> Void = {(url: NSString!) in
127 | NSWorkspace.shared.open(URL(string: (url as String))!)
128 | }
129 |
130 | // _blank internal
131 | let openNow: @convention(block)(NSString?) -> Void = {(url: NSString!) in
132 | self.loadUrl((url as String))
133 | }
134 | // _blank external
135 | jsContext.objectForKeyedSubscript("WSApp").setObject(unsafeBitCast(openInBrowser, to: AnyObject.self), forKeyedSubscript: "openExternal" as (NSCopying & NSObjectProtocol)!)
136 |
137 | // _blank internal
138 | jsContext.objectForKeyedSubscript("WSApp").setObject(unsafeBitCast(openNow, to: AnyObject.self), forKeyedSubscript: "openInternal" as (NSCopying & NSObjectProtocol)!)
139 |
140 | // @wdg Add Notification Support
141 | // Issue: #2, #35, #38 (webkitNotification)
142 | jsContext.evaluateScript("function Notification(myTitle, options){if(typeof options === 'object'){var body,icon,tag;if (typeof options['body'] !== 'undefined'){body=options['body']}if (typeof options['icon'] !== 'undefined'){Notification.note(myTitle, body, options['icon'])}else{Notification.note(myTitle, body)}}else{if(typeof options === 'string'){Notification.note(myTitle, options)}else{Notification.note(myTitle)}}}Notification.length=1;Notification.permission='granted';Notification.requestPermission=function(callback){if(typeof callback === 'function'){callback('granted');}else{return 'granted'}};window.Notification=Notification;window.webkitNotification=Notification;")
143 | let myNofification: @convention(block)(NSString?, NSString?, NSString?) -> Void = {(title: NSString?, message: NSString?, icon: NSString?) in
144 | self.makeNotification(title!, message: message!, icon: icon!)
145 | }
146 | jsContext.objectForKeyedSubscript("Notification").setObject(unsafeBitCast(myNofification, to: AnyObject.self), forKeyedSubscript: "note" as (NSCopying & NSObjectProtocol)!)
147 |
148 | // Add console.log ;)
149 | // Add Console.log (and console.error, and console.warn)
150 | if settings.consoleSupport {
151 | jsContext.evaluateScript("var console = {log: function () {var message = '';for (var i = 0; i < arguments.length; i++) {message += arguments[i] + ' '};console.print(message)},warn: function () {var message = '';for (var i = 0; i < arguments.length; i++) {message += arguments[i] + ' '};console.print(message)},error: function () {var message = '';for (var i = 0; i < arguments.length; i++){message += arguments[i] + ' '};console.print(message)}};")
152 | let logFunction: @convention(block)(NSString?) -> Void = {(message: NSString!) in
153 | print("JS: \(message)")
154 | }
155 | jsContext.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, to: AnyObject.self), forKeyedSubscript: "print" as (NSCopying & NSObjectProtocol)!)
156 | }
157 |
158 | // @wdg Add Print Support
159 | // Issue: #39
160 | // window.print()
161 | let printMe: @convention(block)(NSString?) -> Void = {(url: NSString?) in self.printThisPage(self)}
162 | jsContext.objectForKeyedSubscript("window").setObject(unsafeBitCast(printMe, to: AnyObject.self), forKeyedSubscript: "print" as (NSCopying & NSObjectProtocol)!)
163 |
164 | // navigator.getBattery()
165 | jsContext.objectForKeyedSubscript("navigator").setObject(BatteryManager.self, forKeyedSubscript: "battery" as (NSCopying & NSObjectProtocol)!)
166 |
167 | jsContext.evaluateScript("window.navigator.getBattery = window.navigator.battery.getBattery;")
168 |
169 | // navigator.vibrate
170 | let vibrateNow: @convention(block)(NSString?) -> Void = {(data: NSString!) in
171 | self.flashScreen(data)
172 | }
173 | jsContext.objectForKeyedSubscript("navigator").setObject(unsafeBitCast(vibrateNow, to: AnyObject.self), forKeyedSubscript: "vibrate" as (NSCopying & NSObjectProtocol)!)
174 |
175 | self.injectLocalStorage(jsContext: jsContext)
176 |
177 | // @wdg Support for window.open (popup)
178 | // Issue: #21
179 | // openNewWindow(url: "THEURL", height: "0", width: "0")
180 | // window.open(URL, name, specs, replace)
181 | let windowOpen: @convention(block)(NSString?, NSString?, NSString?, NSString?) -> Void = {(url: NSString?, target: NSString?, specs: NSString?, replace: NSString?) in
182 | self.parseWindowOpen(url! as String, options: specs! as String)
183 | }
184 | jsContext.objectForKeyedSubscript("window").setObject(unsafeBitCast(windowOpen, to: AnyObject.self), forKeyedSubscript: "open" as (NSCopying & NSObjectProtocol)!)
185 |
186 | // Get window.webshell
187 | let nsObject: Any? = Bundle.main.infoDictionary!["CFBundleShortVersionString"]
188 | jsContext.evaluateScript("window.webshell={version:'\(nsObject as! String)'};webshell=window.webshell;")
189 |
190 | // @wdg memorize credentials?
191 | // Issue: #74
192 | _injectPasswordFor(jsContext, website: jsContext.evaluateScript("window.location.host").toString())
193 | _injectPasswordListener(jsContext, website: jsContext.evaluateScript("window.location.host").toString())
194 | let savePassword: @convention(block)(NSString?, NSString?) -> Void = {(username: NSString!, password: NSString!) in
195 | self._savePasswordFor(jsContext,
196 | website: jsContext.evaluateScript("window.location.host").toString(),
197 | username: (username as String),
198 | password: (password as String)
199 | )
200 | }
201 | jsContext.objectForKeyedSubscript("WSApp").setObject(unsafeBitCast(savePassword, to: AnyObject.self), forKeyedSubscript: "savePassword" as (NSCopying & NSObjectProtocol)!)
202 |
203 | _WSInjectCSS(jsContext)
204 | _WSInjectJS(jsContext)
205 | }
206 |
207 | /**
208 | Add Localstorage Support
209 |
210 | - Parameter Sender: AnyObject?
211 |
212 | - Note: @wdg #25
213 | */
214 | func resetLocalStorage(_ Sender: AnyObject?) -> Void {
215 | UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
216 | }
217 |
218 | /**
219 | Support for window.open (popup)
220 |
221 | - Parameter url: Url to open
222 | - Parameter options: Custom options, width, height
223 |
224 | - Note: @wdg #25
225 | */
226 | func parseWindowOpen(_ url: String, options: String) -> Void {
227 | // We ignore x and y. (initial position on the screen)
228 | // Using specifications of W3Schools: http://www.w3schools.com/jsref/met_win_open.asp
229 | // "Open a new window called "MsgWindow", and write some text into it" is not (yet) supported!
230 | var width = "0"
231 | var height = "0"
232 | let options = Array(options.components(separatedBy: ","))
233 |
234 | for i in 0 ..< options.count {
235 | var tmp = Array(options[i].components(separatedBy: "="))
236 |
237 | if (tmp[0] == "width" || tmp[0] == " width") {
238 | width = tmp[1]
239 | print("width=\(tmp[1])")
240 | }
241 | if (tmp[0] == "height" || tmp[0] == " height") {
242 | height = tmp[1]
243 | print("height=\(tmp[1])")
244 | }
245 | }
246 |
247 | // After parsing call
248 | openNewWindow(url: url, height: "\(height)", width: "\(width)")
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/WebShell/Core/WSDebug.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellDebug.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 | // This is generated by swift, i dont know the reason,
12 | // but i'm not removing it.
13 | fileprivate func < (lhs: T?, rhs: T?) -> Bool {
14 | switch (lhs, rhs) {
15 | case let (l?, r?):
16 | return l < r
17 | case (nil, _?):
18 | return true
19 | default:
20 | return false
21 | }
22 | }
23 |
24 | fileprivate func > (lhs: T?, rhs: T?) -> Bool {
25 | switch (lhs, rhs) {
26 | case let (l?, r?):
27 | return l > r
28 | default:
29 | return rhs < lhs
30 | }
31 | }
32 |
33 |
34 | // @wdg Add Debug support
35 | // Issue: None.
36 | // This extension will handle the Debugging options.
37 | extension WSViewController {
38 |
39 | /**
40 | Override settings via commandline
41 |
42 | Used for popups, and debug options.
43 | */
44 | func checkSettings() -> Void {
45 | // Need to overwrite settings?
46 | if (CommandLine.argc > 0) {
47 | for i in stride(from: 1, to: Int(CommandLine.argc), by: 2) {
48 | // for (var i = 1; i < Int(Process.argc) ; i = i + 2) {
49 | if ((String(describing: CommandLine.arguments[i])) == "-NSDocumentRevisionsDebugMode") {
50 | if ((String(describing: CommandLine.arguments[i + 1])) == "YES") {
51 | settings.debugmode = true
52 | settings.consoleSupport = true
53 | }
54 | }
55 |
56 | if ((String(describing: Process().arguments?[i])).uppercased() == "-DEBUG") {
57 | if ((String(describing: Process().arguments![i + 1])).uppercased() == "YES" || (String(describing: Process().arguments?[i + 1])).uppercased() == "true") {
58 | settings.debugmode = true
59 | settings.consoleSupport = true
60 | }
61 | }
62 |
63 | if ((String(describing: CommandLine.arguments[i])) == "-dump-args") {
64 | self._debugDumpArguments("" as AnyObject)
65 | }
66 |
67 | if ((String(describing: CommandLine.arguments[i])) == "-url") {
68 | settings.url = String(CommandLine.arguments[i + 1])
69 | }
70 |
71 | if ((String(describing: CommandLine.arguments[i])) == "-height") {
72 | settings.initialWindowHeight = (Int(CommandLine.arguments[i + 1]) > 250) ? Int(CommandLine.arguments[i + 1])! : 250
73 | }
74 |
75 | if ((String(describing: CommandLine.arguments[i])) == "-width") {
76 | settings.initialWindowWidth = (Int(CommandLine.arguments[i + 1]) > 250) ? Int(CommandLine.arguments[i + 1])! : 250
77 | }
78 | }
79 | }
80 |
81 | initWindow()
82 | }
83 |
84 | /**
85 | Edit contextmenu...
86 |
87 | @wdg Fix contextmenu (problem with the swift 3 update)
88 |
89 | Issue: #61
90 | */
91 | func webView(_ sender: WebView!, contextMenuItemsForElement element: [AnyHashable : Any]!, defaultMenuItems: [Any]!) -> [Any]! {
92 | //Swift 2..
93 | //func webView(_ sender: WebView!, contextMenuItemsForElement element: [NSObject: Any]!, defaultMenuItems: [Any]!) -> [Any]!
94 |
95 | // @wdg Fix contextmenu (problem with the swift 2 update #50)
96 | // Issue: #51
97 | var download = false
98 |
99 | for i in defaultMenuItems {
100 | // Oh! download link available!
101 | if (String(describing: (i as AnyObject).title).contains("Download")) {
102 | download = true
103 | }
104 |
105 | // Get inspect element!
106 | if (String(describing: (i as AnyObject).title).contains("Element")) {
107 | for x in 0 ..< defaultMenuItems.count {
108 | if (String(describing: defaultMenuItems[x]).contains("Element")) {
109 | IElement = defaultMenuItems[x] as! NSMenuItem
110 | }
111 | }
112 | }
113 | }
114 |
115 | var NewMenu: [AnyObject] = [AnyObject]()
116 |
117 | // if can back
118 | if settings.cmBackAndForward {
119 | if (mainWebview.canGoBack) {
120 | NewMenu.append(NSMenuItem(title: "Back", action: #selector(WSViewController._goBack(_:)), keyEquivalent: ""))
121 | }
122 | if (mainWebview.canGoForward) {
123 | NewMenu.append(NSMenuItem(title: "Forward", action: #selector(WSViewController._goForward(_:)), keyEquivalent: ""))
124 | }
125 | }
126 | if settings.cmReload {
127 | NewMenu.append(NSMenuItem(title: "Reload", action: #selector(WSViewController._reloadPage(_:)), keyEquivalent: ""))
128 | }
129 |
130 | if (download) {
131 | if (element["WebElementLinkURL"] != nil) {
132 | lastURL = element["WebElementLinkURL"]! as! URL
133 |
134 | if settings.cmDownload || settings.cmNewWindow {
135 | NewMenu.append(NSMenuItem.separator())
136 |
137 | if settings.cmNewWindow {
138 | NewMenu.append(NSMenuItem(title: "Open Link in a new Window", action: #selector(WSViewController.createNewInstance(_:)), keyEquivalent: ""))
139 | }
140 | if settings.cmDownload {
141 | NewMenu.append(NSMenuItem(title: "Download Linked File", action: #selector(WSViewController.downloadFileWithURL(_:)), keyEquivalent: ""))
142 | }
143 | }
144 | }
145 | }
146 |
147 | NewMenu.append(NSMenuItem.separator())
148 | // Add debug menu. (if enabled)
149 |
150 | if settings.debugmode {
151 | let debugMenu = NSMenu(title: "Debug")
152 | if (IElement.title != "NSMenuItem") {
153 | debugMenu.addItem(IElement) // <-- Inspect element...
154 | }
155 | debugMenu.addItem(NSMenuItem(title: "Open New window", action: #selector(WSViewController._debugNewWindow(_:)), keyEquivalent: ""))
156 | debugMenu.addItem(NSMenuItem(title: "Print arguments", action: #selector(WSViewController._debugDumpArguments(_:)), keyEquivalent: ""))
157 | debugMenu.addItem(NSMenuItem(title: "Open URL", action: #selector(WSViewController._openURL(_:)), keyEquivalent: ""))
158 | debugMenu.addItem(NSMenuItem(title: "Report an issue on this page", action: #selector(WSViewController._reportThisPage(_:)), keyEquivalent: ""))
159 | debugMenu.addItem(NSMenuItem(title: "Print this page", action: #selector(WSViewController._printThisPage(_:)), keyEquivalent: "")) // Stupid swift 2.2 does not look in extensions.
160 | debugMenu.addItem(NSMenuItem.separator())
161 | debugMenu.addItem(NSMenuItem(title: "Fire some random Notifications", action: #selector(WSViewController.__sendNotifications(_:)), keyEquivalent: ""))
162 | debugMenu.addItem(NSMenuItem(title: "Reset localstorage", action: #selector(WSViewController.resetLocalStorage(_:)), keyEquivalent: ""))
163 |
164 | let WSdeveloperMenu = NSMenu(title: "WS Developer")
165 | WSdeveloperMenu.addItem(NSMenuItem(title: "Inject Javascript", action: #selector(WSViewController._injectJS(_:)), keyEquivalent: ""))
166 | WSdeveloperMenu.addItem(NSMenuItem(title: "What the web can do", action: #selector(WSViewController._WWCDT(_:)), keyEquivalent: ""))
167 |
168 | let WSDevMenu = NSMenuItem(title: "WebShell Developer", action: #selector(WSViewController._doNothing(_:)), keyEquivalent: "")
169 | WSDevMenu.submenu = WSdeveloperMenu
170 | debugMenu.addItem(WSDevMenu)
171 |
172 | let item = NSMenuItem(title: "Debug", action: #selector(WSViewController._doNothing(_:)), keyEquivalent: "")
173 | item.submenu = debugMenu
174 |
175 | NewMenu.append(item)
176 | NewMenu.append(NSMenuItem.separator())
177 | }
178 |
179 | NewMenu.append(NSMenuItem(title: "Quit", action: #selector(WSViewController._quit(_:)), keyEquivalent: ""))
180 |
181 | return NewMenu
182 | }
183 |
184 | /**
185 | Debug: Quit WebShell
186 |
187 | - Parameter Sender: Anyobject
188 | */
189 |
190 | @objc func _quit(_ Sender: AnyObject) -> Void {
191 | exit(0)
192 | }
193 |
194 | /**
195 | Debug: doNothing
196 |
197 | - Parameter Sender: Anyobject
198 | */
199 | @objc func _doNothing(_ Sender: AnyObject) -> Void {
200 | // _doNothing
201 | }
202 |
203 | /**
204 | Debug: Open new window
205 |
206 | - Parameter Sender: Anyobject
207 | */
208 | @objc func _debugNewWindow(_ Sender: AnyObject) -> Void {
209 | openNewWindow(url: "https://www.google.nl/search?client=WebShell&rls=en&q=new+window", height: "0", width: "0")
210 | }
211 |
212 | /**
213 | Debug: Print arguments
214 |
215 | - Parameter Sender: Anyobject
216 | */
217 | @objc func _debugDumpArguments(_ Sender: AnyObject) -> Void {
218 | print(CommandLine.arguments)
219 | }
220 |
221 | /**
222 | Debug: Fire 10 notifications (Timer)
223 |
224 | - Parameter Sender: Anyobject
225 | */
226 | @objc func __sendNotifications(_ Sender: AnyObject) -> Void {
227 | // Minimize app
228 | NSApplication.shared.keyWindow?.miniaturize(self)
229 |
230 | // Fire 10 Notifications
231 | Timer.scheduledTimer(timeInterval: TimeInterval(05), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
232 | Timer.scheduledTimer(timeInterval: TimeInterval(15), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
233 | Timer.scheduledTimer(timeInterval: TimeInterval(25), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
234 | Timer.scheduledTimer(timeInterval: TimeInterval(35), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
235 | Timer.scheduledTimer(timeInterval: TimeInterval(45), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
236 | Timer.scheduledTimer(timeInterval: TimeInterval(55), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
237 | Timer.scheduledTimer(timeInterval: TimeInterval(65), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
238 | Timer.scheduledTimer(timeInterval: TimeInterval(75), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
239 | Timer.scheduledTimer(timeInterval: TimeInterval(85), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
240 | Timer.scheduledTimer(timeInterval: TimeInterval(95), target: self, selector: #selector(WSViewController.___sendNotifications), userInfo: nil, repeats: false)
241 | }
242 |
243 | /**
244 | Debug: Send 10 Notifications (real sending)
245 |
246 | - Parameter Sender: Anyobject
247 | */
248 | @objc func ___sendNotifications() -> Void {
249 | // Minimize app
250 | if (NSApplication.shared.keyWindow?.isMiniaturized == false) {
251 | NSApplication.shared.keyWindow?.miniaturize(self)
252 | }
253 |
254 | // Send Actual notification.
255 | makeNotification("Test Notification", message: "Hi!", icon: "https://camo.githubusercontent.com/ee999b2d8fa5413229fdc69e0b53144f02b7b840/687474703a2f2f376d6e6f79372e636f6d312e7a302e676c622e636c6f7564646e2e636f6d2f7765627368656c6c2f6c6f676f2e706e673f696d616765566965772f322f772f313238")
256 | }
257 |
258 | /**
259 | Debug: Open URL
260 |
261 | - Parameter Sender: Anyobject
262 | */
263 | @objc func _openURL(_ Sender: AnyObject) -> Void {
264 | let msg = NSAlert()
265 | msg.addButton(withTitle: "OK") // 1st button
266 | msg.addButton(withTitle: "Cancel") // 2nd button
267 | msg.messageText = "URL"
268 | msg.informativeText = "Where you need to go?"
269 |
270 | let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
271 | txt.stringValue = "http://"
272 |
273 | msg.accessoryView = txt
274 | let response: NSApplication.ModalResponse = msg.runModal()
275 |
276 | if (response == NSApplication.ModalResponse.alertFirstButtonReturn) {
277 | self.loadUrl(txt.stringValue)
278 | }
279 | }
280 |
281 | /**
282 | Debug: WhatTheWebCanDo.Today
283 |
284 | - Parameter Sender: Anyobject
285 | */
286 | @objc func _WWCDT(_ Sender: AnyObject) -> Void {
287 | self.loadUrl("https://whatwebcando.today")
288 | }
289 |
290 | /**
291 | Debug: Inject custom javascript
292 |
293 | - Parameter Sender: Anyobject
294 | */
295 | @objc func _injectJS(_ Sender: AnyObject) -> Void {
296 | let msg = NSAlert()
297 | msg.addButton(withTitle: "OK") // 1st button
298 | msg.addButton(withTitle: "Cancel") // 2nd button
299 | msg.messageText = "Inject Javascript"
300 | msg.informativeText = "Inject Javascript\nBe Carefull!"
301 |
302 | let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 400, height: 400))
303 | txt.stringValue = ""
304 | txt.translatesAutoresizingMaskIntoConstraints = true
305 |
306 | msg.accessoryView = txt
307 | let response: NSApplication.ModalResponse = msg.runModal()
308 |
309 | if (response == NSApplication.ModalResponse.alertFirstButtonReturn) {
310 | let JSReturn: String = mainWebview.stringByEvaluatingJavaScript(from: txt.stringValue)
311 |
312 | let RetVal = NSAlert()
313 | RetVal.addButton(withTitle: "OK")
314 | RetVal.messageText = "Injected Javascript"
315 | RetVal.informativeText = JSReturn != "" ? JSReturn : "Finished"
316 | RetVal.runModal()
317 | }
318 | }
319 |
320 | /**
321 | Debug: Report this page, as containing an error.
322 |
323 | - Parameter Sender: Anyobject
324 | */
325 | @objc func _reportThisPage(_ Sender: AnyObject) -> Void {
326 | let currentUrl: String = (mainWebview.mainFrame.dataSource?.request.url?.absoluteString)!
327 | let host: String = (mainWebview.mainFrame.dataSource?.request.url?.host)!
328 |
329 | let issue: String = String("Problem loading \(host)").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!.replacingOccurrences(of: "&", with: "%26")
330 | var body: String = (String("There is a problem loading \(currentUrl)").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)?.replacingOccurrences(of: "&", with: "%26"))!
331 | body.append("%0D%0AThe%20problem%20is%3A%0D%0A...")
332 |
333 | let url: String = "https://github.com/djyde/WebShell/issues/new?title=\(issue)&body=\(body)"
334 |
335 | NSWorkspace.shared.open(URL(string: (url as String))!)
336 | }
337 |
338 | /**
339 | Print this page
340 |
341 | - Parameter Sender: Anyobject
342 |
343 | - Notes Stupid swift 2.2 & 3 does not look in extensions.
344 | - Notes so we'll copy again...
345 | - Notes @wdg Add Print Support
346 | - Notes Issue: #39
347 | */
348 | @objc func _printThisPage(_ Sender: AnyObject? = nil) -> Void {
349 | let url = mainWebview.mainFrame.dataSource?.request?.url?.absoluteString
350 |
351 | let operation: NSPrintOperation = NSPrintOperation(view: mainWebview)
352 | operation.jobTitle = "Printing \(url!)"
353 |
354 | // If want to print landscape
355 | operation.printInfo.orientation = NSPrintInfo.PaperOrientation.landscape
356 | operation.printInfo.scalingFactor = 0.7
357 |
358 | if operation.run() {
359 | print("Printed?")
360 | }
361 | }
362 |
363 | /**
364 | Go Back
365 |
366 | - Parameter Sender: Anyobject
367 | */
368 | @objc func _goBack(_ Sender: AnyObject) -> Void {
369 | if (mainWebview.canGoBack) {
370 | mainWebview.goBack(Sender)
371 | }
372 | }
373 |
374 | /**
375 | Go Forward
376 |
377 | - Parameter Sender: Anyobject
378 | */
379 | @objc func _goForward(_ Sender: AnyObject) -> Void {
380 | if (mainWebview.canGoForward) {
381 | mainWebview.goForward(Sender)
382 | }
383 | }
384 |
385 | /**
386 | Reload page
387 |
388 | - Parameter Sender: Anyobject
389 | */
390 | @objc func _reloadPage(_ Sender: AnyObject) -> Void {
391 | mainWebview.reload(Sender)
392 | }
393 |
394 | /**
395 | Debug: Open in a new window
396 |
397 | - Parameter Sender: Anyobject
398 | */
399 | @objc func createNewInstance(_ Sender: AnyObject) -> Void {
400 | openNewWindow(url: "\(lastURL)", height: "0", width: "0")
401 | }
402 |
403 | /**
404 | Download file
405 |
406 | - Parameter Sender: Anyobject
407 | */
408 | @objc func downloadFileWithURL(_ Sender: AnyObject) -> Void {
409 | let wsDM = WebShelllDownloadManager(url: lastURL)
410 | wsDM.endDownloadTask()
411 | }
412 |
413 | /**
414 | If in debugmode -> Print
415 |
416 | - Parameter S: Any
417 | */
418 | func Dprint(_ S: Any) -> Void {
419 | if settings.debugmode {
420 | print(S)
421 | }
422 | }
423 |
424 | /**
425 | If in debugmode -> Dump
426 |
427 | - Parameter S: Any
428 | */
429 | func Ddump(_ S: Any) -> Void {
430 | if settings.debugmode {
431 | dump(S)
432 | }
433 | }
434 | }
435 |
--------------------------------------------------------------------------------
/WebShell/WSDebug.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebShellDebug.swift
3 | // WebShell
4 | //
5 | // Created by Wesley de Groot on 31-01-16.
6 | // Copyright © 2016 RandyLu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WebKit
11 | // This is generated by swift, i dont know the reason,
12 | // but i'm not removing it.
13 | fileprivate func < (lhs: T?, rhs: T?) -> Bool {
14 | switch (lhs, rhs) {
15 | case let (l?, r?):
16 | return l < r
17 | case (nil, _?):
18 | return true
19 | default:
20 | return false
21 | }
22 | }
23 |
24 | fileprivate func > (lhs: T?, rhs: T?) -> Bool {
25 | switch (lhs, rhs) {
26 | case let (l?, r?):
27 | return l > r
28 | default:
29 | return rhs < lhs
30 | }
31 | }
32 |
33 |
34 | // @wdg Add Debug support
35 | // Issue: None.
36 | // This extension will handle the Debugging options.
37 | extension ViewController {
38 |
39 | /**
40 | Override settings via commandline
41 |
42 | Used for popups, and debug options.
43 | */
44 | func checkSettings() -> Void {
45 | // Need to overwrite settings?
46 | if (CommandLine.argc > 0) {
47 | for i in stride(from: 1, to: Int(CommandLine.argc), by: 2) {
48 | // for (var i = 1; i < Int(Process.argc) ; i = i + 2) {
49 | if ((String(describing: CommandLine.arguments[i])) == "-NSDocumentRevisionsDebugMode") {
50 | if ((String(describing: CommandLine.arguments[i + 1])) == "YES") {
51 | WebShellSettings["debugmode"] = true
52 | WebShellSettings["consoleSupport"] = true
53 | }
54 | }
55 |
56 | if ((String(describing: Process().arguments?[i])).uppercased() == "-DEBUG") {
57 | if ((String(describing: Process().arguments![i + 1])).uppercased() == "YES" || (String(describing: Process().arguments?[i + 1])).uppercased() == "true") {
58 | WebShellSettings["debugmode"] = true
59 | WebShellSettings["consoleSupport"] = true
60 | }
61 | }
62 |
63 | if ((String(describing: CommandLine.arguments[i])) == "-dump-args") {
64 | self._debugDumpArguments("" as AnyObject)
65 | }
66 |
67 | if ((String(describing: CommandLine.arguments[i])) == "-url") {
68 | WebShellSettings["url"] = String(CommandLine.arguments[i + 1])
69 | }
70 |
71 | if ((String(describing: CommandLine.arguments[i])) == "-height") {
72 | WebShellSettings["initialWindowHeight"] = (Int(CommandLine.arguments[i + 1]) > 250) ? Int(CommandLine.arguments[i + 1]) : Int(250)
73 | }
74 |
75 | if ((String(describing: CommandLine.arguments[i])) == "-width") {
76 | WebShellSettings["initialWindowWidth"] = (Int(CommandLine.arguments[i + 1]) > 250) ? Int(CommandLine.arguments[i + 1]) : Int(250)
77 | }
78 | }
79 | }
80 |
81 | initWindow()
82 | }
83 |
84 | /**
85 | Edit contextmenu...
86 |
87 | @wdg Fix contextmenu (problem with the swift 3 update)
88 |
89 | Issue: #61
90 | */
91 | func webView(_ sender: WebView!, contextMenuItemsForElement element: [AnyHashable : Any]!, defaultMenuItems: [Any]!) -> [Any]! {
92 | //Swift 2..
93 | //func webView(_ sender: WebView!, contextMenuItemsForElement element: [NSObject: Any]!, defaultMenuItems: [Any]!) -> [Any]!
94 |
95 | // @wdg Fix contextmenu (problem with the swift 2 update #50)
96 | // Issue: #51
97 | var download = false
98 |
99 | for i in defaultMenuItems {
100 | // Oh! download link available!
101 | if (String(describing: (i as AnyObject).title).contains("Download")) {
102 | download = true
103 | }
104 |
105 | // Get inspect element!
106 | if (String(describing: (i as AnyObject).title).contains("Element")) {
107 | for x in 0 ..< defaultMenuItems.count {
108 | if (String(describing: defaultMenuItems[x]).contains("Element")) {
109 | IElement = defaultMenuItems[x] as! NSMenuItem
110 | }
111 | }
112 | }
113 | }
114 |
115 | var NewMenu: [AnyObject] = [AnyObject]()
116 | let contextMenu = WebShellSettings["Contextmenu"] as! [String: Bool]
117 |
118 | // if can back
119 | if (contextMenu["BackAndForward"]!) {
120 | if (mainWebview.canGoBack) {
121 | NewMenu.append(NSMenuItem.init(title: "Back", action: #selector(ViewController._goBack(_:)), keyEquivalent: ""))
122 | }
123 | if (mainWebview.canGoForward) {
124 | NewMenu.append(NSMenuItem.init(title: "Forward", action: #selector(ViewController._goForward(_:)), keyEquivalent: ""))
125 | }
126 | }
127 | if (contextMenu["Reload"]!) {
128 | NewMenu.append(NSMenuItem.init(title: "Reload", action: #selector(ViewController._reloadPage(_:)), keyEquivalent: ""))
129 | }
130 |
131 | if (download) {
132 | if (element["WebElementLinkURL"] != nil) {
133 | lastURL = element["WebElementLinkURL"]! as! URL
134 |
135 | if (contextMenu["Download"]! || contextMenu["newWindow"]!) {
136 | NewMenu.append(NSMenuItem.separator())
137 |
138 | if (contextMenu["newWindow"]!) {
139 | NewMenu.append(NSMenuItem.init(title: "Open Link in a new Window", action: #selector(ViewController.createNewInstance(_:)), keyEquivalent: ""))
140 | }
141 | if (contextMenu["Download"]!) {
142 | NewMenu.append(NSMenuItem.init(title: "Download Linked File", action: #selector(ViewController.downloadFileWithURL(_:)), keyEquivalent: ""))
143 | }
144 | }
145 | }
146 | }
147 |
148 | NewMenu.append(NSMenuItem.separator())
149 | // Add debug menu. (if enabled)
150 |
151 | if (WebShellSettings["debugmode"] as! Bool) {
152 | let debugMenu = NSMenu(title: "Debug")
153 | if (IElement.title != "NSMenuItem") {
154 | debugMenu.addItem(IElement) // <-- Inspect element...
155 | }
156 | debugMenu.addItem(NSMenuItem.init(title: "Open New window", action: #selector(ViewController._debugNewWindow(_:)), keyEquivalent: ""))
157 | debugMenu.addItem(NSMenuItem.init(title: "Print arguments", action: #selector(ViewController._debugDumpArguments(_:)), keyEquivalent: ""))
158 | debugMenu.addItem(NSMenuItem.init(title: "Open URL", action: #selector(ViewController._openURL(_:)), keyEquivalent: ""))
159 | debugMenu.addItem(NSMenuItem.init(title: "Report an issue on this page", action: #selector(ViewController._reportThisPage(_:)), keyEquivalent: ""))
160 | debugMenu.addItem(NSMenuItem.init(title: "Print this page", action: #selector(ViewController._printThisPage(_:)), keyEquivalent: "")) // Stupid swift 2.2 does not look in extensions.
161 | debugMenu.addItem(NSMenuItem.separator())
162 | debugMenu.addItem(NSMenuItem.init(title: "Fire some random Notifications", action: #selector(ViewController.__sendNotifications(_:)), keyEquivalent: ""))
163 | debugMenu.addItem(NSMenuItem.init(title: "Reset localstorage", action: #selector(ViewController.resetLocalStorage(_:)), keyEquivalent: ""))
164 |
165 | let WSdeveloperMenu = NSMenu(title: "WS Developer")
166 | WSdeveloperMenu.addItem(NSMenuItem.init(title: "Inject Javascript", action: #selector(ViewController._injectJS(_:)), keyEquivalent: ""))
167 | WSdeveloperMenu.addItem(NSMenuItem.init(title: "What the web can do", action: #selector(ViewController._WWCDT(_:)), keyEquivalent: ""))
168 |
169 | let WSDevMenu = NSMenuItem.init(title: "WebShell Developer", action: #selector(ViewController._doNothing(_:)), keyEquivalent: "")
170 | WSDevMenu.submenu = WSdeveloperMenu
171 | debugMenu.addItem(WSDevMenu)
172 |
173 | let item = NSMenuItem.init(title: "Debug", action: #selector(ViewController._doNothing(_:)), keyEquivalent: "")
174 | item.submenu = debugMenu
175 |
176 | NewMenu.append(item)
177 | NewMenu.append(NSMenuItem.separator())
178 | }
179 |
180 | NewMenu.append(NSMenuItem.init(title: "Quit", action: #selector(ViewController._quit(_:)), keyEquivalent: ""))
181 |
182 | return NewMenu
183 | }
184 |
185 | /**
186 | Debug: Quit WebShell
187 |
188 | - Parameter Sender: Anyobject
189 | */
190 |
191 | @objc func _quit(_ Sender: AnyObject) -> Void {
192 | exit(0)
193 | }
194 |
195 | /**
196 | Debug: doNothing
197 |
198 | - Parameter Sender: Anyobject
199 | */
200 | @objc func _doNothing(_ Sender: AnyObject) -> Void {
201 | // _doNothing
202 | }
203 |
204 | /**
205 | Debug: Open new window
206 |
207 | - Parameter Sender: Anyobject
208 | */
209 | @objc func _debugNewWindow(_ Sender: AnyObject) -> Void {
210 | openNewWindow(url: "https://www.google.nl/search?client=WebShell&rls=en&q=new+window", height: "0", width: "0")
211 | }
212 |
213 | /**
214 | Debug: Print arguments
215 |
216 | - Parameter Sender: Anyobject
217 | */
218 | @objc func _debugDumpArguments(_ Sender: AnyObject) -> Void {
219 | print(CommandLine.arguments)
220 | }
221 |
222 | /**
223 | Debug: Fire 10 notifications (Timer)
224 |
225 | - Parameter Sender: Anyobject
226 | */
227 | @objc func __sendNotifications(_ Sender: AnyObject) -> Void {
228 | // Minimize app
229 | NSApplication.shared.keyWindow?.miniaturize(self)
230 |
231 | // Fire 10 Notifications
232 | Timer.scheduledTimer(timeInterval: TimeInterval(05), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
233 | Timer.scheduledTimer(timeInterval: TimeInterval(15), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
234 | Timer.scheduledTimer(timeInterval: TimeInterval(25), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
235 | Timer.scheduledTimer(timeInterval: TimeInterval(35), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
236 | Timer.scheduledTimer(timeInterval: TimeInterval(45), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
237 | Timer.scheduledTimer(timeInterval: TimeInterval(55), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
238 | Timer.scheduledTimer(timeInterval: TimeInterval(65), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
239 | Timer.scheduledTimer(timeInterval: TimeInterval(75), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
240 | Timer.scheduledTimer(timeInterval: TimeInterval(85), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
241 | Timer.scheduledTimer(timeInterval: TimeInterval(95), target: self, selector: #selector(ViewController.___sendNotifications), userInfo: nil, repeats: false)
242 | }
243 |
244 | /**
245 | Debug: Send 10 Notifications (real sending)
246 |
247 | - Parameter Sender: Anyobject
248 | */
249 | @objc func ___sendNotifications() -> Void {
250 | // Minimize app
251 | if (NSApplication.shared.keyWindow?.isMiniaturized == false) {
252 | NSApplication.shared.keyWindow?.miniaturize(self)
253 | }
254 |
255 | // Send Actual notification.
256 | makeNotification("Test Notification", message: "Hi!", icon: "https://camo.githubusercontent.com/ee999b2d8fa5413229fdc69e0b53144f02b7b840/687474703a2f2f376d6e6f79372e636f6d312e7a302e676c622e636c6f7564646e2e636f6d2f7765627368656c6c2f6c6f676f2e706e673f696d616765566965772f322f772f313238")
257 | }
258 |
259 | /**
260 | Debug: Open URL
261 |
262 | - Parameter Sender: Anyobject
263 | */
264 | @objc func _openURL(_ Sender: AnyObject) -> Void {
265 | let msg = NSAlert()
266 | msg.addButton(withTitle: "OK") // 1st button
267 | msg.addButton(withTitle: "Cancel") // 2nd button
268 | msg.messageText = "URL"
269 | msg.informativeText = "Where you need to go?"
270 |
271 | let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
272 | txt.stringValue = "http://"
273 |
274 | msg.accessoryView = txt
275 | let response: NSApplication.ModalResponse = msg.runModal()
276 |
277 | if (response == NSApplication.ModalResponse.alertFirstButtonReturn) {
278 | self.loadUrl(txt.stringValue)
279 | }
280 | }
281 |
282 | /**
283 | Debug: WhatTheWebCanDo.Today
284 |
285 | - Parameter Sender: Anyobject
286 | */
287 | @objc func _WWCDT(_ Sender: AnyObject) -> Void {
288 | self.loadUrl("https://whatwebcando.today")
289 | }
290 |
291 | /**
292 | Debug: Inject custom javascript
293 |
294 | - Parameter Sender: Anyobject
295 | */
296 | @objc func _injectJS(_ Sender: AnyObject) -> Void {
297 | let msg = NSAlert()
298 | msg.addButton(withTitle: "OK") // 1st button
299 | msg.addButton(withTitle: "Cancel") // 2nd button
300 | msg.messageText = "Inject Javascript"
301 | msg.informativeText = "Inject Javascript\nBe Carefull!"
302 |
303 | let txt = NSTextField(frame: NSRect(x: 0, y: 0, width: 400, height: 400))
304 | txt.stringValue = ""
305 | txt.translatesAutoresizingMaskIntoConstraints = true
306 |
307 | msg.accessoryView = txt
308 | let response: NSApplication.ModalResponse = msg.runModal()
309 |
310 | if (response == NSApplication.ModalResponse.alertFirstButtonReturn) {
311 | let JSReturn: String = mainWebview.stringByEvaluatingJavaScript(from: txt.stringValue)
312 |
313 | let RetVal = NSAlert()
314 | RetVal.addButton(withTitle: "OK")
315 | RetVal.messageText = "Injected Javascript"
316 | RetVal.informativeText = JSReturn != "" ? JSReturn : "Finished"
317 | RetVal.runModal()
318 | }
319 | }
320 |
321 | /**
322 | Debug: Report this page, as containing an error.
323 |
324 | - Parameter Sender: Anyobject
325 | */
326 | @objc func _reportThisPage(_ Sender: AnyObject) -> Void {
327 | let currentUrl: String = (mainWebview.mainFrame.dataSource?.request.url?.absoluteString)!
328 | let host: String = (mainWebview.mainFrame.dataSource?.request.url?.host)!
329 |
330 | let issue: String = String("Problem loading \(host)").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!.replacingOccurrences(of: "&", with: "%26")
331 | var body: String = (String("There is a problem loading \(currentUrl)").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)?.replacingOccurrences(of: "&", with: "%26"))!
332 | body.append("%0D%0AThe%20problem%20is%3A%0D%0A...")
333 |
334 | let url: String = "https://github.com/djyde/WebShell/issues/new?title=\(issue)&body=\(body)"
335 |
336 | NSWorkspace.shared.open(URL(string: (url as String))!)
337 | }
338 |
339 | /**
340 | Print this page
341 |
342 | - Parameter Sender: Anyobject
343 |
344 | - Notes Stupid swift 2.2 & 3 does not look in extensions.
345 | - Notes so we'll copy again...
346 | - Notes @wdg Add Print Support
347 | - Notes Issue: #39
348 | */
349 | @objc func _printThisPage(_ Sender: AnyObject? = nil) -> Void {
350 | let url = mainWebview.mainFrame.dataSource?.request?.url?.absoluteString
351 |
352 | let operation: NSPrintOperation = NSPrintOperation.init(view: mainWebview)
353 | operation.jobTitle = "Printing \(url!)"
354 |
355 | // If want to print landscape
356 | operation.printInfo.orientation = NSPrintInfo.PaperOrientation.landscape
357 | operation.printInfo.scalingFactor = 0.7
358 |
359 | if operation.run() {
360 | print("Printed?")
361 | }
362 | }
363 |
364 | /**
365 | Go Back
366 |
367 | - Parameter Sender: Anyobject
368 | */
369 | @objc func _goBack(_ Sender: AnyObject) -> Void {
370 | if (mainWebview.canGoBack) {
371 | mainWebview.goBack(Sender)
372 | }
373 | }
374 |
375 | /**
376 | Go Forward
377 |
378 | - Parameter Sender: Anyobject
379 | */
380 | @objc func _goForward(_ Sender: AnyObject) -> Void {
381 | if (mainWebview.canGoForward) {
382 | mainWebview.goForward(Sender)
383 | }
384 | }
385 |
386 | /**
387 | Reload page
388 |
389 | - Parameter Sender: Anyobject
390 | */
391 | @objc func _reloadPage(_ Sender: AnyObject) -> Void {
392 | mainWebview.reload(Sender)
393 | }
394 |
395 | /**
396 | Debug: Open in a new window
397 |
398 | - Parameter Sender: Anyobject
399 | */
400 | @objc func createNewInstance(_ Sender: AnyObject) -> Void {
401 | openNewWindow(url: "\(lastURL)", height: "0", width: "0")
402 | }
403 |
404 | /**
405 | Download file
406 |
407 | - Parameter Sender: Anyobject
408 | */
409 | @objc func downloadFileWithURL(_ Sender: AnyObject) -> Void {
410 | let wsDM = WebShelllDownloadManager.init(url: lastURL)
411 | wsDM.endDownloadTask()
412 | }
413 |
414 | /**
415 | If in debugmode -> Print
416 |
417 | - Parameter S: Any
418 | */
419 | func Dprint(_ S: Any) -> Void {
420 | if (WebShellSettings["debugmode"] as! Bool) {
421 | print(S)
422 | }
423 | }
424 |
425 | /**
426 | If in debugmode -> Dump
427 |
428 | - Parameter S: Any
429 | */
430 | func Ddump(_ S: Any) -> Void {
431 | if (WebShellSettings["debugmode"] as! Bool) {
432 | dump(S)
433 | }
434 | }
435 | }
436 |
--------------------------------------------------------------------------------
/WebShell.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 25B2225F1C25328E00F848B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 25B2225E1C25328E00F848B7 /* Assets.xcassets */; };
11 | 25B222621C25328E00F848B7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 25B222601C25328E00F848B7 /* Main.storyboard */; };
12 | 6E2DD7DE1CC56209000FA61A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 6E2DD7DD1CC56209000FA61A /* Credits.rtf */; };
13 | 6EE64A961CCBEAA000E9DA9B /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EE64A951CCBEAA000E9DA9B /* Settings.swift */; };
14 | DD0D711A1FDCF595002F50D1 /* WSStringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71081FDCF595002F50D1 /* WSStringExtension.swift */; };
15 | DD0D711B1FDCF595002F50D1 /* WSDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71091FDCF595002F50D1 /* WSDownloadManager.swift */; };
16 | DD0D711C1FDCF595002F50D1 /* WSInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710A1FDCF595002F50D1 /* WSInjector.swift */; };
17 | DD0D711D1FDCF595002F50D1 /* WSWebViewFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710B1FDCF595002F50D1 /* WSWebViewFunctions.swift */; };
18 | DD0D711E1FDCF595002F50D1 /* WSPasswordManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710C1FDCF595002F50D1 /* WSPasswordManager.swift */; };
19 | DD0D711F1FDCF595002F50D1 /* WSEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710D1FDCF595002F50D1 /* WSEventMonitor.swift */; };
20 | DD0D71211FDCF595002F50D1 /* WSDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710F1FDCF595002F50D1 /* WSDebug.swift */; };
21 | DD0D71221FDCF595002F50D1 /* WSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71101FDCF595002F50D1 /* WSViewController.swift */; };
22 | DD0D71231FDCF595002F50D1 /* WSCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71111FDCF595002F50D1 /* WSCore.swift */; };
23 | DD0D71241FDCF595002F50D1 /* WSCustomInject.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71121FDCF595002F50D1 /* WSCustomInject.swift */; };
24 | DD0D71251FDCF595002F50D1 /* WSBaseSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71131FDCF595002F50D1 /* WSBaseSettings.swift */; };
25 | DD0D71271FDCF595002F50D1 /* WSPageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71151FDCF595002F50D1 /* WSPageActions.swift */; };
26 | DD0D71281FDCF595002F50D1 /* WSTrackpadGestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71161FDCF595002F50D1 /* WSTrackpadGestures.swift */; };
27 | DD0D71291FDCF595002F50D1 /* WSApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71171FDCF595002F50D1 /* WSApplication.swift */; };
28 | DD0D712A1FDCF595002F50D1 /* WSFileHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71181FDCF595002F50D1 /* WSFileHandler.swift */; };
29 | DD0D712B1FDCF595002F50D1 /* WSAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71191FDCF595002F50D1 /* WSAppDelegate.swift */; };
30 | DD0D712C1FDCF5B6002F50D1 /* WSAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71191FDCF595002F50D1 /* WSAppDelegate.swift */; };
31 | DD0D712D1FDCF5B6002F50D1 /* WSApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71171FDCF595002F50D1 /* WSApplication.swift */; };
32 | DD0D712E1FDCF5B6002F50D1 /* WSBaseSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71131FDCF595002F50D1 /* WSBaseSettings.swift */; };
33 | DD0D712F1FDCF5B6002F50D1 /* WSCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71111FDCF595002F50D1 /* WSCore.swift */; };
34 | DD0D71301FDCF5B6002F50D1 /* WSCustomInject.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71121FDCF595002F50D1 /* WSCustomInject.swift */; };
35 | DD0D71311FDCF5B6002F50D1 /* WSDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710F1FDCF595002F50D1 /* WSDebug.swift */; };
36 | DD0D71321FDCF5B6002F50D1 /* WSDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71091FDCF595002F50D1 /* WSDownloadManager.swift */; };
37 | DD0D71331FDCF5B6002F50D1 /* WSEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710D1FDCF595002F50D1 /* WSEventMonitor.swift */; };
38 | DD0D71341FDCF5B6002F50D1 /* WSFileHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71181FDCF595002F50D1 /* WSFileHandler.swift */; };
39 | DD0D71351FDCF5B6002F50D1 /* WSInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710A1FDCF595002F50D1 /* WSInjector.swift */; };
40 | DD0D71361FDCF5B6002F50D1 /* WSPageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71151FDCF595002F50D1 /* WSPageActions.swift */; };
41 | DD0D71371FDCF5B6002F50D1 /* WSPasswordManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710C1FDCF595002F50D1 /* WSPasswordManager.swift */; };
42 | DD0D71391FDCF5B6002F50D1 /* WSStringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71081FDCF595002F50D1 /* WSStringExtension.swift */; };
43 | DD0D713A1FDCF5B6002F50D1 /* WSTrackpadGestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71161FDCF595002F50D1 /* WSTrackpadGestures.swift */; };
44 | DD0D713B1FDCF5B6002F50D1 /* WSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71101FDCF595002F50D1 /* WSViewController.swift */; };
45 | DD0D713D1FDCF5B6002F50D1 /* WSWebViewFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D710B1FDCF595002F50D1 /* WSWebViewFunctions.swift */; };
46 | DD0D71421FDCF5FD002F50D1 /* navigator_geolocation_getCurrentPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D713F1FDCF5FD002F50D1 /* navigator_geolocation_getCurrentPosition.swift */; };
47 | DD0D71431FDCF5FD002F50D1 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71401FDCF5FD002F50D1 /* Notifications.swift */; };
48 | DD0D71441FDCF5FD002F50D1 /* Battery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71411FDCF5FD002F50D1 /* Battery.swift */; };
49 | DD0D71451FDCF610002F50D1 /* navigator_geolocation_getCurrentPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D713F1FDCF5FD002F50D1 /* navigator_geolocation_getCurrentPosition.swift */; };
50 | DD0D71461FDCF610002F50D1 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71401FDCF5FD002F50D1 /* Notifications.swift */; };
51 | DD0D71471FDCF610002F50D1 /* Battery.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0D71411FDCF5FD002F50D1 /* Battery.swift */; };
52 | DD593BC61FDB7F4200C15D57 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 25B2225E1C25328E00F848B7 /* Assets.xcassets */; };
53 | DD593BC71FDB7F4200C15D57 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 6E2DD7DD1CC56209000FA61A /* Credits.rtf */; };
54 | DD593BC81FDB7F4200C15D57 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 25B222601C25328E00F848B7 /* Main.storyboard */; };
55 | DD593BD91FDB80DC00C15D57 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD593BD51FDB80CB00C15D57 /* Settings.swift */; };
56 | DEC374881FFEBE4000A8B04E /* WSWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC374871FFEBE4000A8B04E /* WSWindow.swift */; };
57 | DEC374891FFEBE4000A8B04E /* WSWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC374871FFEBE4000A8B04E /* WSWindow.swift */; };
58 | /* End PBXBuildFile section */
59 |
60 | /* Begin PBXFileReference section */
61 | 25B222571C25328E00F848B7 /* WebShell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebShell.app; sourceTree = BUILT_PRODUCTS_DIR; };
62 | 25B2225E1C25328E00F848B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
63 | 25B222611C25328E00F848B7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
64 | 6E2DD7DD1CC56209000FA61A /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; };
65 | 6E3733301C2890A600CE0058 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Main.strings; sourceTree = ""; };
66 | 6EE64A951CCBEAA000E9DA9B /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; };
67 | DD0D71081FDCF595002F50D1 /* WSStringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSStringExtension.swift; sourceTree = ""; };
68 | DD0D71091FDCF595002F50D1 /* WSDownloadManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSDownloadManager.swift; sourceTree = ""; };
69 | DD0D710A1FDCF595002F50D1 /* WSInjector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSInjector.swift; sourceTree = ""; };
70 | DD0D710B1FDCF595002F50D1 /* WSWebViewFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSWebViewFunctions.swift; sourceTree = ""; };
71 | DD0D710C1FDCF595002F50D1 /* WSPasswordManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSPasswordManager.swift; sourceTree = ""; };
72 | DD0D710D1FDCF595002F50D1 /* WSEventMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSEventMonitor.swift; sourceTree = ""; };
73 | DD0D710F1FDCF595002F50D1 /* WSDebug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSDebug.swift; sourceTree = ""; };
74 | DD0D71101FDCF595002F50D1 /* WSViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSViewController.swift; sourceTree = ""; };
75 | DD0D71111FDCF595002F50D1 /* WSCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSCore.swift; sourceTree = ""; };
76 | DD0D71121FDCF595002F50D1 /* WSCustomInject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSCustomInject.swift; sourceTree = ""; };
77 | DD0D71131FDCF595002F50D1 /* WSBaseSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSBaseSettings.swift; sourceTree = ""; };
78 | DD0D71151FDCF595002F50D1 /* WSPageActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSPageActions.swift; sourceTree = ""; };
79 | DD0D71161FDCF595002F50D1 /* WSTrackpadGestures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSTrackpadGestures.swift; sourceTree = ""; };
80 | DD0D71171FDCF595002F50D1 /* WSApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSApplication.swift; sourceTree = ""; };
81 | DD0D71181FDCF595002F50D1 /* WSFileHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSFileHandler.swift; sourceTree = ""; };
82 | DD0D71191FDCF595002F50D1 /* WSAppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSAppDelegate.swift; sourceTree = ""; };
83 | DD0D713F1FDCF5FD002F50D1 /* navigator_geolocation_getCurrentPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = navigator_geolocation_getCurrentPosition.swift; sourceTree = ""; };
84 | DD0D71401FDCF5FD002F50D1 /* Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; };
85 | DD0D71411FDCF5FD002F50D1 /* Battery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Battery.swift; sourceTree = ""; };
86 | DD0D716A1FDCF7C1002F50D1 /* Udemy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Udemy-Info.plist"; sourceTree = ""; };
87 | DD0D716B1FDCF7C1002F50D1 /* WebShell-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "WebShell-Info.plist"; sourceTree = ""; };
88 | DD593BCD1FDB7F4200C15D57 /* Udemy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Udemy.app; sourceTree = BUILT_PRODUCTS_DIR; };
89 | DD593BD51FDB80CB00C15D57 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; };
90 | DEC374871FFEBE4000A8B04E /* WSWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WSWindow.swift; sourceTree = ""; };
91 | /* End PBXFileReference section */
92 |
93 | /* Begin PBXFrameworksBuildPhase section */
94 | 25B222541C25328E00F848B7 /* Frameworks */ = {
95 | isa = PBXFrameworksBuildPhase;
96 | buildActionMask = 2147483647;
97 | files = (
98 | );
99 | runOnlyForDeploymentPostprocessing = 0;
100 | };
101 | DD593BC41FDB7F4200C15D57 /* Frameworks */ = {
102 | isa = PBXFrameworksBuildPhase;
103 | buildActionMask = 2147483647;
104 | files = (
105 | );
106 | runOnlyForDeploymentPostprocessing = 0;
107 | };
108 | /* End PBXFrameworksBuildPhase section */
109 |
110 | /* Begin PBXGroup section */
111 | 25B2224E1C25328E00F848B7 = {
112 | isa = PBXGroup;
113 | children = (
114 | 25B222591C25328E00F848B7 /* WebShell */,
115 | );
116 | sourceTree = "";
117 | };
118 | 25B222581C25328E00F848B7 /* Products */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 25B222571C25328E00F848B7 /* WebShell.app */,
122 | DD593BCD1FDB7F4200C15D57 /* Udemy.app */,
123 | );
124 | name = Products;
125 | path = ..;
126 | sourceTree = "";
127 | };
128 | 25B222591C25328E00F848B7 /* WebShell */ = {
129 | isa = PBXGroup;
130 | children = (
131 | DD0D71071FDCF595002F50D1 /* Core */,
132 | DD0D713E1FDCF5FD002F50D1 /* Support */,
133 | DD593BD01FDB7F9100C15D57 /* Sites */,
134 | DD0D716C1FDCF7E5002F50D1 /* Resources */,
135 | DD0D716A1FDCF7C1002F50D1 /* Udemy-Info.plist */,
136 | DD0D716B1FDCF7C1002F50D1 /* WebShell-Info.plist */,
137 | 6E2DD7DD1CC56209000FA61A /* Credits.rtf */,
138 | 25B222581C25328E00F848B7 /* Products */,
139 | );
140 | path = WebShell;
141 | sourceTree = "";
142 | };
143 | DD0D71071FDCF595002F50D1 /* Core */ = {
144 | isa = PBXGroup;
145 | children = (
146 | DD0D71191FDCF595002F50D1 /* WSAppDelegate.swift */,
147 | DD0D71171FDCF595002F50D1 /* WSApplication.swift */,
148 | DD0D71131FDCF595002F50D1 /* WSBaseSettings.swift */,
149 | DD0D71111FDCF595002F50D1 /* WSCore.swift */,
150 | DD0D71121FDCF595002F50D1 /* WSCustomInject.swift */,
151 | DD0D710F1FDCF595002F50D1 /* WSDebug.swift */,
152 | DD0D71091FDCF595002F50D1 /* WSDownloadManager.swift */,
153 | DD0D710D1FDCF595002F50D1 /* WSEventMonitor.swift */,
154 | DD0D71181FDCF595002F50D1 /* WSFileHandler.swift */,
155 | DD0D710A1FDCF595002F50D1 /* WSInjector.swift */,
156 | DD0D71151FDCF595002F50D1 /* WSPageActions.swift */,
157 | DD0D710C1FDCF595002F50D1 /* WSPasswordManager.swift */,
158 | DD0D71081FDCF595002F50D1 /* WSStringExtension.swift */,
159 | DD0D71161FDCF595002F50D1 /* WSTrackpadGestures.swift */,
160 | DD0D71101FDCF595002F50D1 /* WSViewController.swift */,
161 | DD0D710B1FDCF595002F50D1 /* WSWebViewFunctions.swift */,
162 | DEC374871FFEBE4000A8B04E /* WSWindow.swift */,
163 | );
164 | path = Core;
165 | sourceTree = "";
166 | };
167 | DD0D713E1FDCF5FD002F50D1 /* Support */ = {
168 | isa = PBXGroup;
169 | children = (
170 | DD0D713F1FDCF5FD002F50D1 /* navigator_geolocation_getCurrentPosition.swift */,
171 | DD0D71401FDCF5FD002F50D1 /* Notifications.swift */,
172 | DD0D71411FDCF5FD002F50D1 /* Battery.swift */,
173 | );
174 | path = Support;
175 | sourceTree = "";
176 | };
177 | DD0D716C1FDCF7E5002F50D1 /* Resources */ = {
178 | isa = PBXGroup;
179 | children = (
180 | 25B2225E1C25328E00F848B7 /* Assets.xcassets */,
181 | 25B222601C25328E00F848B7 /* Main.storyboard */,
182 | );
183 | name = Resources;
184 | sourceTree = "";
185 | };
186 | DD0D716D1FDCF80A002F50D1 /* WebShell */ = {
187 | isa = PBXGroup;
188 | children = (
189 | 6EE64A951CCBEAA000E9DA9B /* Settings.swift */,
190 | );
191 | path = WebShell;
192 | sourceTree = "";
193 | };
194 | DD593BD01FDB7F9100C15D57 /* Sites */ = {
195 | isa = PBXGroup;
196 | children = (
197 | DD0D716D1FDCF80A002F50D1 /* WebShell */,
198 | DD593BD41FDB80CB00C15D57 /* Udemy */,
199 | );
200 | path = Sites;
201 | sourceTree = "";
202 | };
203 | DD593BD41FDB80CB00C15D57 /* Udemy */ = {
204 | isa = PBXGroup;
205 | children = (
206 | DD593BD51FDB80CB00C15D57 /* Settings.swift */,
207 | );
208 | path = Udemy;
209 | sourceTree = "";
210 | };
211 | /* End PBXGroup section */
212 |
213 | /* Begin PBXNativeTarget section */
214 | 25B222561C25328E00F848B7 /* WebShell */ = {
215 | isa = PBXNativeTarget;
216 | buildConfigurationList = 25B222661C25328E00F848B7 /* Build configuration list for PBXNativeTarget "WebShell" */;
217 | buildPhases = (
218 | 25B222531C25328E00F848B7 /* Sources */,
219 | 25B222541C25328E00F848B7 /* Frameworks */,
220 | 25B222551C25328E00F848B7 /* Resources */,
221 | );
222 | buildRules = (
223 | );
224 | dependencies = (
225 | );
226 | name = WebShell;
227 | productName = WebShell;
228 | productReference = 25B222571C25328E00F848B7 /* WebShell.app */;
229 | productType = "com.apple.product-type.application";
230 | };
231 | DD593BAD1FDB7F4200C15D57 /* Udemy */ = {
232 | isa = PBXNativeTarget;
233 | buildConfigurationList = DD593BCA1FDB7F4200C15D57 /* Build configuration list for PBXNativeTarget "Udemy" */;
234 | buildPhases = (
235 | DD593BAE1FDB7F4200C15D57 /* Sources */,
236 | DD593BC41FDB7F4200C15D57 /* Frameworks */,
237 | DD593BC51FDB7F4200C15D57 /* Resources */,
238 | );
239 | buildRules = (
240 | );
241 | dependencies = (
242 | );
243 | name = Udemy;
244 | productName = WebShell;
245 | productReference = DD593BCD1FDB7F4200C15D57 /* Udemy.app */;
246 | productType = "com.apple.product-type.application";
247 | };
248 | /* End PBXNativeTarget section */
249 |
250 | /* Begin PBXProject section */
251 | 25B2224F1C25328E00F848B7 /* Project object */ = {
252 | isa = PBXProject;
253 | attributes = {
254 | LastUpgradeCheck = 0900;
255 | ORGANIZATIONNAME = RandyLu;
256 | TargetAttributes = {
257 | 25B222561C25328E00F848B7 = {
258 | CreatedOnToolsVersion = 7.0.1;
259 | DevelopmentTeam = 4V2D72S45C;
260 | LastSwiftMigration = 0900;
261 | ProvisioningStyle = Automatic;
262 | };
263 | DD593BAD1FDB7F4200C15D57 = {
264 | DevelopmentTeam = 4V2D72S45C;
265 | ProvisioningStyle = Automatic;
266 | };
267 | };
268 | };
269 | buildConfigurationList = 25B222521C25328E00F848B7 /* Build configuration list for PBXProject "WebShell" */;
270 | compatibilityVersion = "Xcode 3.2";
271 | developmentRegion = English;
272 | hasScannedForEncodings = 0;
273 | knownRegions = (
274 | en,
275 | Base,
276 | );
277 | mainGroup = 25B2224E1C25328E00F848B7;
278 | productRefGroup = 25B222581C25328E00F848B7 /* Products */;
279 | projectDirPath = "";
280 | projectRoot = "";
281 | targets = (
282 | 25B222561C25328E00F848B7 /* WebShell */,
283 | DD593BAD1FDB7F4200C15D57 /* Udemy */,
284 | );
285 | };
286 | /* End PBXProject section */
287 |
288 | /* Begin PBXResourcesBuildPhase section */
289 | 25B222551C25328E00F848B7 /* Resources */ = {
290 | isa = PBXResourcesBuildPhase;
291 | buildActionMask = 2147483647;
292 | files = (
293 | 25B2225F1C25328E00F848B7 /* Assets.xcassets in Resources */,
294 | 6E2DD7DE1CC56209000FA61A /* Credits.rtf in Resources */,
295 | 25B222621C25328E00F848B7 /* Main.storyboard in Resources */,
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | DD593BC51FDB7F4200C15D57 /* Resources */ = {
300 | isa = PBXResourcesBuildPhase;
301 | buildActionMask = 2147483647;
302 | files = (
303 | DD593BC61FDB7F4200C15D57 /* Assets.xcassets in Resources */,
304 | DD593BC71FDB7F4200C15D57 /* Credits.rtf in Resources */,
305 | DD593BC81FDB7F4200C15D57 /* Main.storyboard in Resources */,
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | };
309 | /* End PBXResourcesBuildPhase section */
310 |
311 | /* Begin PBXSourcesBuildPhase section */
312 | 25B222531C25328E00F848B7 /* Sources */ = {
313 | isa = PBXSourcesBuildPhase;
314 | buildActionMask = 2147483647;
315 | files = (
316 | DD0D71291FDCF595002F50D1 /* WSApplication.swift in Sources */,
317 | DD0D71421FDCF5FD002F50D1 /* navigator_geolocation_getCurrentPosition.swift in Sources */,
318 | DD0D711E1FDCF595002F50D1 /* WSPasswordManager.swift in Sources */,
319 | DD0D711A1FDCF595002F50D1 /* WSStringExtension.swift in Sources */,
320 | DD0D711F1FDCF595002F50D1 /* WSEventMonitor.swift in Sources */,
321 | DD0D71251FDCF595002F50D1 /* WSBaseSettings.swift in Sources */,
322 | DD0D712B1FDCF595002F50D1 /* WSAppDelegate.swift in Sources */,
323 | DD0D71211FDCF595002F50D1 /* WSDebug.swift in Sources */,
324 | DD0D712A1FDCF595002F50D1 /* WSFileHandler.swift in Sources */,
325 | 6EE64A961CCBEAA000E9DA9B /* Settings.swift in Sources */,
326 | DD0D71221FDCF595002F50D1 /* WSViewController.swift in Sources */,
327 | DD0D71231FDCF595002F50D1 /* WSCore.swift in Sources */,
328 | DEC374881FFEBE4000A8B04E /* WSWindow.swift in Sources */,
329 | DD0D711C1FDCF595002F50D1 /* WSInjector.swift in Sources */,
330 | DD0D71431FDCF5FD002F50D1 /* Notifications.swift in Sources */,
331 | DD0D71271FDCF595002F50D1 /* WSPageActions.swift in Sources */,
332 | DD0D71441FDCF5FD002F50D1 /* Battery.swift in Sources */,
333 | DD0D711B1FDCF595002F50D1 /* WSDownloadManager.swift in Sources */,
334 | DD0D711D1FDCF595002F50D1 /* WSWebViewFunctions.swift in Sources */,
335 | DD0D71241FDCF595002F50D1 /* WSCustomInject.swift in Sources */,
336 | DD0D71281FDCF595002F50D1 /* WSTrackpadGestures.swift in Sources */,
337 | );
338 | runOnlyForDeploymentPostprocessing = 0;
339 | };
340 | DD593BAE1FDB7F4200C15D57 /* Sources */ = {
341 | isa = PBXSourcesBuildPhase;
342 | buildActionMask = 2147483647;
343 | files = (
344 | DD0D712C1FDCF5B6002F50D1 /* WSAppDelegate.swift in Sources */,
345 | DD0D713D1FDCF5B6002F50D1 /* WSWebViewFunctions.swift in Sources */,
346 | DD0D71451FDCF610002F50D1 /* navigator_geolocation_getCurrentPosition.swift in Sources */,
347 | DD0D71361FDCF5B6002F50D1 /* WSPageActions.swift in Sources */,
348 | DD0D71341FDCF5B6002F50D1 /* WSFileHandler.swift in Sources */,
349 | DD0D71391FDCF5B6002F50D1 /* WSStringExtension.swift in Sources */,
350 | DD0D71301FDCF5B6002F50D1 /* WSCustomInject.swift in Sources */,
351 | DD0D712F1FDCF5B6002F50D1 /* WSCore.swift in Sources */,
352 | DD0D71351FDCF5B6002F50D1 /* WSInjector.swift in Sources */,
353 | DD0D712D1FDCF5B6002F50D1 /* WSApplication.swift in Sources */,
354 | DD0D71311FDCF5B6002F50D1 /* WSDebug.swift in Sources */,
355 | DD0D712E1FDCF5B6002F50D1 /* WSBaseSettings.swift in Sources */,
356 | DEC374891FFEBE4000A8B04E /* WSWindow.swift in Sources */,
357 | DD0D71371FDCF5B6002F50D1 /* WSPasswordManager.swift in Sources */,
358 | DD0D71461FDCF610002F50D1 /* Notifications.swift in Sources */,
359 | DD0D71331FDCF5B6002F50D1 /* WSEventMonitor.swift in Sources */,
360 | DD0D71471FDCF610002F50D1 /* Battery.swift in Sources */,
361 | DD0D713B1FDCF5B6002F50D1 /* WSViewController.swift in Sources */,
362 | DD593BD91FDB80DC00C15D57 /* Settings.swift in Sources */,
363 | DD0D71321FDCF5B6002F50D1 /* WSDownloadManager.swift in Sources */,
364 | DD0D713A1FDCF5B6002F50D1 /* WSTrackpadGestures.swift in Sources */,
365 | );
366 | runOnlyForDeploymentPostprocessing = 0;
367 | };
368 | /* End PBXSourcesBuildPhase section */
369 |
370 | /* Begin PBXVariantGroup section */
371 | 25B222601C25328E00F848B7 /* Main.storyboard */ = {
372 | isa = PBXVariantGroup;
373 | children = (
374 | 25B222611C25328E00F848B7 /* Base */,
375 | 6E3733301C2890A600CE0058 /* nl */,
376 | );
377 | name = Main.storyboard;
378 | sourceTree = "";
379 | };
380 | /* End PBXVariantGroup section */
381 |
382 | /* Begin XCBuildConfiguration section */
383 | 25B222641C25328E00F848B7 /* Debug */ = {
384 | isa = XCBuildConfiguration;
385 | buildSettings = {
386 | ALWAYS_SEARCH_USER_PATHS = NO;
387 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
389 | CLANG_CXX_LIBRARY = "libc++";
390 | CLANG_ENABLE_MODULES = YES;
391 | CLANG_ENABLE_OBJC_ARC = YES;
392 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
393 | CLANG_WARN_BOOL_CONVERSION = YES;
394 | CLANG_WARN_COMMA = YES;
395 | CLANG_WARN_CONSTANT_CONVERSION = YES;
396 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
397 | CLANG_WARN_EMPTY_BODY = YES;
398 | CLANG_WARN_ENUM_CONVERSION = YES;
399 | CLANG_WARN_INFINITE_RECURSION = YES;
400 | CLANG_WARN_INT_CONVERSION = YES;
401 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
402 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
403 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
404 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
405 | CLANG_WARN_STRICT_PROTOTYPES = YES;
406 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
407 | CLANG_WARN_UNREACHABLE_CODE = YES;
408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
409 | CODE_SIGN_IDENTITY = "-";
410 | COPY_PHASE_STRIP = NO;
411 | DEBUG_INFORMATION_FORMAT = dwarf;
412 | ENABLE_STRICT_OBJC_MSGSEND = YES;
413 | ENABLE_TESTABILITY = YES;
414 | GCC_C_LANGUAGE_STANDARD = gnu99;
415 | GCC_DYNAMIC_NO_PIC = NO;
416 | GCC_NO_COMMON_BLOCKS = YES;
417 | GCC_OPTIMIZATION_LEVEL = 0;
418 | GCC_PREPROCESSOR_DEFINITIONS = (
419 | "DEBUG=1",
420 | "$(inherited)",
421 | );
422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
424 | GCC_WARN_UNDECLARED_SELECTOR = YES;
425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
426 | GCC_WARN_UNUSED_FUNCTION = YES;
427 | GCC_WARN_UNUSED_VARIABLE = YES;
428 | MACOSX_DEPLOYMENT_TARGET = 10.11;
429 | MTL_ENABLE_DEBUG_INFO = YES;
430 | ONLY_ACTIVE_ARCH = YES;
431 | SDKROOT = macosx;
432 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
433 | };
434 | name = Debug;
435 | };
436 | 25B222651C25328E00F848B7 /* Release */ = {
437 | isa = XCBuildConfiguration;
438 | buildSettings = {
439 | ALWAYS_SEARCH_USER_PATHS = NO;
440 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
442 | CLANG_CXX_LIBRARY = "libc++";
443 | CLANG_ENABLE_MODULES = YES;
444 | CLANG_ENABLE_OBJC_ARC = YES;
445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
446 | CLANG_WARN_BOOL_CONVERSION = YES;
447 | CLANG_WARN_COMMA = YES;
448 | CLANG_WARN_CONSTANT_CONVERSION = YES;
449 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
450 | CLANG_WARN_EMPTY_BODY = YES;
451 | CLANG_WARN_ENUM_CONVERSION = YES;
452 | CLANG_WARN_INFINITE_RECURSION = YES;
453 | CLANG_WARN_INT_CONVERSION = YES;
454 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
455 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
456 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
457 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
458 | CLANG_WARN_STRICT_PROTOTYPES = YES;
459 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
460 | CLANG_WARN_UNREACHABLE_CODE = YES;
461 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
462 | CODE_SIGN_IDENTITY = "-";
463 | COPY_PHASE_STRIP = NO;
464 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
465 | ENABLE_NS_ASSERTIONS = NO;
466 | ENABLE_STRICT_OBJC_MSGSEND = YES;
467 | GCC_C_LANGUAGE_STANDARD = gnu99;
468 | GCC_NO_COMMON_BLOCKS = YES;
469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
471 | GCC_WARN_UNDECLARED_SELECTOR = YES;
472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
473 | GCC_WARN_UNUSED_FUNCTION = YES;
474 | GCC_WARN_UNUSED_VARIABLE = YES;
475 | MACOSX_DEPLOYMENT_TARGET = 10.11;
476 | MTL_ENABLE_DEBUG_INFO = NO;
477 | SDKROOT = macosx;
478 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
479 | };
480 | name = Release;
481 | };
482 | 25B222671C25328E00F848B7 /* Debug */ = {
483 | isa = XCBuildConfiguration;
484 | buildSettings = {
485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
486 | CODE_SIGN_IDENTITY = "Mac Developer";
487 | CODE_SIGN_STYLE = Automatic;
488 | COMBINE_HIDPI_IMAGES = YES;
489 | DEVELOPMENT_TEAM = 4V2D72S45C;
490 | INFOPLIST_FILE = "WebShell/WebShell-Info.plist";
491 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
492 | PRODUCT_BUNDLE_IDENTIFIER = io.github.djyde.WebShell;
493 | PRODUCT_NAME = "$(TARGET_NAME)";
494 | PROVISIONING_PROFILE_SPECIFIER = "";
495 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
496 | SWIFT_VERSION = 4.0;
497 | };
498 | name = Debug;
499 | };
500 | 25B222681C25328E00F848B7 /* Release */ = {
501 | isa = XCBuildConfiguration;
502 | buildSettings = {
503 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
504 | CODE_SIGN_IDENTITY = "Mac Developer";
505 | CODE_SIGN_STYLE = Automatic;
506 | COMBINE_HIDPI_IMAGES = YES;
507 | DEVELOPMENT_TEAM = 4V2D72S45C;
508 | INFOPLIST_FILE = "WebShell/WebShell-Info.plist";
509 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
510 | PRODUCT_BUNDLE_IDENTIFIER = io.github.djyde.WebShell;
511 | PRODUCT_NAME = "$(TARGET_NAME)";
512 | PROVISIONING_PROFILE_SPECIFIER = "";
513 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
514 | SWIFT_VERSION = 4.0;
515 | };
516 | name = Release;
517 | };
518 | DD593BCB1FDB7F4200C15D57 /* Debug */ = {
519 | isa = XCBuildConfiguration;
520 | buildSettings = {
521 | ASSETCATALOG_COMPILER_APPICON_NAME = Udemy;
522 | CODE_SIGN_IDENTITY = "Mac Developer";
523 | CODE_SIGN_STYLE = Automatic;
524 | COMBINE_HIDPI_IMAGES = YES;
525 | DEVELOPMENT_TEAM = 4V2D72S45C;
526 | INFOPLIST_FILE = "WebShell/Udemy-Info.plist";
527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
528 | PRODUCT_BUNDLE_IDENTIFIER = io.github.djyde.WebShell;
529 | PRODUCT_NAME = "$(TARGET_NAME)";
530 | PROVISIONING_PROFILE_SPECIFIER = "";
531 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
532 | SWIFT_VERSION = 4.0;
533 | };
534 | name = Debug;
535 | };
536 | DD593BCC1FDB7F4200C15D57 /* Release */ = {
537 | isa = XCBuildConfiguration;
538 | buildSettings = {
539 | ASSETCATALOG_COMPILER_APPICON_NAME = Udemy;
540 | CODE_SIGN_IDENTITY = "Mac Developer";
541 | CODE_SIGN_STYLE = Automatic;
542 | COMBINE_HIDPI_IMAGES = YES;
543 | DEVELOPMENT_TEAM = 4V2D72S45C;
544 | INFOPLIST_FILE = "WebShell/Udemy-Info.plist";
545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
546 | PRODUCT_BUNDLE_IDENTIFIER = io.github.djyde.WebShell;
547 | PRODUCT_NAME = "$(TARGET_NAME)";
548 | PROVISIONING_PROFILE_SPECIFIER = "";
549 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
550 | SWIFT_VERSION = 4.0;
551 | };
552 | name = Release;
553 | };
554 | /* End XCBuildConfiguration section */
555 |
556 | /* Begin XCConfigurationList section */
557 | 25B222521C25328E00F848B7 /* Build configuration list for PBXProject "WebShell" */ = {
558 | isa = XCConfigurationList;
559 | buildConfigurations = (
560 | 25B222641C25328E00F848B7 /* Debug */,
561 | 25B222651C25328E00F848B7 /* Release */,
562 | );
563 | defaultConfigurationIsVisible = 0;
564 | defaultConfigurationName = Release;
565 | };
566 | 25B222661C25328E00F848B7 /* Build configuration list for PBXNativeTarget "WebShell" */ = {
567 | isa = XCConfigurationList;
568 | buildConfigurations = (
569 | 25B222671C25328E00F848B7 /* Debug */,
570 | 25B222681C25328E00F848B7 /* Release */,
571 | );
572 | defaultConfigurationIsVisible = 0;
573 | defaultConfigurationName = Release;
574 | };
575 | DD593BCA1FDB7F4200C15D57 /* Build configuration list for PBXNativeTarget "Udemy" */ = {
576 | isa = XCConfigurationList;
577 | buildConfigurations = (
578 | DD593BCB1FDB7F4200C15D57 /* Debug */,
579 | DD593BCC1FDB7F4200C15D57 /* Release */,
580 | );
581 | defaultConfigurationIsVisible = 0;
582 | defaultConfigurationName = Release;
583 | };
584 | /* End XCConfigurationList section */
585 | };
586 | rootObject = 25B2224F1C25328E00F848B7 /* Project object */;
587 | }
588 |
--------------------------------------------------------------------------------