LEFT | RIGHT |
1 // | 1 // |
2 // FavIcon | 2 // FavIcon |
3 // Copyright © 2016 Leon Breedt | 3 // Copyright © 2016 Leon Breedt |
4 // | 4 // |
5 // Licensed under the Apache License, Version 2.0 (the "License"); | 5 // Licensed under the Apache License, Version 2.0 (the "License"); |
6 // you may not use this file except in compliance with the License. | 6 // you may not use this file except in compliance with the License. |
7 // You may obtain a copy of the License at | 7 // You may obtain a copy of the License at |
8 // | 8 // |
9 // http://www.apache.org/licenses/LICENSE-2.0 | 9 // http://www.apache.org/licenses/LICENSE-2.0 |
10 // | 10 // |
11 // Unless required by applicable law or agreed to in writing, software | 11 // Unless required by applicable law or agreed to in writing, software |
12 // distributed under the License is distributed on an "AS IS" BASIS, | 12 // distributed under the License is distributed on an "AS IS" BASIS, |
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 // See the License for the specific language governing permissions and | 14 // See the License for the specific language governing permissions and |
15 // limitations under the License. | 15 // limitations under the License. |
16 // | 16 // |
17 | 17 |
18 import FavIcon.XMLDocument | 18 import FavIcon.XMLDocument |
19 import Foundation | |
20 | 19 |
21 /// Represents an icon size. | 20 /// Represents an icon size. |
22 struct IconSize: Hashable, Equatable { | 21 struct IconSize: Hashable, Equatable { |
23 var hashValue: Int { | 22 var hashValue: Int { |
24 return width.hashValue ^ height.hashValue | 23 return width.hashValue ^ height.hashValue |
25 } | 24 } |
26 | 25 |
27 static func == (lhs: IconSize, rhs: IconSize) -> Bool { | 26 static func == (lhs: IconSize, rhs: IconSize) -> Bool { |
28 return lhs.width == rhs.width && lhs.height == rhs.height | 27 return lhs.width == rhs.width && lhs.height == rhs.height |
29 } | 28 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 // swiftlint:disable function_body_length | 62 // swiftlint:disable function_body_length |
64 // swiftlint:disable cyclomatic_complexity | 63 // swiftlint:disable cyclomatic_complexity |
65 func examineHTMLMeta(_ document: HTMLDocument, baseURL: URL) -> [String: String]
{ | 64 func examineHTMLMeta(_ document: HTMLDocument, baseURL: URL) -> [String: String]
{ |
66 var resp: [String: String] = [:] | 65 var resp: [String: String] = [:] |
67 for meta in document.query("/html/head/meta") { | 66 for meta in document.query("/html/head/meta") { |
68 if let property = meta.attributes["property"]?.lowercased(), | 67 if let property = meta.attributes["property"]?.lowercased(), |
69 let content = meta.attributes["content"] { | 68 let content = meta.attributes["content"] { |
70 switch property { | 69 switch property { |
71 case "og:url": | 70 case "og:url": |
72 resp["og:url"] = content | 71 resp["og:url"] = content |
73 break | |
74 case "og:description": | 72 case "og:description": |
75 resp["description"] = content | 73 resp["description"] = content |
76 break | |
77 case "og:image": | 74 case "og:image": |
78 resp["image"] = content | 75 resp["image"] = content |
79 break | |
80 case "og:title": | 76 case "og:title": |
81 resp["title"] = content | 77 resp["title"] = content |
82 break | |
83 case "og:site_name": | 78 case "og:site_name": |
84 resp["site_name"] = content | 79 resp["site_name"] = content |
85 break | |
86 default: | 80 default: |
87 break | 81 break |
88 } | 82 } |
89 } | 83 } |
90 if let name = meta.attributes["name"]?.lowercased(), | 84 if let name = meta.attributes["name"]?.lowercased(), |
91 let content = meta.attributes["content"], | 85 let content = meta.attributes["content"], |
92 name == "description" { | 86 name == "description" { |
93 resp["description"] = resp["description"] ?? content | 87 resp["description"] = resp["description"] ?? content |
94 } | 88 } |
95 } | 89 } |
96 | 90 |
97 for title in document.query("/html/head/title") { | 91 for title in document.query("/html/head/title") { |
98 if let titleString = title.contents { | 92 if let titleString = title.contents { |
99 resp["title"] = resp["title"] ?? titleString | 93 resp["title"] = resp["title"] ?? titleString |
100 } | 94 } |
101 } | 95 } |
102 | 96 |
103 for link in document.query("/html/head/link") { | 97 for link in document.query("/html/head/link") { |
104 if let rel = link.attributes["rel"], | 98 if let rel = link.attributes["rel"], |
105 let href = link.attributes["href"], | 99 let href = link.attributes["href"], |
106 let url = URL(string: href, relativeTo: baseURL) { | 100 let url = URL(string: href, relativeTo: baseURL) { |
107 switch rel.lowercased() { | 101 switch rel.lowercased() { |
108 case "canonical": | 102 case "canonical": |
109 resp["canonical"] = url.absoluteString | 103 resp["canonical"] = url.absoluteString |
110 break | |
111 case "amphtml": | 104 case "amphtml": |
112 resp["amphtml"] = url.absoluteString | 105 resp["amphtml"] = url.absoluteString |
113 break | |
114 case "search": | 106 case "search": |
115 resp["search"] = url.absoluteString | 107 resp["search"] = url.absoluteString |
116 break | |
117 case "fluid-icon": | 108 case "fluid-icon": |
118 resp["fluid-icon"] = url.absoluteString | 109 resp["fluid-icon"] = url.absoluteString |
119 break | |
120 case "alternate": | 110 case "alternate": |
121 let application = link.attributes["application"] | 111 let application = link.attributes["application"] |
122 if application == "application/atom+xml" { | 112 if application == "application/atom+xml" { |
123 resp["atom"] = url.absoluteString | 113 resp["atom"] = url.absoluteString |
124 } | 114 } |
125 break | |
126 default: | 115 default: |
127 break | 116 break |
128 } | 117 } |
129 } | 118 } |
130 } | 119 } |
131 return resp | 120 return resp |
132 } | 121 } |
133 | 122 |
134 func extractHTMLHeadIcons(_ document: HTMLDocument, baseURL: URL) -> [DetectedIc
on] { | 123 func extractHTMLHeadIcons(_ document: HTMLDocument, baseURL: URL) -> [DetectedIc
on] { |
135 var icons: [DetectedIcon] = [] | 124 var icons: [DetectedIcon] = [] |
136 | 125 |
137 for link in document.query("/html/head/link") { | 126 for link in document.query("/html/head/link") { |
138 if let rel = link.attributes["rel"], | 127 if let rel = link.attributes["rel"], |
139 let href = link.attributes["href"], | 128 let href = link.attributes["href"], |
140 let url = URL(string: href, relativeTo: baseURL) { | 129 let url = URL(string: href, relativeTo: baseURL) { |
141 switch rel.lowercased() { | 130 switch rel.lowercased() { |
142 case "shortcut icon": | 131 case "shortcut icon": |
143 icons.append(DetectedIcon(url: url.absoluteURL, type: .shortcut)
) | 132 icons.append(DetectedIcon(url: url.absoluteURL, type: .shortcut)
) |
144 break | |
145 case "icon": | 133 case "icon": |
146 if let type = link.attributes["type"], type.lowercased() == "ima
ge/png" { | 134 if let type = link.attributes["type"], type.lowercased() == "ima
ge/png" { |
147 let sizes = parseHTMLIconSizes(link.attributes["sizes"]) | 135 let sizes = parseHTMLIconSizes(link.attributes["sizes"]) |
148 if sizes.count > 0 { | 136 if sizes.count > 0 { |
149 for size in sizes { | 137 for size in sizes { |
150 if let type = kRelIconTypeMap[size] { | 138 if let type = kRelIconTypeMap[size] { |
151 icons.append(DetectedIcon(url: url, | 139 icons.append(DetectedIcon(url: url, |
152 type: type, | 140 type: type, |
153 width: size.width, | 141 width: size.width, |
154 height: size.height)) | 142 height: size.height)) |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 /// - returns: An array of `DetectedIcon` structures. | 236 /// - returns: An array of `DetectedIcon` structures. |
249 func extractBrowserConfigXMLIcons(_ document: LBXMLDocument, baseURL: URL) -> [D
etectedIcon] { | 237 func extractBrowserConfigXMLIcons(_ document: LBXMLDocument, baseURL: URL) -> [D
etectedIcon] { |
250 var icons: [DetectedIcon] = [] | 238 var icons: [DetectedIcon] = [] |
251 | 239 |
252 for tile in document.query("/browserconfig/msapplication/tile/*") { | 240 for tile in document.query("/browserconfig/msapplication/tile/*") { |
253 if let src = tile.attributes["src"], | 241 if let src = tile.attributes["src"], |
254 let url = URL(string: src, relativeTo: baseURL)?.absoluteURL { | 242 let url = URL(string: src, relativeTo: baseURL)?.absoluteURL { |
255 switch tile.name.lowercased() { | 243 switch tile.name.lowercased() { |
256 case "tileimage": | 244 case "tileimage": |
257 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 144, height: 144)) | 245 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 144, height: 144)) |
258 break | |
259 case "square70x70logo": | 246 case "square70x70logo": |
260 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 70, height: 70)) | 247 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 70, height: 70)) |
261 break | |
262 case "square150x150logo": | 248 case "square150x150logo": |
263 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 150, height: 150)) | 249 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 150, height: 150)) |
264 break | |
265 case "wide310x150logo": | 250 case "wide310x150logo": |
266 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 310, height: 150)) | 251 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 310, height: 150)) |
267 break | |
268 case "square310x310logo": | 252 case "square310x310logo": |
269 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 310, height: 310)) | 253 icons.append(DetectedIcon(url: url, type: .microsoftPinnedSite,
width: 310, height: 310)) |
270 break | |
271 default: | 254 default: |
272 break | 255 break |
273 } | 256 } |
274 } | 257 } |
275 } | 258 } |
276 | 259 |
277 return icons | 260 return icons |
278 } | 261 } |
279 | 262 |
280 /// Extracts the Web App Manifest URLs from an HTML document, if any. | 263 /// Extracts the Web App Manifest URLs from an HTML document, if any. |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 for size in string.components(separatedBy: .whitespaces) { | 307 for size in string.components(separatedBy: .whitespaces) { |
325 let parts = size.components(separatedBy: "x") | 308 let parts = size.components(separatedBy: "x") |
326 if parts.count != 2 { continue } | 309 if parts.count != 2 { continue } |
327 if let width = Int(parts[0]), let height = Int(parts[1]) { | 310 if let width = Int(parts[0]), let height = Int(parts[1]) { |
328 sizes.append(IconSize(width: width, height: height)) | 311 sizes.append(IconSize(width: width, height: height)) |
329 } | 312 } |
330 } | 313 } |
331 } | 314 } |
332 return sizes | 315 return sizes |
333 } | 316 } |
LEFT | RIGHT |