├── .gitignore
├── .hound.yml
├── .swiftlint.yml
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── Package.pins
├── Package.swift
├── README.md
├── Sources
└── UIWebKit
│ ├── Components
│ ├── UIAnchor.swift
│ ├── UIForm.swift
│ ├── UILink.swift
│ ├── UILists.swift
│ ├── UINavigation.swift
│ ├── UIParagraph.swift
│ └── UITable.swift
│ ├── Dependencies.swift
│ ├── Element.swift
│ ├── ElementRenderable.swift
│ ├── Extensions
│ ├── Character.swift
│ └── String.swift
│ ├── UIElement.swift
│ └── UIWebPage.swift
├── Tests
├── LinuxMain.swift
└── UIWebKitTests
│ ├── UIElementTests.swift
│ └── UIWebPageTests.swift
├── docs
├── Classes.html
├── Classes
│ ├── CSSLink.html
│ ├── UIAnchor.html
│ ├── UIElement.html
│ ├── UIForm.html
│ ├── UIFormElement.html
│ ├── UILink.html
│ ├── UIListItem.html
│ ├── UINavigation.html
│ ├── UIOrderedList.html
│ ├── UIParagraph.html
│ ├── UITable.html
│ ├── UITableCell.html
│ ├── UITableCellHeader.html
│ ├── UITableRow.html
│ ├── UITableRowHeader.html
│ ├── UIUnorderedList.html
│ └── UIWebPage.html
├── Enums.html
├── Enums
│ ├── Dependency.html
│ ├── DependencyType.html
│ ├── Element.html
│ └── LoginFormType.html
├── Protocols.html
├── Protocols
│ └── ElementRenderable.html
├── badge.svg
├── css
│ ├── highlight.css
│ └── jazzy.css
├── docsets
│ ├── .docset
│ │ └── Contents
│ │ │ ├── Info.plist
│ │ │ └── Resources
│ │ │ ├── Documents
│ │ │ ├── Classes.html
│ │ │ ├── Classes
│ │ │ │ ├── CSSLink.html
│ │ │ │ ├── UIAnchor.html
│ │ │ │ ├── UIElement.html
│ │ │ │ ├── UIForm.html
│ │ │ │ ├── UIFormElement.html
│ │ │ │ ├── UILink.html
│ │ │ │ ├── UIListItem.html
│ │ │ │ ├── UINavigation.html
│ │ │ │ ├── UIOrderedList.html
│ │ │ │ ├── UIParagraph.html
│ │ │ │ ├── UITable.html
│ │ │ │ ├── UITableCell.html
│ │ │ │ ├── UITableCellHeader.html
│ │ │ │ ├── UITableRow.html
│ │ │ │ ├── UITableRowHeader.html
│ │ │ │ ├── UIUnorderedList.html
│ │ │ │ └── UIWebPage.html
│ │ │ ├── Enums.html
│ │ │ ├── Enums
│ │ │ │ ├── Dependency.html
│ │ │ │ ├── DependencyType.html
│ │ │ │ ├── Element.html
│ │ │ │ └── LoginFormType.html
│ │ │ ├── Protocols.html
│ │ │ ├── Protocols
│ │ │ │ └── ElementRenderable.html
│ │ │ ├── badge.svg
│ │ │ ├── css
│ │ │ │ ├── highlight.css
│ │ │ │ └── jazzy.css
│ │ │ ├── img
│ │ │ │ ├── carat.png
│ │ │ │ ├── dash.png
│ │ │ │ └── gh.png
│ │ │ ├── index.html
│ │ │ ├── js
│ │ │ │ ├── jazzy.js
│ │ │ │ └── jquery.min.js
│ │ │ ├── search.json
│ │ │ └── undocumented.json
│ │ │ └── docSet.dsidx
│ └── .tgz
├── img
│ ├── carat.png
│ ├── dash.png
│ └── gh.png
├── index.html
├── js
│ ├── jazzy.js
│ └── jquery.min.js
├── search.json
└── undocumented.json
├── icons
├── github-link-trimmed.svg
├── speed-guage.svg
├── uiwebkit-icon-slim-sized.png
├── uiwebkit-icon-slim-sized.svg
├── uiwebkit-icon-trimmed.png
├── uiwebkit-icon-trimmed.svg
├── uiwebkit-icon.ai
├── uiwebkit-icon.png
└── uiwebkit-icon.svg
└── site
├── index.html
└── styles
└── main.css
/.gitignore:
--------------------------------------------------------------------------------
1 | Packages
2 | .build
3 | xcuserdata
4 | *.xcodeproj
5 | Config/secrets
6 | .DS_Store
7 | Tests/
8 |
--------------------------------------------------------------------------------
/.hound.yml:
--------------------------------------------------------------------------------
1 | swift:
2 | config_file: .swiftlint.yml
3 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | custom_rules:
2 | no_on_class_inheritance:
3 | regex: "\w*\s(struct|class)\s\w*\:\s{0,1}\w*\s{0,1}\{"
4 | match_kinds: objectliteral
5 | message: "Use an extension for inheritance or protocol implimentation."
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 | language: generic
5 | sudo: required
6 | dist: trusty
7 | osx_image: xcode8.2
8 | install:
9 | - eval "$(curl -sL https://gist.githubusercontent.com/calebkleveter/db4ef3b6746381e85f9b58e3b9adc6a2/raw/87980619cce44df7684d45fa1cd8b405697e1651/travis-build.sh)"
10 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Project Contributions
2 |
3 | If you want to contribute to this project, that's great!
4 |
5 | Start by opening an issue. Name it something like 'Proposal: IDEA', or 'Enhancement: IDEA'. The administrators of the project can review it. When you get the go-ahead, create the enhancement and submit a PR. Here are some rules to follow
6 |
7 | 1. Either use the `develop` branch or create your own.
8 | 2. Create the PR to merge in to `develop`, or create the branch on the base repo. Do _not_ create a PR that will merge in to `master`.
9 | 3. Try to follow the same commit messaging template as I use: `path/to/file - Cool enhancment`.
10 |
11 | The PR will then go through review, There are a couple of things we are looking for:
12 |
13 | 1. That the PR passes all the tests and CIs.
14 | 2. That there is as little code needed for a user to impliment the feature as possible.
15 |
16 | If either of these fail, they will need to be fixed before the PR is accepted.
17 |
18 | Have fun!
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Caleb Kleveter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.pins:
--------------------------------------------------------------------------------
1 | {
2 | "autoPin": true,
3 | "pins": [
4 | {
5 | "package": "BCrypt",
6 | "reason": null,
7 | "repositoryURL": "https://github.com/vapor/bcrypt.git",
8 | "version": "1.0.0"
9 | },
10 | {
11 | "package": "Bits",
12 | "reason": null,
13 | "repositoryURL": "https://github.com/vapor/bits.git",
14 | "version": "1.0.0"
15 | },
16 | {
17 | "package": "CTLS",
18 | "reason": null,
19 | "repositoryURL": "https://github.com/vapor/ctls.git",
20 | "version": "1.0.0"
21 | },
22 | {
23 | "package": "Console",
24 | "reason": null,
25 | "repositoryURL": "https://github.com/vapor/console.git",
26 | "version": "2.0.0"
27 | },
28 | {
29 | "package": "Core",
30 | "reason": null,
31 | "repositoryURL": "https://github.com/vapor/core.git",
32 | "version": "2.0.0"
33 | },
34 | {
35 | "package": "Crypto",
36 | "reason": null,
37 | "repositoryURL": "https://github.com/vapor/crypto.git",
38 | "version": "2.0.0"
39 | },
40 | {
41 | "package": "Debugging",
42 | "reason": null,
43 | "repositoryURL": "https://github.com/vapor/debugging.git",
44 | "version": "1.0.0"
45 | },
46 | {
47 | "package": "Engine",
48 | "reason": null,
49 | "repositoryURL": "https://github.com/vapor/engine.git",
50 | "version": "2.0.0"
51 | },
52 | {
53 | "package": "JSON",
54 | "reason": null,
55 | "repositoryURL": "https://github.com/vapor/json.git",
56 | "version": "2.0.0"
57 | },
58 | {
59 | "package": "Multipart",
60 | "reason": null,
61 | "repositoryURL": "https://github.com/vapor/multipart.git",
62 | "version": "2.0.0"
63 | },
64 | {
65 | "package": "Node",
66 | "reason": null,
67 | "repositoryURL": "https://github.com/vapor/node.git",
68 | "version": "2.0.0"
69 | },
70 | {
71 | "package": "Random",
72 | "reason": null,
73 | "repositoryURL": "https://github.com/vapor/random.git",
74 | "version": "1.0.0"
75 | },
76 | {
77 | "package": "Routing",
78 | "reason": null,
79 | "repositoryURL": "https://github.com/vapor/routing.git",
80 | "version": "2.0.0"
81 | },
82 | {
83 | "package": "Sockets",
84 | "reason": null,
85 | "repositoryURL": "https://github.com/vapor/sockets.git",
86 | "version": "2.0.0"
87 | },
88 | {
89 | "package": "SwiftMark",
90 | "reason": null,
91 | "repositoryURL": "https://github.com/calebkleveter/SwiftMark.git",
92 | "version": "1.0.1"
93 | },
94 | {
95 | "package": "TLS",
96 | "reason": null,
97 | "repositoryURL": "https://github.com/vapor/tls.git",
98 | "version": "2.0.0"
99 | },
100 | {
101 | "package": "Vapor",
102 | "reason": null,
103 | "repositoryURL": "https://github.com/vapor/vapor.git",
104 | "version": "2.0.1"
105 | }
106 | ],
107 | "version": 1
108 | }
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | import PackageDescription
2 |
3 | let package = Package(
4 | // Name of Package here:
5 | name: "UIWebKit",
6 | dependencies: [
7 | .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2, minor: 0),
8 | .Package(url: "https://github.com/calebkleveter/SwiftMark.git", majorVersion: 1, minor: 0)
9 | ],
10 | exclude: [
11 | "Config",
12 | "Database",
13 | "Localization",
14 | "Public",
15 | "Resources",
16 | "Tests",
17 | ]
18 | )
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # UIWebKit
4 |
5 | [](https://travis-ci.org/calebkleveter/UIWebKit)
6 | [](https://calebkleveter.github.io/UIWebKit/)
7 |
8 | Create UI's for Vapor without HTML.
9 |
10 | ## Example:
11 |
12 | Create a class to control the creation of a web page:
13 |
14 | ```swift
15 | import Foundation
16 | import UIWebKit
17 | import Vapor
18 |
19 | final class MainView: UIWebPage {
20 |
21 | override func configure() {
22 | addSectionText()
23 | addHead()
24 | }
25 |
26 | func addSectionText() {
27 | let content = UIElement(element: Element.p)
28 | content.add("Text")
29 | content.attributes["style"] = "font-family: Roboto, sans-serif;"
30 | for _ in 0...10 {
31 | section.add(content)
32 | }
33 | }
34 |
35 | func addHead() {
36 | let title = UIElement(element: .title)
37 | title.add("UIWebKit Example")
38 | let link = UIElement(element: .link)
39 | link.attributes["rel"] = "stylesheet"
40 | link.attributes["href"] = "https://fonts.googleapis.com/css?family=Roboto"
41 | head.add(title)
42 | head.add(link)
43 | }
44 | }
45 | ```
46 |
47 | Use the class to create the page:
48 |
49 | ```swift
50 | drop.get("about") { req in
51 | return MainView()
52 | }
53 | ```
54 | ## Documentation:
55 |
56 | You can get the API documentation [here](https://calebkleveter.github.io/UIWebKit/).
57 |
58 | ## Contributing:
59 |
60 | Read the contribution guidlines [here](https://github.com/calebkleveter/UIWebKit/blob/master/CONTRIBUTING.md).
61 |
62 | ## License:
63 |
64 | All code is under the [MIT license](https://github.com/calebkleveter/UIWebKit/blob/master/LICENSE) agreement.
65 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UIAnchor.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// A wrapper around an HTML `a` (anchor) element. This is what is used for links.
24 | open class UIAnchor {
25 |
26 | /// The anchor element `UIAnchor` represents.
27 | public let anchor = UIElement(element: .a)
28 |
29 | /// The text in the anchor.
30 | public var title: String {
31 | didSet {
32 | self.anchor.add(title)
33 | }
34 | }
35 |
36 | /// The link the anchor opens when selected.
37 | public var link: String {
38 | didSet {
39 | self.anchor.attributes["href"] = link
40 | }
41 | }
42 |
43 | /// Creates a basic `UIAnchor` object with a title and link.
44 | ///
45 | /// - Parameters:
46 | /// - title: The text that is used for the anchor element.
47 | /// - link: The link the anchor will open when it is selected.
48 | public init(title: String, link: String) {
49 | self.title = title
50 | self.link = link
51 | anchor.attributes["href"] = link
52 | anchor.add(title)
53 | }
54 | }
55 |
56 | extension UIAnchor: ElementRenderable {
57 |
58 | /// The top level anchor element of the `UIAnchor` object.
59 | public var topLevelElement: UIElement {
60 | return self.anchor
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UIForm.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2016 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// Designates the login that is used for creating the login form.
24 | public enum LoginFormType {
25 |
26 | /// Sets the form as taking an email for the users login.
27 | case email
28 |
29 | /// Sets the form as taking a username for the login
30 | case username
31 | }
32 |
33 | /// A wrapper class for an HTML form.
34 | open class UIForm {
35 |
36 | /// The HTML form that is the base of the class
37 | public let form = UIElement(element: .form)
38 |
39 | /// Creates a form with a label and input for each item.
40 | ///
41 | /// - Parameters:
42 | /// - items: The items that will be used for each label/input combonation for the form.
43 | /// - idPrefix: The prefix for the id's for the wrappr divs, inputs and labels. This defaults for `nil`.
44 | /// - submitText: The text for the submission button.
45 | /// - action: The path that the data will be sent to at form submission.
46 | public init(with items: [String], idPrefix: String? = nil, submitText: String, and action: String? = nil) {
47 | for item in items {
48 | self.form.add(UIFormElement(with: item, and: idPrefix))
49 | }
50 | let formSubmitButton = UIElement(element: .button)
51 | formSubmitButton.attributes["type"] = "submit"
52 | formSubmitButton.attributes["id"] = submitText.lowercased()
53 | formSubmitButton.add(submitText)
54 | self.form.add(formSubmitButton)
55 |
56 | guard let action = action else { return }
57 |
58 | self.form.attributes["action"] = action
59 | self.form.attributes["method"] = "POST"
60 | }
61 |
62 | /// Creates a form for loging in a user.
63 | ///
64 | /// - Parameters:
65 | /// - login: The login that will be used to authenticate the user. This could be an email or username.
66 | /// - action: The path that the data will be sent to at form submission.
67 | /// - Returns: A `UIForm` for authenticating a user.
68 | public class func loginForm(with login: LoginFormType, and action: String) -> UIForm {
69 | var formItems: [String] = []
70 |
71 | switch login {
72 | case .email: formItems.append("Email")
73 | case .username: formItems.append("Username")
74 | }
75 | formItems.append("Password")
76 |
77 | return UIForm(with: formItems, idPrefix: "user", submitText: "Login", and: action)
78 | }
79 |
80 | /// Creates a basic form for signing up a user.
81 | ///
82 | /// - Parameters:
83 | /// - action: The path that the data will be sent to at form submission.
84 | /// - idPrefix: The prefix for the element's ids. This defaults to "user".
85 | /// - Returns: The `UIForm` that contains the HTML form that will be submited on submission.
86 | public class func signUpForm(with action: String, and idPrefix: String = "user") -> UIForm {
87 | return UIForm(with: ["Email", "Username", "Password"], idPrefix: idPrefix, submitText: "Sign Up", and: action)
88 | }
89 |
90 | /// The top level element of the class. In this case, it is the form property.
91 | public var topElement: UIElement {
92 | return form
93 | }
94 | }
95 |
96 | extension UIForm: ElementRenderable {
97 |
98 | /// The top level element of `UIForm`. This is the `form` property.
99 | public var topLevelElement: UIElement {
100 | return self.form
101 | }
102 | }
103 |
104 | /// A label and input combonation that is used in a form.
105 | open class UIFormElement {
106 |
107 | /// The label that says what the input is for.
108 | let label: UIElement
109 |
110 | /// The text input for the form. The type for the input could be `email`, `password`, or `text` depending on the name of the form element.
111 | let input: UIElement
112 |
113 | /// The div that acts as a wrapper for the input and the label.
114 | let elementWrapperDiv: UIElement
115 |
116 | /// Creates a `UIFormElement` that contains a label and an input.
117 | ///
118 | /// - Parameters:
119 | /// - name: The name of the element. This is used to set the text in the label, set the placeholder in the input, and set the type of the input.
120 | /// - idPrefix: The prefix for the element id's. This defaults to nil
121 | init(with name: String, and idPrefix: String? = nil) {
122 | self.label = UIElement(element: .label)
123 | self.input = UIElement(element: .input)
124 | self.elementWrapperDiv = UIElement(element: .div)
125 |
126 | let casedName = String(name.characters.first ?? Character("")).uppercased() + String(name.characters.dropFirst()).lowercased()
127 | let lowerCasedName = name.lowercased()
128 |
129 | elementWrapperDiv.attributes["id"] = idPrefix == nil ? lowerCasedName : idPrefix! + "-" + lowerCasedName
130 |
131 | label.attributes["for"] = lowerCasedName
132 | label.attributes["id"] = idPrefix == nil ? lowerCasedName + "-label" : idPrefix! + "-" + lowerCasedName + "-label"
133 | label.add(casedName)
134 |
135 | input.attributes["name"] = lowerCasedName
136 | input.attributes["id"] = idPrefix == nil ? lowerCasedName + "-input" : idPrefix! + "-" + lowerCasedName + "-input"
137 | input.attributes["placeholder"] = casedName
138 |
139 | if lowerCasedName == "email" || lowerCasedName == "e-mail" {
140 | input.attributes["type"] = "email"
141 | } else if lowerCasedName == "password" {
142 | input.attributes["type"] = "password"
143 | } else {
144 | input.attributes["type"] = "text"
145 | }
146 |
147 | elementWrapperDiv.add(label)
148 | elementWrapperDiv.add(input)
149 | }
150 | }
151 |
152 | extension UIFormElement: ElementRenderable {
153 |
154 | /// The top level element of the form element. This is the wrapper div.
155 | public var topLevelElement: UIElement {
156 | return self.elementWrapperDiv
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UILink.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// `CSSLink` represents a link to a CSS file from the HTML file that is rendered.
24 | open class CSSLink {
25 |
26 | /// The `UILink` used to link to the CSS file.
27 | public let link: UILink
28 |
29 | /// Creates a link to a CSS file from the rendered HTML file.
30 | ///
31 | /// - Parameter href: The path to the CSS file. This parameter defaults `"css/main.css"`
32 | public init(href: String = "css/main.css") {
33 | self.link = UILink(href: href, rel: "stylesheet")
34 | }
35 | }
36 |
37 | extension CSSLink: ElementRenderable {
38 |
39 | /// The `link` element contained in the `CSSLink`'s `UILink`.
40 | public var topLevelElement: UIElement {
41 | return link.link
42 | }
43 | }
44 |
45 | /// A wrapper class for a `UIElement` that represents a `link` element.
46 | open class UILink {
47 |
48 | /// The `link` element that the class represents.
49 | public let link: UIElement = UIElement(element: .link)
50 |
51 | /// The `rel` attribute for the `link` tag.
52 | public var rel: String = "" {
53 | didSet {
54 | link.attributes["rel"] = rel
55 | }
56 | }
57 |
58 | /// The `href` attribute for the `link` tag.
59 | public var href: String = "" {
60 | didSet {
61 | link.attributes["href"] = href
62 | }
63 | }
64 |
65 | /// Creates a instance of `UILink` with an `href` and `rel` properties.
66 | ///
67 | /// - Parameters:
68 | /// - href: The path the the document being linked to.
69 | /// - rel: The relationship between the linked document to the current document. View the HTML [documention](http://devdocs.io/html/element/link) for more details.
70 | public init(href: String, rel: String) {
71 | self.href = href
72 | self.rel = rel
73 | }
74 | }
75 |
76 | extension UILink: ElementRenderable {
77 |
78 | /// The `link` tag represented by a `UIElement` object that is controlled by an instance `UILink`.
79 | public var topLevelElement: UIElement {
80 | return link
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UILists.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | // MARK: - UIUnorderedList
24 |
25 | /// A wrapper class that represents an HTML `ul` element.
26 | open class UIUnorderedList {
27 |
28 | /// The base `ul` element of the `UIUnorderedList` object.
29 | public let ul = UIElement(element: .ul)
30 |
31 | /// The list items used in the unordered list.
32 | public private(set) var listItems: [UIListItem] = []
33 |
34 | /// Creates a `UIUnorderedList` object with text in the list items.
35 | ///
36 | /// - Parameter text: The strings for the `UIListItems`.
37 | public init(text: [String]) {
38 | self.listItems = text.map { UIListItem(text: $0) }
39 | _ = listItems.map { ul.add($0) }
40 | }
41 |
42 | /// Creates a `UIUnorderedList` object with children in the list items.
43 | ///
44 | /// - Parameter elements: The elements that will be used in the `li` elements.
45 | public init(elements: [ElementRenderable]) {
46 | self.listItems = elements.map { UIListItem(children: [$0]) }
47 | _ = listItems.map { ul.add($0) }
48 | }
49 |
50 | /// Creates a `UIUnorderedList` object with custom `UIListItems`.
51 | ///
52 | /// - Parameter listElements: The `UIListItems` for the `UIUnorderedList` ul.
53 | public init(with listElements: [UIListItem]) {
54 | self.listItems = listElements
55 | _ = listItems.map { ul.add($0) }
56 | }
57 | }
58 |
59 | extension UIUnorderedList: ElementRenderable {
60 |
61 | /// The top level `ul` element.
62 | public var topLevelElement: UIElement {
63 | return self.ul
64 | }
65 | }
66 |
67 | // MARK: - UIOrderedList
68 |
69 | /// A wrapper class around an `ol` (odered list) element.
70 | open class UIOrderedList {
71 |
72 | /// The `ol` element that is represented by this class.
73 | public let ol = UIElement(element: .ol)
74 |
75 | /// The list items used in the ordered list.
76 | public private(set) var listItems: [UIListItem] = []
77 |
78 | /// Creates a `UIOrderedList` with text in the list items.
79 | ///
80 | /// - Parameter text: The strings for the `UIListItems`.
81 | public init(text: [String]) {
82 | self.listItems = text.map { UIListItem(text: $0) }
83 | _ = listItems.map { ol.add($0) }
84 | }
85 |
86 | /// Creates a `UIOrderedList` with children in the list items.
87 | ///
88 | /// - Parameter elements: The elements that will be used in the `li` elements.
89 | public init(elements: [ElementRenderable]) {
90 | self.listItems = elements.map { UIListItem(children: [$0]) }
91 | _ = listItems.map { ol.add($0) }
92 | }
93 |
94 | /// Creates a `UIOrderedList` with custom `UIListItems`.
95 | ///
96 | /// - Parameter listElements: The `UIListItems` for the `UIOrderedList` ol.
97 | public init(with listElements: [UIListItem]) {
98 | self.listItems = listElements
99 | _ = listItems.map { ol.add($0) }
100 | }
101 | }
102 |
103 | extension UIOrderedList: ElementRenderable {
104 |
105 | /// The top level `ol` element of the object.
106 | public var topLevelElement: UIElement {
107 | return self.ol
108 | }
109 | }
110 |
111 | // MARK: - UIListItem
112 |
113 | /// A wrapper class for li (list) elements.
114 | open class UIListItem {
115 |
116 | /// The base list element of the class
117 | public let li = UIElement(element: .li)
118 |
119 | /// The child elements of the li.
120 | public var children: [ElementRenderable] = []
121 |
122 | /// The text for the list element.
123 | public let text: String?
124 |
125 | /// Creates a `UIListItem` with the text passed in.
126 | ///
127 | /// - Parameter text: The text for instances `li` property.
128 | public init(text: String? = nil) {
129 | self.text = text
130 | if let text = text { li.add(text) }
131 | }
132 |
133 | /// Creates a `UIListItem` with children and no text.
134 | ///
135 | /// - Parameter children: The elements that are to be the children of the `li` element.
136 | public init(children: [ElementRenderable]) {
137 | self.text = nil
138 | self.children = children
139 | for child in children { li.add(child) }
140 | }
141 | }
142 |
143 | extension UIListItem: ElementRenderable {
144 |
145 | /// The `li` property of the instance of `UIListItem`.
146 | public var topLevelElement: UIElement {
147 | return self.li
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UINavigation.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2016 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// A wrapper object around a navigation element with a unordered list of anchors.
24 | open class UINavigation {
25 |
26 | /// The top level nav of the `UINavigation` object.
27 | public let nav = UIElement(element: .nav)
28 |
29 | /// The link and text the navigations anchor elements are created from.
30 | var anchorAttributes: [String: String]
31 |
32 | /// The anchor elements in the `nav's` unordered list.
33 | public var anchors: [UIAnchor]
34 |
35 | /// Creates a `UINavigation` with the attributs for the anchor elements.
36 | ///
37 | /// - Parameter navItems: The dictionary used for getting the information for the anchors. The key is used for the text and the value is used as the link.
38 | public init(navItems: [String: String]) {
39 | self.anchorAttributes = navItems
40 | self.anchors = anchorAttributes.map({ (title, link) -> UIAnchor in
41 | UIAnchor(title: title, link: link)
42 | })
43 | self.nav.add(UIUnorderedList(elements: self.anchors))
44 | }
45 | }
46 |
47 | extension UINavigation: ElementRenderable {
48 |
49 | /// The top level `nav` element of the `UINavigation` object.
50 | public var topLevelElement: UIElement {
51 | return self.nav
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UIParagraph.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// `UIParagraph` is a wrapper class around a `p` HTML tag which is represented by a `UIElement`.
24 | open class UIParagraph {
25 |
26 | /// The `p` element that is represented by the object.
27 | public let p = UIElement(element: .p)
28 |
29 | /// The text held in the `p` element.
30 | public var text: String {
31 | didSet {
32 | self.p.add(self.text)
33 | }
34 | }
35 |
36 | /// Creates a `UIParagraph` with the desired text.
37 | ///
38 | /// - Parameter text: The text used in the objects `p` element.
39 | public init(text: String) {
40 | self.text = text
41 | self.p.add(text)
42 | }
43 | }
44 |
45 | extension UIParagraph: ElementRenderable {
46 |
47 | /// The top level `p` element of the `UIParagraph`.
48 | public var topLevelElement: UIElement {
49 | return p
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Components/UITable.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// Handles an HTML table element.
24 | open class UITable {
25 |
26 | /// The HTML table. I have nothing else to say.
27 | public let table: UIElement
28 |
29 | /// The header row for the HTML table.
30 | public var headerRow: UITableRowHeader?
31 |
32 | /// The rows of data cells for the table.
33 | public var rows: [UITableRow]
34 |
35 | /// Creats a table with rows of data cells and no header.
36 | ///
37 | /// - Parameter data: A 2D array of strings that contains the data for the cells. Each sub-array is for a row of cells.
38 | public init(with data: [[String]]) {
39 | self.table = UIElement(element: .table)
40 | self.rows = []
41 | for row in data {
42 | let newRow = UITableRow(with: row)
43 | self.rows.append(newRow)
44 | self.table.add(newRow)
45 | }
46 | }
47 |
48 | /// Creates a table with a header row and subsequent rows of data cells.
49 | ///
50 | /// - Parameters:
51 | /// - headerTitles: The titles that will be in the header row's cells.
52 | /// - rowsCellData: A 2D array of strings that contains the data for the data cells. Each sub-array is for a row of cells.
53 | public init(with headerTitles: [String], and rowsCellData: [[String]]) {
54 | self.table = UIElement(element: .table)
55 | self.rows = []
56 |
57 | self.headerRow = UITableRowHeader(with: headerTitles)
58 | self.table.add(headerRow ?? UITableRowHeader(with: []))
59 | for row in rowsCellData {
60 | let newRow = UITableRow(with: row)
61 | self.rows.append(newRow)
62 | self.table.add(newRow)
63 | }
64 | }
65 | }
66 |
67 | /// A row in an HTML table.
68 | open class UITableRow {
69 |
70 | /// The row that contains the table cells.
71 | public let row: UIElement
72 |
73 | /// The cells that contain the data for the table.
74 | public var cells: [UITableCell]
75 |
76 | /// Creates a row for an HTML table with cells containing data.`
77 | ///
78 | /// - Parameter data: The data that will conatined in the cells in the table row.
79 | public init(with data: [String]) {
80 | self.row = UIElement(element: .tr)
81 | self.cells = []
82 |
83 | for string in data {
84 | let cell = UITableCell(with: string)
85 | self.cells.append(cell)
86 | self.row.add(cell)
87 | }
88 | }
89 | }
90 |
91 | /// A header row in an HTML table.
92 | open class UITableRowHeader {
93 |
94 | /// The row that contains the header cells.
95 | public let rowHeader: UIElement
96 |
97 | /// The header cells contained in the header row.
98 | public var cellHeaders: [UITableCellHeader]
99 |
100 | /// Creates a header row with cells for an HTML table.
101 | ///
102 | /// - Parameter data: The titles that will be contained in the header cells.
103 | public init(with data: [String]) {
104 | self.rowHeader = UIElement(element: .tr)
105 | self.cellHeaders = []
106 |
107 | for string in data {
108 | let cellHeader = UITableCellHeader(with: string)
109 | self.cellHeaders.append(cellHeader)
110 | rowHeader.add(cellHeader)
111 | }
112 | }
113 | }
114 |
115 | /// Handles the header cell of an HTML table header row.
116 | open class UITableCellHeader {
117 |
118 | /// The header cell that contains the description of the data contained in subsequent cells.
119 | public let cellHeader: UIElement
120 |
121 | /// Creates a header cell for a table's header row.
122 | ///
123 | /// - Parameter data: The title for the data contained in the following cells.
124 | public init(with data: String) {
125 | self.cellHeader = UIElement(element: .th)
126 | self.cellHeader.add(data)
127 | }
128 | }
129 |
130 | /// Handles the cell of a row in an HTML table.
131 | open class UITableCell {
132 |
133 | /// The cell that contains the data for the table.
134 | public let cell: UIElement
135 |
136 | /// Creats a HTML table row cell.
137 | ///
138 | /// - Parameter data: The data that the cell will contain.
139 | public init(with data: String) {
140 | self.cell = UIElement(element: .td)
141 | self.cell.add(data)
142 | }
143 | }
144 |
145 | extension UITable: ElementRenderable {
146 |
147 | /// The table element that is contained in the `UITable`.
148 | public var topLevelElement: UIElement {
149 | return self.table
150 | }
151 | }
152 |
153 | extension UITableRow: ElementRenderable {
154 |
155 | /// The row contained in the current `UITableRow`.
156 | public var topLevelElement: UIElement{
157 | return self.row
158 | }
159 | }
160 |
161 | extension UITableRowHeader: ElementRenderable {
162 |
163 | /// The header row of the current `UITableRowHeader`.
164 | public var topLevelElement: UIElement {
165 | return self.rowHeader
166 | }
167 | }
168 |
169 | extension UITableCellHeader: ElementRenderable {
170 |
171 | /// The header cell contained in the current instance of `UITableCellHeader`.
172 | public var topLevelElement: UIElement {
173 | return self.cellHeader
174 | }
175 | }
176 |
177 | extension UITableCell: ElementRenderable {
178 |
179 | /// The cell contained in the current instance of `UITableCell`.
180 | public var topLevelElement: UIElement {
181 | return self.cell
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Dependencies.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | /// Although dependencies use more CSS or JavaScript, this enum is used more for adding the CSS of the dependeny to the head of the page and the JavaScript below the footer of the page.
24 | public enum DependencyType {
25 |
26 | /// Defines the CDN links of the dependency as linking to JavaScript.
27 | case javaScript
28 |
29 | /// Defines the CDN links of the dependency as linking to CSS.
30 | case css
31 | }
32 |
33 | /// A dependency that a UIWebPage loads in.
34 | public enum Dependency {
35 |
36 | /// This case is for loading jQuery into a `UIWebPage`.
37 | case jQuery
38 |
39 | /// This case is used for loading Twitter Bootstrap into a `UIWebPage`.
40 | case bootstrap
41 |
42 | /// For loading UIKit (the [front-end framework](https://getuikit.com/)) into a `UIWebPage`.
43 | case uiKit
44 |
45 | /// For loading Normalize.css into a `UIWebPage`.
46 | case normalize
47 |
48 | /// For loading custom CSS files into a web page.
49 | ///
50 | /// - parameter _: The path to the CSS file.
51 | case customCSS(String)
52 |
53 | /// For loading custom JS into a web page.
54 | ///
55 | /// - parameter _: The path to the JS file.
56 | case customJavaScript(String)
57 |
58 | /// Returns a a dictionary with the key as the type of CDN links that are used in the value and the value as an array of Strings that are the CDN links to the dependency.
59 | public var htmlTags: [DependencyType: [String]] {
60 | switch self {
61 | case .bootstrap: return [.css: ["", ""], .javaScript: [""]]
62 | case .jQuery: return [.javaScript: [""]]
63 | case .uiKit: return [.css: [""], .javaScript: [""]]
64 | case .customCSS(let path): return [.css: [""]]
66 | case .normalize: return [.css: ["', '<', '/', '(', ')', '"', '{', or '}'.
28 | func isDangerousAscii() -> Bool {
29 | if self == "<" || self == ">" || self == "/" || self == "(" || self == ")" || self == "{" || self == "}" || self == "\"" { return true }
30 | return false
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/Extensions/String.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2017 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | extension String {
24 |
25 | /// Encodes a string to pervent direct HTML injection to a web page.
26 | ///
27 | /// - Returns: The encoded string.
28 | func safetyHTMLEncoded() -> String {
29 | let htmlAsciiCodes: [String: String] = ["<": "<", ">": ">", "/": "/", "(": "(", ")": ")", "{": "{", "}": "}", "\"": """]
30 | var finalString = ""
31 | _ = self.characters.map {
32 | if $0.isDangerousAscii() {
33 | finalString.append(htmlAsciiCodes[String($0)]!)
34 | } else { finalString.append(String($0)) }
35 | }
36 | return finalString
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/UIElement.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2016 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import Foundation
24 | import SwiftMark
25 |
26 | /// A UIElement works as the wrapper for an HTML element.
27 | public class UIElement {
28 |
29 | /// The element that is returned when the UIElement is parsed.
30 | public var element: Element
31 |
32 | /// The opening tag of the element.
33 | public var start: String
34 |
35 | /// The closing tag of the element.
36 | public var end: String
37 |
38 | /// Create an instance of UIElement that is the element passed in.
39 | ///
40 | /// - parameter element: The element that is created by `.parse()`.
41 | ///
42 | /// - returns: An instance of UIElement with it's properties initilized with the element passed in.
43 | public init(element: Element) {
44 | switch element.isSingleTag {
45 | case true:
46 | self.element = element
47 | self.start = "<\(element.rawValue)/>"
48 | self.end = ""
49 | case false:
50 | self.element = element
51 | self.start = "<\(element.rawValue)>"
52 | self.end = "\(element.rawValue)>"
53 | }
54 | }
55 |
56 | /// The attributes that are added to the element on parsing.
57 | public var attributes: [String: String] = [:]
58 |
59 | /// The elements text.
60 | public private(set)var text: String = ""
61 |
62 | /// The elements children.
63 | public private(set)var children: [ElementRenderable] = []
64 |
65 | /// Child elements that are added in String format. Note that these elements are added as children _first_.
66 | public private(set)var rawElements: [String] = []
67 |
68 | /// Adds text to an element _if_ it is not an empty element. The text is safety encoded.
69 | ///
70 | /// - parameter text: The text to add to the element.
71 | public func add(_ text: String) {
72 | if !element.isSingleTag {
73 | self.text = text.safetyHTMLEncoded()
74 | }
75 | }
76 |
77 | /// Adds text to an element _if_ it is not a single tag element. Note that this text is not safety encoded.
78 | ///
79 | /// - Parameter text: The text that will be added to the element. If you want to know more, ask Jon Skeet 🦄.
80 | public func addRaw(_ text: String) {
81 | if !element.isSingleTag {
82 | self.text = text
83 | }
84 | }
85 |
86 | /// Adds a child element to an element _if_ it is not an empty element.
87 | ///
88 | /// - parameter child: The element to add to the element.
89 | public func add(_ child: ElementRenderable) {
90 | if !element.isSingleTag {
91 | self.children.append(child)
92 | }
93 | }
94 |
95 | /// Adds a String to the rawElements array _if_ it is not a single tag element.
96 | ///
97 | /// - Parameter element: The element that will be added as a child of the current element.
98 | public func inject(_ element: String) {
99 | if !self.element.isSingleTag {
100 | self.rawElements.append(element)
101 | }
102 | }
103 |
104 |
105 | /// Renders Markdown to HTML that gets added as child elements. This method uses `.inject(string)` for adding the elements.
106 | ///
107 | /// - Warning: This method uses an incomplete and broken moduel to render the Markdown. This should be fixed in the future, but for now use Vapor's [markdown package](https://github.com/vapor/markdown).
108 | ///
109 | /// - Parameter string: The Markdown that will be rendered to HTML.
110 | public func addMarkdown(_ string: String)throws {
111 | if !element.isSingleTag {
112 | let renderer = MarkdownRenderer()
113 | let html = try renderer.render(string)
114 | self.inject(html)
115 | }
116 | }
117 |
118 | /// Creates HTML from the current element and all it's children. This method is deprecated due to bad naming. Use the `render()` method instead.
119 | ///
120 | /// - returns: The HTML from the current elements and it's children.
121 | @available(*, deprecated: 2.0, message: "Use the render method instead. This method will be removed in version 4")
122 | public func parse() -> String {
123 | print("[UIWebKit: \(Date.init())] - The parse() method is deprecated and will be removed in version 4. Use the render() method instead.")
124 | var html = ""
125 | self.appendAttributes()
126 | html.append(self.start)
127 | html.append(text)
128 | if !rawElements.isEmpty {
129 | for element in rawElements {
130 | html.append(element)
131 | }
132 | }
133 | if !children.isEmpty {
134 | for element in children {
135 | html.append(element.topLevelElement.render())
136 | }
137 | }
138 | html.append(self.end)
139 | return html
140 | }
141 |
142 | /// Creates HTML from the current element and all it's children.
143 | ///
144 | /// - Returns: The HTML from the current elements and it's children.
145 | public func render() -> String {
146 | var html = ""
147 | self.appendAttributes()
148 | html.append(self.start)
149 | html.append(text)
150 | if !rawElements.isEmpty {
151 | for element in rawElements {
152 | html.append(element)
153 | }
154 | }
155 | if !children.isEmpty {
156 | for element in children {
157 | html.append(element.topLevelElement.render())
158 | }
159 | }
160 | html.append(self.end)
161 | return html
162 | }
163 |
164 | /// Adds the elements attributes to it's opening tag.
165 | private func appendAttributes() {
166 | var attr = ""
167 | for (key, value) in self.attributes {
168 | attr.append("\(key)=\"\(value)\" ")
169 | }
170 | start = "<\(element.rawValue) \(attr)>"
171 | }
172 | }
173 |
174 | extension UIElement: ElementRenderable {
175 |
176 | /// The top level element of the current element. This means `self`.
177 | public var topLevelElement: UIElement {
178 | return self
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/Sources/UIWebKit/UIWebPage.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2016 Caleb Kleveter
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import Foundation
24 | import Vapor
25 | import HTTP
26 |
27 | /// Represents a web page and contains all the HTML elements.
28 | open class UIWebPage {
29 |
30 | /// The head in a web page.
31 | public var head: UIElement
32 |
33 | /// The header of the page.
34 | public var header: UIElement
35 |
36 | /// The section between the header and the footer of the page.
37 | public var section: UIElement
38 |
39 | /// The footer of the page.
40 | public var footer: UIElement
41 |
42 | /// Dependancies that will be loaded into the webpage such as Bootstrap or JQuery.
43 | private(set) var dependancies: [Dependency] = []
44 |
45 |
46 | /// Creates a web page with a head, header, section and footer.
47 | ///
48 | /// - returns: A UIWebPage with all the neccasary elements.
49 | public init() {
50 | self.head = UIElement(element: .head)
51 | self.header = UIElement(element: .header)
52 | self.section = UIElement(element: .section)
53 | self.footer = UIElement(element: .footer)
54 | }
55 |
56 | /// Creates an instance of `UIWebPage` with the basic required elements.
57 | ///
58 | /// - Parameter title: The title of the page.
59 | convenience public init(title: String) {
60 | self.init()
61 | self.head.inject("
\(title)")
62 | }
63 |
64 | /// For custom configuration of the web page before it is rendered. Over-ride this method to do anything before page rendering.
65 | open func configure() {}
66 |
67 | /// Renders the current page to a View with bytes that can be returned from a droplet route.
68 | ///
69 | /// - Returns: A view that contains the pages HTML in bytes.
70 | /// - Throws: Any errors that get thrown when creating the view.
71 | public func render() -> View {
72 | self.configure()
73 | var html = ""
74 | html.append("")
75 | for dependency in dependancies {
76 | if let cssTags = dependency.htmlTags[.css] {
77 | for tag in cssTags {
78 | head.inject(tag)
79 | }
80 | }
81 | }
82 | html.append(head.render())
83 | html.append("")
84 | html.append(header.render())
85 | html.append(section.render())
86 | html.append(footer.render())
87 | for dependency in dependancies {
88 | if let jsTags = dependency.htmlTags[.javaScript] {
89 | for tag in jsTags {
90 | html.append(tag)
91 | }
92 | }
93 | }
94 | html.append("")
95 |
96 | return View(bytes: html.bytes)
97 | }
98 |
99 | /// Adds dependancies that will be loaded into the webpage.
100 | ///
101 | /// - Parameter dependancy: The dependancy that will added to the webpage.
102 | public func `import`(_ dependency: Dependency) {
103 | self.dependancies.append(dependency)
104 | }
105 | }
106 |
107 | extension UIWebPage: ResponseRepresentable {
108 |
109 | /// Handles auto rendering for routes.
110 | ///
111 | /// - Returns: A response containing the view from rendering.
112 | /// - Throws: Any errors generated from creating the view.
113 | public func makeResponse()throws -> Response {
114 | return render().makeResponse()
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import UIWebKit
3 |
4 | XCTMain([
5 | testCase(UIElementTests.allTests)
6 | ])
7 |
--------------------------------------------------------------------------------
/Tests/UIWebKitTests/UIElementTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import UIWebKit
3 |
4 | class UIElementTests: XCTestCase {
5 | static let allTests = [
6 | ("Test Element Start", testElementStart),
7 | ("Test Element End", testElementEnd),
8 | ("Test Element Attributes", testAttributes),
9 | ("Test Element Text", testText),
10 | ("Test Element Children", testChildren),
11 | ("Test Element Parse", testParse)
12 | ]
13 |
14 | func testElementStart() {
15 | let element = UIElement(element: .p)
16 | XCTAssert(element.start == "
Although dependencies use more CSS or JavaScript, this enum is used more for adding the CSS of the dependeny to the head of the page and the JavaScript below the footer of the page.
Although dependencies use more CSS or JavaScript, this enum is used more for adding the CSS of the dependeny to the head of the page and the JavaScript below the footer of the page.