├── .gitignore
├── Example
├── HTNSwift.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
├── HTNSwift
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── HTNSwift.entitlements
│ ├── Info.plist
│ ├── Test.swift
│ ├── ViewController.swift
│ └── VueTest.swift
└── Podfile
├── HTN.podspec
├── HTNCLI
├── .gitignore
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
│ └── HTNCLI
│ │ ├── Build.swift
│ │ ├── HTNCLI.swift
│ │ └── main.swift
└── install.sh
├── HTNExamples
├── .gitignore
├── Podfile
├── Podfile.lock
├── Sample.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
├── Sample.xcworkspace
│ └── contents.xcworkspacedata
└── Sample
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── Flexbox.h
│ ├── Flexbox.m
│ ├── Info.plist
│ ├── MainViewController.h
│ ├── MainViewController.m
│ ├── UIColor+Extension.h
│ ├── UIColor+Extension.m
│ └── main.m
├── LICENSE
├── Package.swift
├── README.md
├── Sources
├── Core
│ ├── CSS
│ │ ├── CSSDeclaration.swift
│ │ ├── CSSDefault.swift
│ │ ├── CSSParser.swift
│ │ ├── CSSProperty.swift
│ │ ├── CSSRule.swift
│ │ ├── CSSSelector.swift
│ │ └── CSSStyleSheet.swift
│ ├── HTML
│ │ ├── Attribute.swift
│ │ ├── Document.swift
│ │ ├── Element.swift
│ │ ├── HTMLToken.swift
│ │ ├── HTMLTokenizer.swift
│ │ └── HTMLTreeBuilder.swift
│ ├── HTNFundation
│ │ ├── HTNMultilingualism.swift
│ │ ├── HTNStateMachine.swift
│ │ ├── Node.swift
│ │ ├── SMNetWorking.swift
│ │ └── StringExtension.swift
│ ├── JavaScript
│ │ ├── Archive
│ │ │ ├── JSNode.swift
│ │ │ ├── JSToken.swift
│ │ │ ├── JSTokenizer.swift
│ │ │ └── JSTreeBuilder.swift
│ │ ├── CodeGeneratorFromJSToOC.swift
│ │ ├── JNode.swift
│ │ ├── JParser.swift
│ │ ├── JState.swift
│ │ ├── JToken.swift
│ │ ├── JTokenizer.swift
│ │ ├── JTransformer.swift
│ │ ├── JTraverser.swift
│ │ └── babylonAll.js
│ ├── Layout
│ │ ├── HTMLToTexture.swift
│ │ ├── JSONToFrame.swift
│ │ ├── LayoutElement.swift
│ │ ├── RenderObject.swift
│ │ └── StyleResolver.swift
│ └── OC
│ │ ├── OCAST.swift
│ │ ├── OCArithmetics.swift
│ │ ├── OCInterpreter.swift
│ │ ├── OCLexer.swift
│ │ ├── OCParser.swift
│ │ ├── OCToken.swift
│ │ └── StaticAnalyzer
│ │ ├── OCStaticAnalyzer.swift
│ │ ├── OCSymbol.swift
│ │ ├── OCSymbolTable.swift
│ │ └── OCVisitor.swift
└── H5Editor
│ ├── H5EditorDatasource.swift
│ ├── H5EditorObjc.swift
│ ├── H5EditorStruct.swift
│ └── H5EditorToFrame.swift
├── deploy.sh
└── htnbluemap.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | Pods/
49 | HTNSwift.xcworkspace/
50 | Podfile.lock
51 |
52 | # Carthage
53 | #
54 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
55 | # Carthage/Checkouts
56 |
57 | Carthage/Build
58 |
59 | # fastlane
60 | #
61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
62 | # screenshots whenever they are needed.
63 | # For more information about the recommended setup visit:
64 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
65 |
66 | fastlane/report.xml
67 | fastlane/Preview.html
68 | fastlane/screenshots
69 | fastlane/test_output
70 |
--------------------------------------------------------------------------------
/Example/HTNSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/HTNSwift/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 |
15 |
16 | func applicationDidFinishLaunching(_ aNotification: Notification) {
17 | // Insert code here to initialize your application
18 | }
19 |
20 | func applicationWillTerminate(_ aNotification: Notification) {
21 | // Insert code here to tear down your application
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/Example/HTNSwift/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 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Example/HTNSwift/HTNSwift.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Example/HTNSwift/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017年 Starming. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 | NSAppTransportSecurity
32 |
33 | NSAllowsArbitraryLoads
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/HTNSwift/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import HTN
11 |
12 |
13 | struct Data {
14 | let a: String
15 | }
16 |
17 |
18 | @available(OSX 10.13, *)
19 | @available(OSX 10.13, *)
20 | class ViewController: NSViewController {
21 |
22 | @IBOutlet weak var inputLb: NSTextField!
23 |
24 | @IBOutlet var nativeCodeLb: NSTextView!
25 | @IBOutlet weak var inputTypeSelect: NSPopUpButton!
26 | @IBOutlet weak var toNativeBt: NSButton!
27 |
28 | @IBAction func toNativeAction(_ sender: Any) {
29 | guard let item = inputTypeSelect.selectedItem else {
30 | return
31 | }
32 | switch item.title {
33 | case "HTML 转 Texture":
34 | htmlToTexture()
35 | case "JSON 转 Frame":
36 | jsonToFrame()
37 | case "Javascript 测试":
38 | javascriptTest()
39 | default:
40 | htmlToTexture()
41 | }
42 |
43 | }
44 |
45 | func jsonToFrame() {
46 | guard inputLb.stringValue.count > 0 else {
47 | return;
48 | }
49 | //请求地址在输入框输入
50 | SMNetWorking().requestJSON(inputLb.stringValue) { (jsonModel) in
51 | guard let model = jsonModel else {
52 | return
53 | }
54 | let reStr = H5EditorToFrame(H5EditorObjc()).convert(model)
55 | // print(reStr)
56 | DispatchQueue.main.async {
57 | self.nativeCodeLb.string = reStr.0 + "\n\n" + reStr.1
58 | }
59 | }
60 | }
61 | public func javascriptTest() {
62 |
63 | }
64 |
65 |
66 |
67 | //递归所有子节点
68 | public func htmlToTexture() {
69 | let treeBuilder = HTMLTreeBuilder(inputLb.stringValue)
70 | _ = treeBuilder.parse()
71 | let cssStyle = CSSParser(treeBuilder.doc.allStyle()).parseSheet()
72 | let document = StyleResolver().resolver(treeBuilder.doc, styleSheet: cssStyle)
73 | document.des() //打印包含样式信息的 DOM 树
74 |
75 | //转 Textrue
76 | let layoutElement = LayoutElement().createRenderer(doc: document)
77 | _ = HTMLToTexture(nodeName:"Flexbox").converter(layoutElement);
78 | nativeCodeLb.string = ""
79 | }
80 |
81 | override func viewDidLoad() {
82 | super.viewDidLoad()
83 | self.nativeCodeLb.font = NSFont.userFont(ofSize: 16)
84 |
85 | var caseStr = "31 + 8 / 2 * 3"
86 |
87 | caseStr = "31 + (4 + 5 - (3 + 3)) * 4 - (1 + (51 - 4))"
88 | caseStr = "4 + 3 * 2"
89 | //caseStr = "4 + 3 - 2"
90 | caseStr = "4 + - 3 * 2"
91 | caseStr = """
92 | @interface OurClass
93 | // 定义属性
94 | @property (nonatomic, assign) NSUInteger pa;
95 | @property (nonatomic, assign) CGFloat pb;
96 |
97 | @end
98 |
99 | @implementation OurClass
100 |
101 | /* 开始运算 */
102 | - (void)run {
103 | CGFloat ta = 4.3;
104 | pb = 13.3;
105 | pa = (3 + 2) + pb * 2 - ta;
106 | }
107 |
108 | @end
109 | """
110 |
111 | let interperter = OCInterpreter(caseStr)
112 |
113 | print(interperter)
114 |
115 | // VueTest().LetTestBegin()
116 |
117 | // let jsonStringClear = justTest().replacingOccurrences(of: "\n", with: "")
118 | // let jsonData = jsonStringClear.data(using: .utf8)!
119 | //
120 | // do {
121 | // let a = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions(rawValue: 0)) as! [Dictionary]
122 | // for c in a {
123 | // print(c["type"] ?? "")
124 | // }
125 | // } catch let error as NSError { print(error) }
126 |
127 | }
128 |
129 | }
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/Example/HTNSwift/VueTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VueTest.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2018/5/10.
6 | // Copyright © 2018年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import HTN
11 |
12 | class VueTest {
13 | var isPrintable = true
14 | let _case_tmp = """
15 | const PI = 3.141593
16 | PI > 3.0
17 | """
18 | let _case_1 = """
19 | const tokens = [45,34.5,"this","is","case1 content is const tokens = [\\"bulabula\\"]"];
20 | if (/[0-9]/.test(currentChar)) {
21 | var num = 1244.7 % 889;
22 | }
23 | """
24 | let _case_1_hash = """
25 | const|const:tokens|name:=|eq:[|braceL:45|float:,|comma:34.5|float:,|comma:this|string:,|comma:is|string:,|comma:case1 content is const tokens = [slash"bulabulaslash"]|string:]|braceR:|eof:if|if:(|parenL:/[0-9]/|regular:.|dot:test|name:(|parenL:currentChar|name:)|parenR:)|parenR:{|braceL:|eof:var|var:num|name:=|eq:1244.7|float:%|modulo:889|float:|eof:}|braceR:
26 | """
27 |
28 | func LetTestBegin() {
29 | checkCase_1()
30 | checkCase_2()
31 |
32 | }
33 |
34 | // 检查 Case
35 | // Case2
36 | func checkCase_2() {
37 | let re = JParser(_case_tmp).parser()
38 |
39 | }
40 | // Case1 包含了字符串,正则,数字还有基本的 token 的测试
41 | func checkCase_1() {
42 | let tks = JTokenizer(_case_1).tokenizer()
43 | if isPrintable {
44 | print("Case1 String is:\(_case_1)")
45 | }
46 | let hash = hashFrom(tokens: tks)
47 | caseResultPrint(hash: hash, rightHash: _case_1_hash, caseStr: "case1")
48 | }
49 |
50 | // 打印的方法
51 | func caseResultPrint(hash: String, rightHash: String, caseStr: String) {
52 | if hash == rightHash {
53 | print("\(caseStr) ✅")
54 | } else {
55 | print("\(caseStr) ❌")
56 | }
57 | }
58 | // 打印 token 并返回 hash 的 token
59 | func hashFrom(tokens:[JToken]) -> String {
60 | var hash = ""
61 | for tk in tokens {
62 | if isPrintable {
63 | print("\(tk.value) ::: \(tk.type.rawValue)")
64 | }
65 | hash.append("\(tk.value)|\(tk.type.rawValue):")
66 | }
67 | let reHash = hash.replacingOccurrences(of: "\\", with: "slash")
68 | if isPrintable {
69 | print(reHash)
70 | }
71 | return reHash
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :osx, '10.11'
3 |
4 | target 'HTNSwift' do
5 | use_frameworks!
6 | pod 'HTN', :path => '../'
7 | end
8 |
--------------------------------------------------------------------------------
/HTN.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint HTN.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'HTN'
11 | s.version = '0.1.0'
12 | s.summary = 'HTML to Native like swift and objective-c'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | HTML to Native like swift and objective-c.
22 | DESC
23 |
24 | s.homepage = 'https://github.com/ming1016/HTN'
25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
26 | s.license = { :type => 'MIT', :file => 'LICENSE' }
27 | s.author = { 'daiming' => 'daiming@didichuxing.com' }
28 | s.source = { :git => 'https://github.com/ming1016/HTN.git', :tag => s.version.to_s }
29 | # s.social_media_url = 'https://twitter.com/'
30 |
31 | s.osx.deployment_target = '10.10'
32 | s.requires_arc = true
33 | #s.swift_version = '4.0'
34 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0'}
35 |
36 | # s.resource_bundles = {
37 | # 'HTNSwift' => ['HTNSwift/Assets/*.png']
38 | # }
39 | # s.dependency 'Alamofire', '~> 4.7'
40 |
41 | # Core
42 | s.subspec 'Core' do |core|
43 | core.source_files = 'Sources/Core/**/*'
44 | end
45 |
46 | # H5Editor
47 | s.subspec 'H5Editor' do |h5Editor|
48 | h5Editor.source_files = 'Sources/H5Editor/**/*'
49 | h5Editor.dependency 'HTN/Core'
50 | end
51 |
52 | end
--------------------------------------------------------------------------------
/HTNCLI/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 |
--------------------------------------------------------------------------------
/HTNCLI/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "HTN",
6 | "repositoryURL": "https://github.com/ming1016/HTN",
7 | "state": {
8 | "branch": null,
9 | "revision": "c1a2cff12195c6273805f7c00c9c8ca704c670a9",
10 | "version": "0.1.0"
11 | }
12 | },
13 | {
14 | "package": "PathKit",
15 | "repositoryURL": "https://github.com/kylef/PathKit",
16 | "state": {
17 | "branch": null,
18 | "revision": "fa81fa9e3a9f59645159c4ea45c0c46ee6558f71",
19 | "version": "0.9.1"
20 | }
21 | },
22 | {
23 | "package": "Rainbow",
24 | "repositoryURL": "https://github.com/onevcat/Rainbow",
25 | "state": {
26 | "branch": null,
27 | "revision": "7737750b25a9cb4174c7567bef11d175715a824f",
28 | "version": "3.1.2"
29 | }
30 | },
31 | {
32 | "package": "Spectre",
33 | "repositoryURL": "https://github.com/kylef/Spectre.git",
34 | "state": {
35 | "branch": null,
36 | "revision": "e34d5687e1e9d865e3527dd58bc2f7464ef6d936",
37 | "version": "0.8.0"
38 | }
39 | },
40 | {
41 | "package": "SwiftCLI",
42 | "repositoryURL": "https://github.com/jakeheis/SwiftCLI",
43 | "state": {
44 | "branch": null,
45 | "revision": "88aa58faaea2ad78f62fbb427f8bafa93589c7f4",
46 | "version": "4.3.0"
47 | }
48 | }
49 | ]
50 | },
51 | "version": 1
52 | }
53 |
--------------------------------------------------------------------------------
/HTNCLI/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "HTNCLI",
8 | products: [
9 | .executable(name: "htn", targets: ["HTNCLI"])
10 | ],
11 | dependencies: [
12 | .package(url: "https://github.com/ming1016/HTN", from: "0.1.0"),
13 | // .package(url: "../../HTN", .branch("master")),
14 | .package(url: "https://github.com/jakeheis/SwiftCLI", from: "4.2.0"),
15 | .package(url: "https://github.com/onevcat/Rainbow", from: "3.1.1"),
16 | .package(url: "https://github.com/kylef/PathKit", from: "0.9.1")
17 | ],
18 | targets: [
19 | .target(name: "HTNCLI", dependencies: ["HTN", "SwiftCLI", "Rainbow", "PathKit"])
20 | ]
21 | )
22 |
--------------------------------------------------------------------------------
/HTNCLI/README.md:
--------------------------------------------------------------------------------
1 | # HTNCLI
2 |
3 | A description of this package.
4 |
--------------------------------------------------------------------------------
/HTNCLI/Sources/HTNCLI/Build.swift:
--------------------------------------------------------------------------------
1 | import SwiftCLI
2 |
3 | class BuildCommand: Command {
4 |
5 | let name = "build"
6 | let shortDescription = "Build H5Editor Url to Native file."
7 | let type = OptionalParameter()
8 | let urlFlag = Key("-u", "--urls")
9 | let outputFlag = Key("-o", "--output")
10 |
11 | public func execute() throws {
12 | let buildType = type.value ?? "h5editor" //默认是H5Editor的json类型
13 | let urls = (urlFlag.value ?? "").split(separator: ",").map(String.init).filter{ $0.count > 0}
14 | let outputPath = outputFlag.value ?? "."
15 |
16 | let htn = HTNCLI()
17 | if buildType == "h5editor" {
18 | //convert h5editor
19 | htn.buildH5Editor(urls: urls, outputPath: outputPath)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/HTNCLI/Sources/HTNCLI/HTNCLI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTNCLI.swift
3 | // HTNCLI
4 | //
5 | // Created by 陈爱彬 on 2018/4/17. Maintain by 陈爱彬
6 | // Description
7 | //
8 |
9 | import Foundation
10 | import HTN
11 | import PathKit
12 | import Dispatch
13 |
14 | public class HTNCLI {
15 | public static let version = "0.1.0"
16 |
17 | let sema = DispatchSemaphore(value: 0)
18 |
19 | public init() {}
20 |
21 | //build h5editor urls and export to outputPath
22 | public func buildH5Editor(urls: [String], outputPath: String) {
23 | let path = Path(outputPath)
24 | for url in urls {
25 | print("Request url:\(url)")
26 | SMNetWorking().requestJSON(url) { (jsonModel) in
27 | guard let model = jsonModel else {
28 | self.sema.signal()
29 | return
30 | }
31 | let converter = H5EditorToFrame(H5EditorObjc())
32 | let reStr = converter.convert(model)
33 | // print(reStr)
34 | // print(converter.m.pageId)
35 | let hPath = path + Path(converter.m.pageId + ".h")
36 | let mPath = path + Path(converter.m.pageId + ".m")
37 | self.writeFileToPath(hPath, content: reStr.0)
38 | self.writeFileToPath(mPath, content: reStr.1)
39 | self.sema.signal()
40 | }
41 | sema.wait()
42 | }
43 | }
44 | //write response to path
45 | func writeFileToPath(_ path: Path, content: String) {
46 | try? path.write(content)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/HTNCLI/Sources/HTNCLI/main.swift:
--------------------------------------------------------------------------------
1 | import SwiftCLI
2 | import Rainbow
3 |
4 | Rainbow.enabled = Term.isTTY
5 |
6 | let _verbose = Flag("-v", "--verbose")
7 | extension Command {
8 | var verbose: Flag {
9 | return _verbose
10 | }
11 |
12 | func verboseLog(_ content: String) {
13 | if verbose.value {
14 | stdout <<< content
15 | }
16 | }
17 | }
18 |
19 | let htn = CLI(name: "htn", version: HTNCLI.version, description: "HTN toolkit")
20 | htn.commands = [
21 | BuildCommand()
22 | ]
23 | htn.globalOptions = [_verbose]
24 | htn.goAndExit()
25 |
--------------------------------------------------------------------------------
/HTNCLI/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | swift package clean
3 | swift build -c release
4 | cp .build/release/htn /usr/local/bin/htn
5 |
--------------------------------------------------------------------------------
/HTNExamples/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | # Carthage/Checkouts
54 |
55 | Carthage/Build
56 |
57 | # fastlane
58 | #
59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60 | # screenshots whenever they are needed.
61 | # For more information about the recommended setup visit:
62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
63 |
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 |
--------------------------------------------------------------------------------
/HTNExamples/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :ios, '8.0'
3 | target 'Sample' do
4 | pod 'Texture' , '2.5.1'
5 | pod 'ReactiveCocoa', '2.3.1'
6 | end
7 |
8 |
--------------------------------------------------------------------------------
/HTNExamples/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - PINCache (3.0.1-beta.5):
3 | - PINCache/Arc-exception-safe (= 3.0.1-beta.5)
4 | - PINCache/Core (= 3.0.1-beta.5)
5 | - PINCache/Arc-exception-safe (3.0.1-beta.5):
6 | - PINCache/Core
7 | - PINCache/Core (3.0.1-beta.5):
8 | - PINOperation (= 1.0.3)
9 | - PINOperation (1.0.3)
10 | - PINRemoteImage/Core (3.0.0-beta.12):
11 | - PINOperation
12 | - PINRemoteImage/iOS (3.0.0-beta.12):
13 | - PINRemoteImage/Core
14 | - PINRemoteImage/PINCache (3.0.0-beta.12):
15 | - PINCache (= 3.0.1-beta.5)
16 | - PINRemoteImage/Core
17 | - ReactiveCocoa (2.3.1):
18 | - ReactiveCocoa/UI (= 2.3.1)
19 | - ReactiveCocoa/Core (2.3.1):
20 | - ReactiveCocoa/no-arc
21 | - ReactiveCocoa/no-arc (2.3.1)
22 | - ReactiveCocoa/UI (2.3.1):
23 | - ReactiveCocoa/Core
24 | - Texture (2.5.1):
25 | - Texture/PINRemoteImage (= 2.5.1)
26 | - Texture/Core (2.5.1)
27 | - Texture/PINRemoteImage (2.5.1):
28 | - PINRemoteImage/iOS (= 3.0.0-beta.12)
29 | - PINRemoteImage/PINCache
30 | - Texture/Core
31 |
32 | DEPENDENCIES:
33 | - ReactiveCocoa (= 2.3.1)
34 | - Texture (= 2.5.1)
35 |
36 | SPEC CHECKSUMS:
37 | PINCache: 98e7b1ef782824ad231ade51987c218b758c30d8
38 | PINOperation: ac23db44796d4a564ecf9b5ea7163510f579b71d
39 | PINRemoteImage: 0cefe720c2612960bd360710efbd6c73a1991d5f
40 | ReactiveCocoa: 3fe9b233ca6db810e86681b5749488564c1179a1
41 | Texture: c3f06e344aa091667b7a56002e525cde674d021a
42 |
43 | PODFILE CHECKSUM: 8b2a75f66332744932242f3409be56dee897cfa7
44 |
45 | COCOAPODS: 1.3.1
46 |
--------------------------------------------------------------------------------
/HTNExamples/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/HTNExamples/Sample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // Sample
4 | //
5 | // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
6 | // This source code is licensed under the BSD-style license found in the
7 | // LICENSE file in the root directory of this source tree. An additional grant
8 | // of patent rights can be found in the PATENTS file in the same directory.
9 | //
10 |
11 | #import
12 |
13 | @interface AppDelegate : UIResponder
14 | @property (strong, nonatomic) UIWindow *window;
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // Sample
4 | //
5 | // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
6 | // This source code is licensed under the BSD-style license found in the
7 | // LICENSE file in the root directory of this source tree. An additional grant
8 | // of patent rights can be found in the PATENTS file in the same directory.
9 | //
10 |
11 | #import "AppDelegate.h"
12 | #import "MainViewController.h"
13 |
14 | @implementation AppDelegate
15 |
16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
17 |
18 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
19 | self.window.backgroundColor = [UIColor whiteColor];
20 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[MainViewController new]];
21 | [self.window makeKeyAndVisible];
22 |
23 | // [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:47/255.0 green:184/255.0 blue:253/255.0 alpha:1.0]];
24 | // [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
25 | // [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}];;
26 |
27 | return YES;
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/Flexbox.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface Flexbox : ASDisplayNode
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Sample
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 |
32 | UILaunchStoryboardName
33 | LaunchScreen
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/MainViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.h
3 | // Sample
4 | //
5 | // Created by sunshinelww on 2017/11/1.
6 | // Copyright © 2017年 AsyncDisplayKit. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MainViewController : ASViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/MainViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.m
3 | // Sample
4 | //
5 | // Created by sunshinelww on 2017/11/1.
6 | // Copyright © 2017年 AsyncDisplayKit. All rights reserved.
7 | //
8 |
9 | #import "MainViewController.h"
10 | #import "Flexbox.h"
11 |
12 | @interface MainViewController ()
13 |
14 | @property (strong, nonatomic)Flexbox *flexBoxNode;
15 |
16 | @end
17 |
18 | @implementation MainViewController
19 |
20 | - (instancetype)init{
21 | ASDisplayNode *node = [ASDisplayNode new];
22 | _flexBoxNode = [[Flexbox alloc] init];
23 | __weak typeof(self) weakSelf = self;
24 | node.automaticallyManagesSubnodes = YES;
25 | node.layoutSpecBlock = ^ASLayoutSpec * _Nonnull(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
26 | return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY sizingOptions:ASCenterLayoutSpecSizingOptionMinimumX child:weakSelf.flexBoxNode];
27 | };
28 | self=[super initWithNode:node];
29 | return self;
30 | }
31 |
32 | - (void)viewDidLoad {
33 | [super viewDidLoad];
34 | // Do any additional setup after loading the view.
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/UIColor+Extension.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Extension.h
3 | // HTNSample
4 | //
5 | // Created by sunshinelww on 2017/4/6.
6 | // Copyright © 2017年 sunshinelww. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIColor (Extension)
12 |
13 | +(UIColor *)colorWithHexString:(NSString *)hexString;
14 |
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/UIColor+Extension.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Extension.m
3 | // HTNSample
4 | //
5 | // Created by sunshinelww on 2017/4/6.
6 | // Copyright © 2017年 sunshinelww. All rights reserved.
7 | //
8 |
9 | #import "UIColor+Extension.h"
10 |
11 | @implementation UIColor (Extension)
12 |
13 | + (UIColor *)colorWithHexString:(NSString *)colorHexString {
14 | CGFloat alpha, red, blue, green;
15 |
16 | NSString *colorString = [[colorHexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString];
17 | switch ([colorString length]) {
18 | case 3: // #RGB
19 | alpha = 1.0f;
20 | red = colorComponentFromHexString(colorString, 0, 1);
21 | green = colorComponentFromHexString(colorString, 1, 1);
22 | blue = colorComponentFromHexString(colorString, 2, 1);
23 | break;
24 |
25 | case 6: // #RRGGBB
26 | alpha = 1.0f;
27 | red = colorComponentFromHexString(colorString, 0, 2);
28 | green = colorComponentFromHexString(colorString, 2, 2);
29 | blue = colorComponentFromHexString(colorString, 4, 2);
30 | break;
31 |
32 | default:
33 | return nil;
34 | }
35 | return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
36 | }
37 |
38 | CGFloat colorComponentFromHexString(NSString *string, NSUInteger start, NSUInteger length) {
39 | NSString *substring = [string substringWithRange:NSMakeRange(start, length)];
40 | NSString *hexString = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring];
41 |
42 | unsigned hexNum;
43 | NSScanner *scanner=[NSScanner scannerWithString:hexString];
44 | [scanner scanHexInt: &hexNum];
45 | return hexNum / 255.0;
46 | }
47 | @end
48 |
--------------------------------------------------------------------------------
/HTNExamples/Sample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // Sample
4 | //
5 | // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
6 | // This source code is licensed under the BSD-style license found in the
7 | // LICENSE file in the root directory of this source tree. An additional grant
8 | // of patent rights can be found in the PATENTS file in the same directory.
9 | //
10 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
13 | // FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
14 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | //
17 |
18 | #import
19 | #import "AppDelegate.h"
20 |
21 | int main(int argc, char * argv[]) {
22 | @autoreleasepool {
23 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "HTN",
8 | products: [
9 | .library(name: "HTN",targets: [ "HTN" ]),
10 | ],
11 | dependencies: [],
12 | targets: [
13 | .target(name: "HTN", dependencies: [], path: "Sources"),
14 | ]
15 | )
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTN
2 |
3 | 
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Sources/Core/CSS/CSSDeclaration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSDeclaration.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/15.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CSSDeclaration {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Core/CSS/CSSParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSParser.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/15.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CSSParser {
12 |
13 | public var styleSheet: CSSStyleSheet
14 |
15 | private var _input: String
16 | private var _index: String.Index
17 | private var _bufferStr: String
18 | private var _currentSelector: CSSSelector
19 | private var _currentProperty: CSSProperty
20 | private var _currentRule: CSSRule
21 |
22 | public init(_ input: String) {
23 | self.styleSheet = CSSStyleSheet()
24 | //过滤注释
25 | var newStr = ""
26 | let annotationBlockPattern = "/\\*[\\s\\S]*?\\*/" //匹配/*...*/这样的注释
27 | let regexBlock = try! NSRegularExpression(pattern: annotationBlockPattern, options: NSRegularExpression.Options(rawValue:0))
28 | newStr = regexBlock.stringByReplacingMatches(in: input, options: NSRegularExpression.MatchingOptions(rawValue:0), range: NSMakeRange(0, input.count), withTemplate: "")
29 | //默认样式
30 | //TODO: 考虑后面需要考虑样式属性需要支持优先级,这里的处理还需要考虑记录更多信息支持后面根据类型判断优先级
31 | let defaultCSSString = CSSDefault.htnCSSDefault()
32 | // let defaultCSSString = ""
33 | newStr = defaultCSSString + newStr
34 | _input = newStr
35 |
36 | //初始化
37 | _index = _input.startIndex
38 | _bufferStr = ""
39 | _currentSelector = CSSSelector()
40 | _currentProperty = CSSProperty()
41 | _currentRule = CSSRule()
42 | }
43 |
44 | //解析 CSS 样式表
45 | public func parseSheet() -> CSSStyleSheet {
46 |
47 | let stateMachine = HTNStateMachine(S.UnknownState)
48 |
49 | stateMachine.listen(E.CommaEvent, transit: [S.UnknownState, S.SelectorState], to: S.SelectorState) { (t) in
50 | self._currentSelector.path = self._bufferStr
51 | self.addSelector()
52 | self.advanceIndexAndResetCurrentStr()
53 | }
54 | stateMachine.listen(E.BraceLeftEvent, transit: [S.UnknownState, S.SelectorState], to: S.PropertyKeyState) { (t) in
55 | self._currentSelector.path = self._bufferStr
56 | self.addSelector()
57 | self.advanceIndexAndResetCurrentStr()
58 | }
59 | stateMachine.listen(E.ColonEvent, transit: S.PropertyKeyState, to: S.PropertyValueState) { (t) in
60 | self._currentProperty.key = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).lowercased()
61 | self.advanceIndexAndResetCurrentStr()
62 | }
63 | stateMachine.listen(E.SemicolonEvent, transit: S.PropertyValueState, to: S.PropertyKeyState) { (t) in
64 | self._currentProperty.value = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).lowercased()
65 | self.addProperty()
66 | self.advanceIndexAndResetCurrentStr()
67 | }
68 | //加上 PropertyValueState 的原因是需要支持 property 最后一个不需要写 ; 的情况
69 | stateMachine.listen(E.BraceRightEvent, transit: [S.PropertyKeyState,S.PropertyValueState], to: S.UnknownState) { (t) in
70 | if self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).count > 0 {
71 | self._currentProperty.value = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).lowercased()
72 | self.addProperty()
73 | }
74 | self.addRule()
75 | self.advanceIndexAndResetCurrentStr()
76 | }
77 |
78 | while let aChar = currentChar {
79 | let aStr = aChar.description
80 | var hasAStrTrigger = false
81 | //deal with aStr
82 | if aStr == "," { hasAStrTrigger = stateMachine.trigger(E.CommaEvent) }
83 | if aStr == "." { hasAStrTrigger = stateMachine.trigger(E.DotEvent) }
84 | if aStr == "#" { hasAStrTrigger = stateMachine.trigger(E.HashTagEvent) }
85 | if aStr == "{" { hasAStrTrigger = stateMachine.trigger(E.BraceLeftEvent) }
86 | if aStr == "}" { hasAStrTrigger = stateMachine.trigger(E.BraceRightEvent) }
87 | if aStr == ":" { hasAStrTrigger = stateMachine.trigger(E.ColonEvent) }
88 | if aStr == ";" { hasAStrTrigger = stateMachine.trigger(E.SemicolonEvent) }
89 |
90 | if !hasAStrTrigger {
91 | addBufferStr(aStr)
92 | advanceIndex()
93 | }
94 | }
95 |
96 | return self.styleSheet
97 | }
98 |
99 | //parser tool
100 | var currentChar: Character? {
101 | return _index < _input.endIndex ? _input[_index] : nil
102 | }
103 | func addBufferStr(_ bufferStr: String) {
104 | _bufferStr += bufferStr
105 | }
106 | func advanceIndex() {
107 | _input.formIndex(after: &_index)
108 | }
109 | func advanceIndexAndResetCurrentStr() {
110 | _bufferStr = ""
111 | advanceIndex()
112 | }
113 | //add
114 | func addSelector() {
115 | let selector = _currentSelector
116 | _currentRule.selectorList.append(selector)
117 | _currentSelector = CSSSelector()
118 | }
119 | func addProperty() {
120 | let property = _currentProperty
121 | _currentRule.propertyList.append(property)
122 | _currentRule.propertyMap[property.key] = property.value
123 | _currentProperty = CSSProperty()
124 | }
125 | func addRule() {
126 | let rule = _currentRule
127 | styleSheet.ruleList.append(rule)
128 | _currentRule = CSSRule()
129 | }
130 |
131 | enum S: HTNStateType {
132 | case UnknownState //
133 | case SelectorState // 比如 div p, #id
134 | case PropertyKeyState // 属性的 key
135 | case PropertyValueState // 属性的 value
136 |
137 | //TODO:以下后期支持,优先级2
138 | case PseudoClass // :nth-child(2)
139 | case PseudoElement // ::first-line
140 |
141 | //TODO:以下后期支持,优先级3
142 | case PagePseudoClass
143 | case AttributeExact // E[attr]
144 | case AttributeSet // E[attr|="value"]
145 | case AttributeHyphen // E[attr~="value"]
146 | case AttributeList // E[attr*="value"]
147 | case AttributeContain // E[attr^="value"]
148 | case AttributeBegin // E[attr$="value"]
149 | case AttributeEnd
150 | //TODO:@media 这类 @规则 ,后期支持,优先级4
151 | }
152 | enum E: HTNEventType {
153 | case SpaceEvent //空格
154 | case CommaEvent // ,
155 | case DotEvent // .
156 | case HashTagEvent // #
157 | case BraceLeftEvent // {
158 | case BraceRightEvent // }
159 | case ColonEvent // :
160 | case SemicolonEvent // ;
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/Sources/Core/CSS/CSSProperty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSProperty.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/15.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CSSProperty {
12 | public var key = ""
13 | public var value = ""
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/Core/CSS/CSSRule.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSRule.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/15.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CSSRule {
12 | public var selectorList = [CSSSelector]()
13 | public var propertyList = [CSSProperty]()
14 | public var propertyMap = [String:String]()
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Core/CSS/CSSSelector.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSSelector.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/15.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CSSSelector {
12 | public var path : String {
13 | get {
14 | return String()
15 | }
16 | set(newPath) {
17 | self.identifier = newPath.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
18 | self.matchList = self.identifier.components(separatedBy: " ")
19 | }
20 | }
21 | public var matchList = [String]()
22 | public var identifier = ""
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Core/CSS/CSSStyleSheet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSStyleSheet.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/15.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CSSStyleSheet {
12 | public var ruleList = [CSSRule]()
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Core/HTML/Attribute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Attribute.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class Attribute {
12 | public var name: String
13 | public var value: String
14 | init() {
15 | self.name = ""
16 | self.value = ""
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Core/HTML/Document.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Document.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class Document : Element {
12 | public var styleList = [Element]()
13 | public var scriptList = [Element]()
14 |
15 | public func allStyle() -> String {
16 | var allStyle = ""
17 | for style in styleList {
18 | guard let str = style.charToken?.data else {
19 | continue
20 | }
21 | allStyle += str
22 | }
23 | return allStyle
24 | }
25 |
26 | public func allScript() -> String {
27 | var allScript = ""
28 | for script in scriptList {
29 | guard let str = script.charToken?.data else {
30 | continue
31 | }
32 | allScript += str
33 | }
34 | return allScript
35 | }
36 |
37 | //打印 Dom 树
38 | public func des() {
39 | for elm in self.children {
40 | desElement(elm: elm as! Element, level: 0)
41 | }
42 | }
43 |
44 | private func desElement(elm:Element, level:Int) {
45 | var propertyStr = ""
46 | var attributeStr = ""
47 | if elm.startTagToken != nil {
48 | if elm.startTagToken!.attributeList.count > 0 {
49 | attributeStr = "[ATTR]:"
50 | for attr in (elm.startTagToken?.attributeList)! {
51 | attributeStr += " \(attr.name):\(attr.value)"
52 | }
53 | }
54 | }
55 | if elm.propertyMap.count > 0 {
56 | propertyStr = "[CSS]:"
57 | for property in elm.propertyMap {
58 | propertyStr += " \(property.key):\(property.value)"
59 | }
60 | }
61 | var frontStr = "";
62 | for _ in 0...level {
63 | if level > 0 {
64 | frontStr += " "
65 | }
66 | }
67 | print("\(frontStr)[\(elm.startTagToken?.data.uppercased() ?? "CHAR")] \(attributeStr) \(propertyStr)")
68 | if elm.children.count > 0 {
69 | for child in elm.children {
70 | self.desElement(elm: child as! Element, level: level + 1)
71 | }
72 | }
73 | }
74 |
75 | }
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Sources/Core/HTML/Element.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Element.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class Element : Node {
12 | public var startTagToken: HTMLToken?
13 | public var charToken: HTMLToken?
14 | public var propertyMap = [String:String]() //属性映射表
15 | public var renderer:RenderObject? //代表元素的渲染对象
16 |
17 | override init() {
18 |
19 | }
20 |
21 | init(token: HTMLToken) {
22 |
23 | if token.type == .StartTag {
24 | self.startTagToken = token
25 | }
26 | if token.type == .Char {
27 | self.charToken = token
28 | }
29 | }
30 |
31 | public func createRenderObject (){
32 | renderer=RenderObject();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/Core/HTML/HTMLToken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTMLToken.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class HTMLToken {
12 | public var type: tokenType
13 | public var data: String
14 | public var selfClosing: Bool
15 | public var attributeList: [Attribute]
16 | public var attributeDic: [String:String]
17 | public var currentAttribute: Attribute
18 |
19 | init() {
20 | self.type = tokenType.DocType
21 | self.data = ""
22 | self.selfClosing = false
23 | self.attributeList = [Attribute]()
24 | self.attributeDic = [String:String]()
25 | self.currentAttribute = Attribute()
26 | }
27 |
28 | public enum tokenType {
29 | case DocType
30 | case StartTag
31 | case EndTag
32 | case Comment
33 | case Char
34 | case EOF
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Core/HTML/HTMLTokenizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTMLTokenizer.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class HTMLTokenizer {
12 | private let _input: String
13 | private var _index: String.Index
14 | private var _bufferStr: String
15 | private var _bufferToken: HTMLToken
16 | private var _tks: [HTMLToken]
17 | //TODO: add doctypeData for DOCTYPE
18 | public init(_ input: String) {
19 | _input = input
20 | _index = input.startIndex
21 | _bufferStr = ""
22 | _bufferToken = HTMLToken()
23 | _tks = [HTMLToken]()
24 | }
25 | public func parse() -> [HTMLToken]{
26 | //初始化状态机
27 | let stateMachine = HTNStateMachine(S.DataState)
28 |
29 | //状态机监听 state
30 | //不同事件在来源是集合状态的转成统一状态的处理
31 | //TODO:根据 w3c 标准 https://dev.w3.org/html5/spec-preview/tokenization.html 来完善状态机,添加更多状态的处理
32 | let anglebracketRightEventFromStatesArray = [S.DOCTYPEState,
33 | S.CommentEndState,
34 | S.TagOpenState,
35 | S.EndTagOpenState,
36 | S.AfterAttributeValueQuotedState,
37 | S.BeforeDOCTYPENameState,
38 | S.AfterDOCTYPEPublicIdentifierState]
39 | stateMachine.listen(E.AngleBracketRight, transit: anglebracketRightEventFromStatesArray, to: S.DataState) { (t) in
40 | if t.fromState == S.TagOpenState || t.fromState == S.EndTagOpenState {
41 | if self._bufferStr.count > 0 {
42 | self._bufferToken.data = self._bufferStr.lowercased()
43 | }
44 | }
45 | self.addHTMLToken()
46 | self.advanceIndexAndResetCurrentStr()
47 | }
48 |
49 | //DataState
50 | stateMachine.listen(E.AngleBracketLeft, transit: S.DataState, to: S.TagOpenState) { (t) in
51 | self._bufferStr = self._bufferStr.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
52 | if self._bufferStr.count > 0 {
53 | self._bufferToken.type = .Char
54 | self._bufferToken.data = self._bufferStr
55 | self.addHTMLToken()
56 | }
57 | self._bufferToken.type = .StartTag
58 | self.advanceIndexAndResetCurrentStr()
59 | }
60 | stateMachine.listen(E.And, transit: S.DataState, to: S.CharacterReferenceInDataState) { (t) in
61 | self.advanceIndexAndResetCurrentStr()
62 | }
63 | //TagOpenState
64 | stateMachine.listen(E.Exclamation, transit: S.TagOpenState, to: S.MarkupDeclarationOpenState) { (t) in
65 | self.advanceIndexAndResetCurrentStr()
66 | }
67 | //TODO:补上这种需要判断开始时字母,或者特定首字母集进入的状态,比如 TagNameState 和 BeforeAttributeNameState,但需要权衡下,如果不加这个状态会少不少检查
68 | stateMachine.listen(E.Space, transit: S.TagOpenState, to: S.AttributeNameState) { (t) in
69 | self._bufferToken.data = self._bufferStr.lowercased()
70 | self.advanceIndexAndResetCurrentStr()
71 | }
72 | stateMachine.listen(E.Slash, transit: S.TagOpenState, to: S.EndTagOpenState) { (t) in
73 | if self._bufferStr.count == 0 {
74 | self._bufferToken.type = .EndTag
75 | } else {
76 | self._bufferToken.data = self._bufferStr.lowercased()
77 | self._bufferToken.selfClosing = true
78 | }
79 | self.advanceIndexAndResetCurrentStr()
80 | }
81 | //AttributeNameState
82 | stateMachine.listen(E.Equal, transit: [S.AttributeNameState,S.AfterAttributeValueQuotedState], to: S.BeforeAttributeValueState) { (t) in
83 | self._bufferToken.currentAttribute.name = self._bufferStr.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
84 | self.advanceIndexAndResetCurrentStr()
85 | }
86 | //BeforeAttributeValueState
87 | stateMachine.listen(E.Quotation, transit: S.BeforeAttributeValueState, to: S.AttributeValueDoubleQuotedState) { (t) in
88 | self.advanceIndexAndResetCurrentStr()
89 | }
90 | //AttributeValueDoubleQuotedState
91 | stateMachine.listen(E.Quotation, transit: S.AttributeValueDoubleQuotedState, to: S.AfterAttributeValueQuotedState) { (t) in
92 | self._bufferToken.currentAttribute.value = self._bufferStr
93 | self._bufferToken.attributeList.append(self._bufferToken.currentAttribute)
94 | self._bufferToken.attributeDic[self._bufferToken.currentAttribute.name] = self._bufferToken.currentAttribute.value
95 | self._bufferToken.currentAttribute = Attribute()
96 | self.advanceIndexAndResetCurrentStr()
97 | }
98 | //AfterAttributeValueQuotedState
99 | stateMachine.listen(E.Slash, transit: S.AfterAttributeValueQuotedState, to: S.EndTagOpenState) { (t) in
100 | self._bufferToken.selfClosing = true
101 | self.advanceIndexAndResetCurrentStr()
102 | }
103 | stateMachine.listen(E.InStyleOrScript, transit: S.AfterAttributeValueQuotedState, to: S.PLAINTEXTState) { (t) in
104 | self.addHTMLToken()
105 | self.advanceIndexAndResetCurrentStr()
106 | }
107 | //MarkupDeclarationOpenState
108 | stateMachine.listen(E.DocType, transit: S.MarkupDeclarationOpenState, to: S.DOCTYPEState) { (t) in
109 | self._bufferToken.type = .DocType
110 | self.advanceIndexAndResetCurrentStr()
111 | }
112 | stateMachine.listen(E.DoubleMinus, transit: S.MarkupDeclarationOpenState, to: S.CommentStartState) { (t) in
113 | self._bufferToken.type = .Comment
114 | self.advanceIndexAndResetCurrentStr()
115 | }
116 | //CommentStartState
117 | stateMachine.listen(E.Minus, transit: S.CommentStartState, to: S.CommentEndDashState) { (t) in
118 | self._bufferToken.data = self._bufferStr
119 | self.advanceIndexAndResetCurrentStr()
120 | }
121 | //CommentEndDashState
122 | stateMachine.listen(E.Minus, transit: S.CommentEndDashState, to: S.CommentEndState) { (t) in
123 | self.advanceIndexAndResetCurrentStr()
124 | }
125 | //DOCTYPEState
126 | stateMachine.listen(E.Space, transit: S.DOCTYPEState, to: S.BeforeDOCTYPENameState) { (t) in
127 | self.advanceIndexAndResetCurrentStr()
128 | }
129 | //BeforeDOCTYPENameState
130 | stateMachine.listen(E.Space, transit: S.BeforeDOCTYPENameState, to: S.AfterDOCTYPENameState) { (t) in
131 | self._bufferToken.data = self._bufferStr
132 | self.advanceIndexAndResetCurrentStr()
133 | }
134 | //AfterDOCTYPENameState
135 | stateMachine.listen(E.Public, transit: S.AfterDOCTYPENameState, to: S.AfterDOCTYPEPublicKeywordState) { (t) in
136 | self.advanceIndexAndResetCurrentStr()
137 | }
138 | //AfterDOCTYPEPublicKeywordState
139 | stateMachine.listen(E.Space, transit: S.AfterDOCTYPEPublicKeywordState, to: S.BeforeDOCTYPEPublicIdentifierState) { (t) in
140 | self.advanceIndexAndResetCurrentStr()
141 | }
142 | //BeforeDOCTYPEPublicIdentifierState
143 | //暂时仅仅记录 DoubleQuoted 状态,不区分单引号和双引号
144 | stateMachine.listen(E.Quotation, transit: S.BeforeDOCTYPEPublicIdentifierState, to: S.DOCTYPEPublicIdentifierDoubleQuotedState) { (t) in
145 | self.advanceIndexAndResetCurrentStr()
146 | }
147 | //DOCTYPEPublicIdentifierDoubleQuotedState
148 | stateMachine.listen(E.Quotation, transit: S.DOCTYPEPublicIdentifierDoubleQuotedState, to: S.AfterDOCTYPEPublicIdentifierState) { (t) in
149 | //TODO:记录 doctypeData
150 | self.advanceIndexAndResetCurrentStr()
151 | }
152 | //PLAINTEXTState
153 | stateMachine.listen(E.AngleBracketLeft, transit: S.PLAINTEXTState, to: S.TagOpenState) { (t) in
154 | if self._bufferStr.count > 0 {
155 | self._bufferToken.type = .Char
156 | self._bufferToken.data = self._bufferStr
157 | self.addHTMLToken()
158 | }
159 | self._bufferToken.type = .StartTag
160 | self.advanceIndexAndResetCurrentStr()
161 | }
162 |
163 | while let aChar = currentChar {
164 |
165 | let aStr = aChar.description
166 | var hasAStrTrigger = false
167 | var hasBufferStrTrigger = false
168 | //deal with aStr
169 | if aStr == "!" {
170 | hasAStrTrigger = stateMachine.trigger(E.Exclamation)
171 | }
172 | if aStr == "<" {
173 | hasAStrTrigger = stateMachine.trigger(E.AngleBracketLeft)
174 | }
175 | if aStr == ">" {
176 | if _bufferToken.type == .StartTag && (_bufferToken.data.lowercased() == "style" || _bufferToken.data.lowercased() == "script") {
177 | hasAStrTrigger = stateMachine.trigger(E.InStyleOrScript)
178 | } else {
179 | hasAStrTrigger = stateMachine.trigger(E.AngleBracketRight)
180 | }
181 |
182 | }
183 | if aStr == " " {
184 | hasAStrTrigger = stateMachine.trigger(E.Space)
185 | }
186 | if self.isQuotation(s: aStr) {
187 | hasAStrTrigger = stateMachine.trigger(E.Quotation)
188 | }
189 | if aStr == "-" {
190 | hasAStrTrigger = stateMachine.trigger(E.Minus)
191 | }
192 | if aStr == "=" {
193 | hasAStrTrigger = stateMachine.trigger(E.Equal)
194 | }
195 | if aStr == "/" {
196 | hasAStrTrigger = stateMachine.trigger(E.Slash)
197 | }
198 |
199 | //deal with bufferStr
200 | if !hasAStrTrigger {
201 | addBufferStr(aStr)
202 | } else {
203 | continue
204 | }
205 |
206 | if _bufferStr.lowercased() == "doctype" {
207 | hasBufferStrTrigger = stateMachine.trigger(E.DocType)
208 | }
209 | if _bufferStr.lowercased() == "public" {
210 | hasBufferStrTrigger = stateMachine.trigger(E.Public)
211 | }
212 | if _bufferStr.lowercased() == "--" {
213 | hasBufferStrTrigger = stateMachine.trigger(E.DoubleMinus)
214 | }
215 | if !hasBufferStrTrigger {
216 | self.advanceIndex()
217 | }
218 | }
219 | return _tks
220 | }
221 |
222 | //tiny tool
223 | var currentChar: Character? {
224 | return _index < _input.endIndex ? _input[_index] : nil
225 | }
226 | func addBufferStr(_ bufferStr: String) {
227 | _bufferStr += bufferStr
228 | }
229 | func isQuotation(s:String) -> Bool {
230 | if s == "\"" || s == "'" {
231 | return true
232 | }
233 | return false
234 | }
235 | //添加 token
236 | func addHTMLToken() {
237 | let tk = _bufferToken
238 | _tks.append(tk)
239 | _bufferToken = HTMLToken()
240 | }
241 | func advanceIndex() {
242 | _input.formIndex(after: &_index)
243 | }
244 | func advanceIndexAndResetCurrentStr() {
245 | _bufferStr = ""
246 | advanceIndex()
247 | }
248 |
249 | //枚举
250 | enum S: HTNStateType {
251 | case DataState //half done
252 | case CharacterReferenceInDataState
253 | case RCDATAState
254 | case CharacterReferenceInRCDATAState
255 | case RAWTEXTState
256 | case ScriptDataState
257 | case PLAINTEXTState
258 | case TagOpenState //half done
259 | case EndTagOpenState
260 | case TagNameState //half done
261 |
262 | case RCDATALessThanSignState
263 | case RCDATAEndTagOpenState
264 | case RCDATAEndTagNameState
265 |
266 | case RAWTEXTLessThanSignState
267 | case RAWTEXTEndTagOpenState
268 | case RAWTEXTEndTagNameState
269 |
270 | //Script
271 | case ScriptDataLessThanSignState
272 | case ScriptDataEndTagOpenState
273 | case ScriptDataEndTagNameState
274 | case ScriptDataEscapeStartState
275 | case ScriptDataEscapeStartDashState
276 | case ScriptDataEscapedState
277 | case ScriptDataEscapedDashState
278 | case ScriptDataEscapedDashDashState
279 | case ScriptDataEscapedLessThanSignState
280 | case ScriptDataEscapedEndTagOpenState
281 | case ScriptDataEscapedEndTagNameState
282 | case ScriptDataDoubleEscapeStartState
283 | case ScriptDataDoubleEscapedState
284 | case ScriptDataDoubleEscapedDashState
285 | case ScriptDataDoubleEscapedDashDashState
286 | case ScriptDataDoubleEscapedLessThanSignState
287 | case ScriptDataDoubleEscapeEndState
288 |
289 | //Tag
290 | case BeforeAttributeNameState
291 | case AttributeNameState //half done
292 | case AfterAttributeNameState
293 | case BeforeAttributeValueState
294 | case AttributeValueDoubleQuotedState //half done
295 | case AttributeValueSingleQuotedState
296 | case AttributeValueUnquotedState
297 | case CharacterReferenceInAttributeValueState
298 | case AfterAttributeValueQuotedState //half done
299 | case SelfClosingStartTagState
300 | case BogusCommentState
301 | case ContinueBogusCommentState
302 | case MarkupDeclarationOpenState //half done
303 |
304 | //Comment
305 | case CommentStartState //half done
306 | case CommentStartDashState
307 | case CommentState
308 | case CommentEndDashState //half done
309 | case CommentEndState //half done
310 | case CommentEndBangState
311 |
312 | //DOCTYPE
313 | case DOCTYPEState //half done
314 | case BeforeDOCTYPENameState //half done
315 | case DOCTYPENameState
316 | case AfterDOCTYPENameState //half done
317 | case AfterDOCTYPEPublicKeywordState //half done
318 | case BeforeDOCTYPEPublicIdentifierState //half done
319 | case DOCTYPEPublicIdentifierDoubleQuotedState //half done
320 | case DOCTYPEPublicIdentifierSingleQuotedState
321 | case AfterDOCTYPEPublicIdentifierState //half done
322 | case BetweenDOCTYPEPublicAndSystemIdentifiersState
323 | case AfterDOCTYPESystemKeywordState
324 | case BeforeDOCTYPESystemIdentifierState
325 | case DOCTYPESystemIdentifierDoubleQuotedState
326 | case DOCTYPESystemIdentifierSingleQuotedState
327 | case AfterDOCTYPESystemIdentifierState
328 | case BogusDOCTYPEState
329 |
330 | case CDATASectionState
331 | case CDATASectionRightSquareBracketState
332 | case CDATASectionDoubleRightSquareBracketState
333 | }
334 | enum E: HTNEventType {
335 | case Advance
336 | case AngleBracketLeft // <
337 | case AngleBracketRight // >
338 | case And // &
339 | case Space // 空格
340 | case Exclamation // !
341 | case DocType // doctype
342 | case Public // public
343 | case Quotation // ' 或 "
344 | case DoubleMinus // --
345 | case Minus // -
346 | case Equal // =
347 | case Slash // /
348 | case InStyleOrScript //
349 | case MarkEndTagForSure //TODO: fix script 里可能出现 < 符号的问题,这个事件触发发生在 _bufferStr == "" 时,这时才能确保 script 的内容是结束了
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/Sources/Core/HTML/HTMLTreeBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTMLTreeBuilder.swift
3 | // HTNSwift
4 | //
5 | // Created by DaiMing on 2017/10/11.
6 | // Copyright © 2017年 Starming. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class HTMLTreeBuilder {
12 | var tokenizer: HTMLTokenizer
13 | public var doc: Document
14 | public var currentToken: HTMLToken?
15 |
16 | private var _lastElement: Element //TODO: 上一个元素,暂时无用
17 | private var _currentElement: Element //当前元素
18 | private var _currentParent: Element? //当前元素父元素
19 |
20 | public init(_ input: String) {
21 | doc = Document()
22 | tokenizer = HTMLTokenizer(input)
23 | _lastElement = Element()
24 | _currentElement = Element()
25 | }
26 |
27 | public func parse() -> [HTMLToken] {
28 |
29 | let tks = tokenizer.parse() //词法分析
30 |
31 | var stackElement = [Element]() //为了父子级别而记录深度的堆栈
32 | let stateMachine = HTNStateMachine(S.BeforeHTMLState)
33 |
34 | stateMachine.listen(E.StartTagEvent, transit: S.InitialModeState, to: S.BeforeHTMLState) { (t) in
35 | //TODO:暂时只支持 html 标签内,所以外部的定义先不处理
36 | }
37 | stateMachine.listen(E.StartTagEvent, transit: S.BeforeHTMLState, to: S.BeforeHeadState) { (t) in
38 | //TODO:根 Node Document
39 | }
40 | stateMachine.listen(E.StartTagEvent, transit: S.BeforeHeadState, to: S.InHeadState) { (t) in
41 | //
42 | }
43 | stateMachine.listen(E.CharEvent, transit: S.InHeadState, to: S.InHeadState) { (t) in
44 | //在 head 里
45 | if self._currentParent?.startTagToken?.data == "style" {
46 | self.doc.styleList.append(self._currentElement)
47 | }
48 | if self._currentParent?.startTagToken?.data == "script" {
49 | self.doc.scriptList.append(self._currentElement)
50 | }
51 | }
52 |
53 | //InHeadState
54 | stateMachine.listen(E.EndHeadTagEvent, transit: S.InHeadState, to: S.AfterHeadState) { (t) in
55 | //
56 | }
57 |
58 | //AfterHeadState
59 | stateMachine.listen(E.StartTagEvent, transit: S.AfterHeadState, to: S.InBodyState) { (t) in
60 | //
61 | }
62 | stateMachine.listen(E.StartTagEvent, transit: S.InBodyState, to: S.InBodyState) { (t) in
63 | //TODO: 处理 inline style
64 | }
65 |
66 | //TODO: AfterBodyState 和 AfterAfterBodyState 的情况
67 |
68 | for tk in tks {
69 | var hasTrigger = false
70 | //TODO:现将无关的过滤之,以后再做处理
71 | if tk.type == .StartTag || tk.type == .Char || tk.type == .EndTag {
72 | } else {
73 | continue
74 | }
75 | _currentElement = Element(token: tk)
76 | //根元素的处理
77 | if tk.data == "html" && tk.type == .StartTag {
78 | _currentElement = Document(token: tk)
79 | doc = _currentElement as! Document
80 | }
81 |
82 | //StartTag
83 | if tk.type == .StartTag {
84 | stackElement.append(_currentElement) //堆栈添加
85 |
86 | //子关闭标签的情况
87 | if tk.selfClosing {
88 | _ = stackElement.popLast()
89 | _currentParent = stackElement.last
90 | self.parentAppendChild()
91 | } else {
92 | self.parentAppendChild()
93 | _currentParent = _currentElement
94 | }
95 |
96 | hasTrigger = stateMachine.trigger(E.StartTagEvent)
97 | }
98 |
99 | //Char
100 | if tk.type == .Char {
101 | //添加子结点
102 | self.parentAppendChild()
103 | hasTrigger = stateMachine.trigger(E.CharEvent)
104 | }
105 |
106 | //EndTag
107 | if tk.type == .EndTag {
108 | //pop 出堆栈
109 | _ = stackElement.popLast()
110 | _currentParent = stackElement.last
111 | if tk.data == "head" {
112 | hasTrigger = stateMachine.trigger(E.EndHeadTagEvent)
113 | } else {
114 | hasTrigger = stateMachine.trigger(E.EndTagEvent)
115 | }
116 | }
117 | if hasTrigger {
118 |
119 | }
120 | }
121 |
122 | return tks
123 | }
124 |
125 | func parentAppendChild() {
126 | _currentElement.parent = _currentParent
127 | _currentParent?.children.append(_currentElement)
128 | }
129 |
130 | //TODO: 按照 w3c 的状态来。
131 | //w3c 的定义:https://www.w3.org/TR/html5/syntax.html#html-parser
132 | enum S: HTNStateType {
133 | case InitialModeState
134 | case BeforeHTMLState
135 | case BeforeHeadState
136 | case InHeadState
137 | case AfterHeadState
138 | case InBodyState
139 | case AfterBodyState
140 | case AfterAfterBodyState
141 | }
142 |
143 | enum E: HTNEventType {
144 | case StartTagEvent
145 | case CharEvent
146 | case EndTagEvent
147 | case EndHeadTagEvent //
148 | case EndBodyTagEvent //TODO: 先不处理