Browse Source

Initial import

Wirecog, LLC 2 years ago
commit
d3b0d4d395
8 changed files with 755 additions and 0 deletions
  1. 6
    0
      .gitignore
  2. 163
    0
      Acknowledgements.md
  3. 29
    0
      LICENSE
  4. 10
    0
      Readme.md
  5. 170
    0
      domnode.go
  6. 253
    0
      parsetree.go
  7. 123
    0
      reconcile.go
  8. 1
    0
      vendor/golang.org/x/net

+ 6
- 0
.gitignore View File

@@ -0,0 +1,6 @@
1
+*~
2
+*.swp
3
+*.swo
4
+*.tmp
5
+*.out
6
+.DS_Store

+ 163
- 0
Acknowledgements.md View File

@@ -0,0 +1,163 @@
1
+# Acknowledgements
2
+
3
+Reconcile is made possible by the following open-source software projects:
4
+
5
+## Golang
6
+Website: [Golang](https://github.com/golang/go)
7
+
8
+Copyright (c) 2009 The Go Authors. All rights reserved.
9
+
10
+Redistribution and use in source and binary forms, with or without
11
+modification, are permitted provided that the following conditions are
12
+met:
13
+
14
+   * Redistributions of source code must retain the above copyright
15
+notice, this list of conditions and the following disclaimer.
16
+   * Redistributions in binary form must reproduce the above
17
+copyright notice, this list of conditions and the following disclaimer
18
+in the documentation and/or other materials provided with the
19
+distribution.
20
+   * Neither the name of Google Inc. nor the names of its
21
+contributors may be used to endorse or promote products derived from
22
+this software without specific prior written permission.
23
+
24
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+## Gorilla Web Toolkit
37
+
38
+Website: [Gorilla Web Toolkit](https://github.com/gorilla)
39
+
40
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
41
+
42
+Redistribution and use in source and binary forms, with or without
43
+modification, are permitted provided that the following conditions are
44
+met:
45
+
46
+* Redistributions of source code must retain the above copyright
47
+notice, this list of conditions and the following disclaimer.
48
+
49
+* Redistributions in binary form must reproduce the above
50
+copyright notice, this list of conditions and the following disclaimer
51
+in the documentation and/or other materials provided with the
52
+distribution.
53
+
54
+* Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from
55
+this software without specific prior written permission.
56
+
57
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
58
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
59
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
60
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
61
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
62
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
63
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
64
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
65
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
66
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
67
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68
+
69
+
70
+## GopherJS
71
+
72
+Website: [GopherJS](https://github.com/gopherjs)
73
+
74
+Copyright (c) 2013 Richard Musiol. All rights reserved.
75
+
76
+Redistribution and use in source and binary forms, with or without
77
+modification, are permitted provided that the following conditions are
78
+met:
79
+
80
+   * Redistributions of source code must retain the above copyright
81
+notice, this list of conditions and the following disclaimer.
82
+   * Redistributions in binary form must reproduce the above
83
+copyright notice, this list of conditions and the following disclaimer
84
+in the documentation and/or other materials provided with the
85
+distribution.
86
+
87
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
88
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
89
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
90
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
91
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
92
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
93
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
94
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
95
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
96
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
97
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
98
+
99
+## Go JS DOM
100
+
101
+Website [go-js-dom](https://github.com/dominikh/go-js-dom)
102
+
103
+Copyright (c) 2014 Dominik Honnef
104
+
105
+Permission is hereby granted, free of charge, to any person obtaining
106
+a copy of this software and associated documentation files (the
107
+"Software"), to deal in the Software without restriction, including
108
+without limitation the rights to use, copy, modify, merge, publish,
109
+distribute, sublicense, and/or sell copies of the Software, and to
110
+permit persons to whom the Software is furnished to do so, subject to
111
+the following conditions:
112
+
113
+The above copyright notice and this permission notice shall be
114
+included in all copies or substantial portions of the Software.
115
+
116
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
117
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
118
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
119
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
120
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
121
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
122
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
123
+
124
+## Go Wade
125
+Website: [Go Wade](https://github.com/gowade)
126
+
127
+Copyright (c) 2015 Hai Thanh Nguyen and Michael Hanley.
128
+All rights reserved.
129
+
130
+Redistribution and use in source and binary forms, with or without
131
+modification, are permitted provided that the following conditions are
132
+met:
133
+
134
+   * Redistributions of source code must retain the above copyright
135
+notice, this list of conditions and the following disclaimer.
136
+   * Redistributions in binary form must reproduce the above
137
+copyright notice, this list of conditions and the following disclaimer
138
+in the documentation and/or other materials provided with the
139
+distribution.
140
+
141
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
142
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
143
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
144
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
145
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
146
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
147
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
148
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
149
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
150
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
151
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
152
+
153
+## Go Humble
154
+Website: [Go Humble](https://github.com/go-humble/)
155
+
156
+Copyright (C) 2015, Alex Browne and Soroush Pour
157
+
158
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
159
+
160
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
161
+
162
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
163
+

+ 29
- 0
LICENSE View File

@@ -0,0 +1,29 @@
1
+The UXToolkit Project
2
+Copyright (c) Wirecog, LLC. All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are
6
+met:
7
+
8
+   * Redistributions of source code must retain the above copyright
9
+notice, this list of conditions and the following disclaimer.
10
+   * Redistributions in binary form must reproduce the above
11
+copyright notice, this list of conditions and the following disclaimer
12
+in the documentation and/or other materials provided with the
13
+distribution.
14
+   * Neither the name of Wirecog, LLC. nor the names of its contributors
15
+may be used to endorse or promote products derived from
16
+this software without specific prior written permission.
17
+
18
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+

+ 10
- 0
Readme.md View File

@@ -0,0 +1,10 @@
1
+<p align="center"><a href="http://uxtoolkit.io" target="_blank"><img src="https://avatars3.githubusercontent.com/u/27899635?v=4&s=360"></a></p>
2
+
3
+# Reconcile
4
+
5
+[![Go Report Card](https://goreportcard.com/badge/github.com/uxtoolkit/reconcile)](https://goreportcard.com/report/github.com/uxtoolkit/reconcile)
6
+
7
+The reconcile package is used for DOM reconcilation in [Isomorphic Go](http://isomorphicgo.org) web applications.
8
+
9
+## License
10
+Reconcile is licensed under the BSD License. Read the [LICENSE](https://github.com/uxtoolkit/reconcile/blob/master/LICENSE) file for more information.

+ 170
- 0
domnode.go View File

@@ -0,0 +1,170 @@
1
+// The UXToolkit Project
2
+// Copyright (c) Wirecog, LLC. All rights reserved.
3
+// Use of this source code is governed by a BSD-style
4
+// license, which can be found in the LICENSE file.
5
+
6
+package reconcile
7
+
8
+import (
9
+	"html"
10
+	"strconv"
11
+
12
+	"honnef.co/go/js/dom"
13
+)
14
+
15
+const (
16
+	ElementNodeType = iota
17
+	CommentNodeType
18
+	TextNodeType
19
+)
20
+
21
+type Positions struct {
22
+	startPosition      int
23
+	innerStartPosition int
24
+	endPosition        int
25
+	innerEndPosition   int
26
+}
27
+
28
+type Attribute struct {
29
+	Name  string
30
+	Value string
31
+}
32
+
33
+type DOMNode struct {
34
+	Positions
35
+	NodeType      int
36
+	Position      []int
37
+	ParentNode    *DOMNode
38
+	ChildNodes    []*DOMNode
39
+	Contents      []byte
40
+	Name          string
41
+	Value         []byte
42
+	IsSelfClosing bool
43
+	Attributes    []Attribute
44
+	tree          *ParseTree
45
+}
46
+
47
+func NewDOMNode(nodeType int) *DOMNode {
48
+
49
+	result := &DOMNode{}
50
+	result.innerEndPosition = -1
51
+	if nodeType == ElementNodeType {
52
+		result.NodeType = ElementNodeType
53
+	} else if nodeType == CommentNodeType {
54
+		result.NodeType = CommentNodeType
55
+	} else if nodeType == TextNodeType {
56
+		result.NodeType = TextNodeType
57
+	}
58
+
59
+	return result
60
+}
61
+
62
+func (dn *DOMNode) Create() dom.Node {
63
+
64
+	var result dom.Node = nil
65
+	d := dom.GetWindow().Document()
66
+
67
+	if dn.NodeType == CommentNodeType {
68
+
69
+		element := d.Underlying().Call("createComment", string(dn.Value))
70
+		result = dom.WrapNode(element)
71
+
72
+	} else if dn.NodeType == TextNodeType {
73
+
74
+		result = d.CreateTextNode(string(dn.Value))
75
+
76
+	} else if dn.NodeType == ElementNodeType {
77
+
78
+		element := d.CreateElement(dn.Name)
79
+		for _, attribute := range dn.Attributes {
80
+			element.SetAttribute(attribute.Name, attribute.Value)
81
+		}
82
+
83
+		element.SetInnerHTML(string(dn.GetHTMLContents(true)))
84
+		result = element
85
+	}
86
+
87
+	return result
88
+}
89
+
90
+func (dn *DOMNode) Locate(rootElement dom.Element) dom.Node {
91
+
92
+	result := rootElement.ChildNodes()[dn.Position[0]]
93
+	for _, index := range dn.Position[1:] {
94
+		result = result.ChildNodes()[index]
95
+	}
96
+	return result
97
+}
98
+
99
+func (dn *DOMNode) IsEqual(b DOMNode) bool {
100
+
101
+	if dn.NodeType != b.NodeType {
102
+		return false
103
+	}
104
+
105
+	if dn.NodeType == TextNodeType || dn.NodeType == CommentNodeType {
106
+		if string(dn.Value) != string(b.Value) {
107
+			return false
108
+		} else {
109
+			return true
110
+		}
111
+	}
112
+
113
+	if dn.NodeType == ElementNodeType {
114
+
115
+		if dn.Name != b.Name {
116
+			return false
117
+		}
118
+
119
+		attrs := dn.Attributes
120
+		otherAttrs := b.Attributes
121
+		if len(attrs) != len(otherAttrs) {
122
+			return false
123
+		}
124
+		for i, attr := range attrs {
125
+			otherAttr := otherAttrs[i]
126
+			if attr != otherAttr {
127
+				return false
128
+			}
129
+		}
130
+		return true
131
+	}
132
+
133
+	return false
134
+}
135
+
136
+func (dn *DOMNode) GetHTMLContents(innerContentsOnly bool) []byte {
137
+	if dn.IsSelfClosing == true {
138
+		return nil
139
+	} else {
140
+		contents := string(dn.tree.src[dn.innerStartPosition:dn.innerEndPosition])
141
+		return []byte(html.UnescapeString(contents))
142
+	}
143
+}
144
+
145
+func (dn *DOMNode) AttributesMap() map[string]string {
146
+
147
+	var result map[string]string
148
+
149
+	if dn.NodeType == ElementNodeType {
150
+		result = make(map[string]string)
151
+
152
+		for _, attribute := range dn.Attributes {
153
+			result[attribute.Name] = attribute.Value
154
+		}
155
+	} else {
156
+		result = nil
157
+	}
158
+
159
+	return result
160
+
161
+}
162
+
163
+func (dn *DOMNode) String() string {
164
+
165
+	s := "### DOMNODE ###\n"
166
+	s += "name: " + dn.Name + "\n"
167
+	s += "innerStartPosition: " + strconv.Itoa(dn.innerStartPosition) + "\n"
168
+	s += "innerEndPosition: " + strconv.Itoa(dn.innerEndPosition) + "\n"
169
+	return s
170
+}

+ 253
- 0
parsetree.go View File

@@ -0,0 +1,253 @@
1
+// The UXToolkit Project
2
+// Copyright (c) Wirecog, LLC. All rights reserved.
3
+// Use of this source code is governed by a BSD-style
4
+// license, which can be found in the LICENSE file.
5
+
6
+package reconcile
7
+
8
+import (
9
+	"bytes"
10
+	"errors"
11
+	"io"
12
+
13
+	"golang.org/x/net/html"
14
+)
15
+
16
+// Declare considered empty HTML tags
17
+// Reference: https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
18
+var emptyTags = map[string]int{"input": 1, "area": 1, "base": 1, "br": 1, "col": 1, "embed": 1, "hr": 1, "img": 1, "keygen": 1, "link": 1, "meta": 1, "param": 1, "source": 1, "track": 1, "wbr": 1}
19
+
20
+func IsEmptyElement(tag string) bool {
21
+	if _, ok := emptyTags[tag]; ok == true {
22
+		return true
23
+	} else {
24
+		return false
25
+	}
26
+}
27
+
28
+type ParseTree struct {
29
+	reader     io.Reader
30
+	src        []byte
31
+	ChildNodes []*DOMNode
32
+}
33
+
34
+func NewParseTree(src []byte) (*ParseTree, error) {
35
+	r := bytes.NewReader(src)
36
+	z := html.NewTokenizer(r)
37
+	tree := &ParseTree{src: src, reader: r}
38
+	var node *DOMNode = nil
39
+	for tokenType := z.Next(); tokenType != html.ErrorToken; tokenType = z.Next() {
40
+		token := z.Token()
41
+		if next, err := tree.parse(token, node, z); err != nil {
42
+			return nil, err
43
+		} else {
44
+			node = next
45
+		}
46
+	}
47
+	return tree, nil
48
+}
49
+
50
+func (pt *ParseTree) parse(token html.Token, current *DOMNode, tokenizer *html.Tokenizer) (next *DOMNode, err error) {
51
+
52
+	emptyElement := IsEmptyElement(token.Data)
53
+	offset := tokenizer.CurrentOffset()
54
+	var result *DOMNode
55
+	if token.Type == html.StartTagToken || token.Type == html.SelfClosingTagToken {
56
+
57
+		node := &DOMNode{}
58
+		node.NodeType = ElementNodeType
59
+		node.Name = token.Data
60
+		node.tree = pt
61
+
62
+		for _, attribute := range token.Attr {
63
+			node.Attributes = append(node.Attributes, Attribute{
64
+				Name:  attribute.Key,
65
+				Value: attribute.Val,
66
+			})
67
+		}
68
+
69
+		if current != nil {
70
+			node.Position = make([]int, len(current.Position)+1)
71
+			copy(node.Position, current.Position)
72
+			node.Position[len(current.Position)] = len(current.ChildNodes)
73
+			node.ParentNode = current
74
+			current.ChildNodes = append(current.ChildNodes, node)
75
+		} else {
76
+			node.Position = []int{len(pt.ChildNodes)}
77
+		}
78
+
79
+		node.startPosition, err = pt.ReverseFind(0, offset[0]-1, '<')
80
+
81
+		if err != nil {
82
+			return nil, err
83
+		}
84
+
85
+		node.innerStartPosition = offset[0]
86
+		next = node
87
+		result = node
88
+
89
+		if token.Type == html.SelfClosingTagToken || emptyElement == true {
90
+			current = node
91
+		}
92
+	}
93
+
94
+	if token.Type == html.EndTagToken || token.Type == html.SelfClosingTagToken || emptyElement == true {
95
+
96
+		offset := tokenizer.CurrentOffset()
97
+
98
+		if token.Type == html.SelfClosingTagToken || emptyElement == true {
99
+			current.IsSelfClosing = true
100
+
101
+			if current.ParentNode != nil {
102
+				next = current.ParentNode
103
+			} else {
104
+				next = nil
105
+			}
106
+
107
+		} else {
108
+			current.endPosition = offset[0]
109
+			closingTagLength := len(current.Name) + 3
110
+			current.innerEndPosition = offset[0] - closingTagLength
111
+
112
+			if current.ParentNode != nil {
113
+				next = current.ParentNode
114
+			} else {
115
+				next = nil
116
+			}
117
+
118
+		}
119
+	}
120
+
121
+	if token.Type == html.TextToken {
122
+
123
+		charData := token.Data
124
+		text := &DOMNode{}
125
+		text.NodeType = TextNodeType
126
+		text.Value = []byte(charData)
127
+		if current != nil {
128
+			text.Position = make([]int, len(current.Position)+1)
129
+			copy(text.Position, current.Position)
130
+			text.Position[len(current.Position)] = len(current.ChildNodes)
131
+			text.ParentNode = current
132
+			current.ChildNodes = append(current.ChildNodes, text)
133
+		} else {
134
+			text.Position = []int{len(pt.ChildNodes)}
135
+		}
136
+		result = text
137
+		next = current
138
+	} else if token.Type == html.CommentToken {
139
+
140
+		htmlComment := token.Data
141
+		comment := &DOMNode{}
142
+		comment.NodeType = CommentNodeType
143
+		comment.Value = []byte(htmlComment)
144
+		if current != nil {
145
+
146
+			comment.Position = make([]int, len(current.Position)+1)
147
+			copy(comment.Position, current.Position)
148
+			comment.Position[len(current.Position)] = len(current.ChildNodes)
149
+			comment.ParentNode = current
150
+			current.ChildNodes = append(current.ChildNodes, comment)
151
+		} else {
152
+
153
+			comment.Position = []int{len(pt.ChildNodes)}
154
+		}
155
+		result = comment
156
+		next = current
157
+	}
158
+
159
+	if result != nil && current == nil {
160
+
161
+		pt.ChildNodes = append(pt.ChildNodes, result)
162
+	}
163
+	return next, nil
164
+}
165
+
166
+func (t *ParseTree) Compare(other *ParseTree) (Changes, error) {
167
+
168
+	changes := []Reconciler{}
169
+	if err := compareNodes(&changes, t.ChildNodes, other.ChildNodes); err != nil {
170
+		return nil, err
171
+	}
172
+
173
+	return changes, nil
174
+}
175
+
176
+func compareAttributes(changes *[]Reconciler, a, b *DOMNode) {
177
+
178
+	bAttributes := b.AttributesMap()
179
+	aAttributes := a.AttributesMap()
180
+
181
+	for attributeName := range aAttributes {
182
+		if _, found := bAttributes[attributeName]; !found {
183
+
184
+			*changes = append(*changes, Reconciler{ActionType: RemoveNodeAttributeAction, ExistingNode: a, AttributeName: attributeName})
185
+		}
186
+	}
187
+
188
+	for name, bValue := range bAttributes {
189
+		value, found := aAttributes[name]
190
+		if !found {
191
+
192
+			*changes = append(*changes, Reconciler{ActionType: SetNodeAttributeAction, ExistingNode: a, AttributeName: name, AttributeValue: bValue})
193
+
194
+		} else if value != bValue {
195
+
196
+			*changes = append(*changes, Reconciler{ActionType: SetNodeAttributeAction, ExistingNode: a, AttributeName: name, AttributeValue: bValue})
197
+
198
+		}
199
+	}
200
+}
201
+
202
+func compareNodes(changes *[]Reconciler, a, b []*DOMNode) error {
203
+
204
+	bCount := len(b)
205
+	aCount := len(a)
206
+	minCount := bCount
207
+
208
+	if bCount > aCount {
209
+		for _, bNode := range b[aCount:] {
210
+
211
+			*changes = append(*changes, Reconciler{ActionType: AppendChildNodeAction, ParentNode: bNode.ParentNode, ChildNode: bNode})
212
+		}
213
+		minCount = aCount
214
+	} else if aCount > bCount {
215
+		for _, node := range a[bCount:] {
216
+
217
+			*changes = append(*changes, Reconciler{ActionType: RemoveNodeAction, ExistingNode: node})
218
+
219
+		}
220
+		minCount = bCount
221
+	}
222
+	for i := 0; i < minCount; i++ {
223
+		bNode := b[i]
224
+		n := a[i]
225
+		if n.IsEqual(*bNode) == false {
226
+
227
+			*changes = append(*changes, Reconciler{ActionType: ReplaceNodeAction, ExistingNode: n, NewNode: bNode})
228
+
229
+			continue
230
+		}
231
+		if bNode.NodeType == ElementNodeType {
232
+			node := n
233
+			compareAttributes(changes, node, bNode)
234
+			compareNodes(changes, n.ChildNodes, bNode.ChildNodes)
235
+		}
236
+	}
237
+	return nil
238
+}
239
+
240
+func (pt *ParseTree) ReverseFind(m int, n int, b byte) (int, error) {
241
+	if n >= len(pt.src) || n < m {
242
+		return -1, errors.New("invalid value for n")
243
+	}
244
+	if m >= len(pt.src) || m < 0 {
245
+		return -1, errors.New("invalid value for m")
246
+	}
247
+	for i := n; i >= m; i-- {
248
+		if pt.src[i] == b {
249
+			return i, nil
250
+		}
251
+	}
252
+	return -1, nil
253
+}

+ 123
- 0
reconcile.go View File

@@ -0,0 +1,123 @@
1
+// The UXToolkit Project
2
+// Copyright (c) Wirecog, LLC. All rights reserved.
3
+// Use of this source code is governed by a BSD-style
4
+// license, which can be found in the LICENSE file.
5
+
6
+package reconcile
7
+
8
+import (
9
+	"errors"
10
+
11
+	"honnef.co/go/js/dom"
12
+)
13
+
14
+const (
15
+	SetNodeAttributeAction = iota
16
+	RemoveNodeAttributeAction
17
+	RemoveNodeAction
18
+	ReplaceNodeAction
19
+	AppendChildNodeAction
20
+)
21
+
22
+type ReconcileParams struct {
23
+}
24
+
25
+type Reconciler struct {
26
+	ActionType     int
27
+	ParentNode     *DOMNode
28
+	ChildNode      *DOMNode
29
+	ExistingNode   *DOMNode
30
+	NewNode        *DOMNode
31
+	AttributeName  string
32
+	AttributeValue string
33
+}
34
+
35
+func (r *Reconciler) ApplyChange(rootElement dom.Element) error {
36
+
37
+	switch r.ActionType {
38
+
39
+	case SetNodeAttributeAction:
40
+		r.SetNodeAttribute(rootElement)
41
+	case RemoveNodeAttributeAction:
42
+		r.RemoveNodeAttribute(rootElement)
43
+	case RemoveNodeAction:
44
+		r.RemoveNode(rootElement)
45
+	case ReplaceNodeAction:
46
+		r.ReplaceNode(rootElement)
47
+	case AppendChildNodeAction:
48
+		r.AppendChildNode(rootElement)
49
+	default:
50
+		return errors.New("Unknown action")
51
+	}
52
+	return nil
53
+}
54
+
55
+type Changes []Reconciler
56
+
57
+func (r *Reconciler) AppendChildNode(rootElement dom.Element) {
58
+	var parent dom.Node
59
+	if r.ParentNode != nil {
60
+		parent = r.ParentNode.Locate(rootElement)
61
+	} else {
62
+		parent = rootElement
63
+	}
64
+	child := r.ChildNode.Create()
65
+	parent.AppendChild(child)
66
+}
67
+
68
+func (r *Reconciler) ReplaceNode(rootElement dom.Element) {
69
+	var parent dom.Node
70
+	if r.ExistingNode.ParentNode != nil {
71
+		parent = r.ExistingNode.ParentNode.Locate(rootElement)
72
+	} else {
73
+		parent = rootElement
74
+	}
75
+	existingNode := r.ExistingNode.Locate(rootElement)
76
+	newNode := r.NewNode.Create()
77
+	parent.ReplaceChild(newNode, existingNode)
78
+}
79
+
80
+func (r *Reconciler) RemoveNode(rootElement dom.Element) {
81
+	var parent dom.Node
82
+	if r.ExistingNode.ParentNode != nil {
83
+		parent = r.ExistingNode.ParentNode.Locate(rootElement)
84
+	} else {
85
+		parent = rootElement
86
+	}
87
+	self := r.ExistingNode.Locate(rootElement)
88
+	parent.RemoveChild(self)
89
+	if r.ExistingNode.ParentNode != nil {
90
+		lastIndex := r.ExistingNode.Position[len(r.ExistingNode.Position)-1]
91
+		for _, sibling := range r.ExistingNode.ParentNode.ChildNodes[lastIndex:] {
92
+			switch sibling.NodeType {
93
+			case ElementNodeType:
94
+				sibling.Position[len(sibling.Position)-1] = sibling.Position[len(sibling.Position)-1] - 1
95
+			case TextNodeType:
96
+				sibling.Position[len(sibling.Position)-1] = sibling.Position[len(sibling.Position)-1] - 1
97
+			case CommentNodeType:
98
+				sibling.Position[len(sibling.Position)-1] = sibling.Position[len(sibling.Position)-1] - 1
99
+			default:
100
+				panic("Undetermined Node Type!")
101
+			}
102
+		}
103
+	}
104
+
105
+}
106
+
107
+func (r *Reconciler) RemoveNodeAttribute(rootElement dom.Element) {
108
+	self := r.ExistingNode.Locate(rootElement).(dom.Element)
109
+	self.RemoveAttribute(r.AttributeName)
110
+}
111
+
112
+func (r *Reconciler) SetNodeAttribute(rootElement dom.Element) {
113
+	self := r.ExistingNode.Locate(rootElement).(dom.Element)
114
+	self.SetAttribute(r.AttributeName, r.AttributeValue)
115
+}
116
+
117
+func (c Changes) ApplyChanges(rootElement dom.Element) {
118
+
119
+	for _, reconciler := range c {
120
+		reconciler.ApplyChange(rootElement)
121
+	}
122
+
123
+}

+ 1
- 0
vendor/golang.org/x/net

@@ -0,0 +1 @@
1
+Subproject commit 1c05540f6879653db88113bc4a2b70aec4bd491f