-
-
Notifications
You must be signed in to change notification settings - Fork 108
/
xmlbuilder.coffee
139 lines (122 loc) · 4.62 KB
/
xmlbuilder.coffee
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# xmlbuilder
# ==========
# An XML builder for node.js
# Copyright Ozgur Ozcitak 2010
# Licensed under the MIT license
class XMLBuilder
decorators:
OpenTag: '<'
CloseTag: '>'
CloseSOTag: '/>'
AttDelim: '='
AttSep: ' '
Quote: '"'
Space: ' '
Indent: ' '
# Regular expressions to validate tokens
# See: http://www.w3.org/TR/xml/
# Supplementary Unicode code points not supported
validators:
Space:
"(?:\u0020|\u0009|\u000D|\u000A)+"
Char:
"\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]"
NameStartChar:
":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|" +
"[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|" +
"[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"
NameChar:
":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|" +
"[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|" +
"[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]" +
"-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]"
CharRef:
"&#[0-9]+;|&#x[0-9a-fA-F]+;"
CharData:
"(?![^<&]*]]>[^<&]*)[^<&]*"
constructor: (options) ->
@reset ()
options or= { indent: false, validate: true };
@settings = {}
@settings.indent = options.indent or false
@settings.validate = options.validate or true
# build compound validation strings
@validators.Name = @validators.NameStartChar + '(?:' + @validators.NameChar + ')*'
@validators.NMToken = '(?:' + @validators.NameChar + ')+'
@validators.EntityRef = '&' + @validators.Name + ';'
@validators.Reference = @validators.EntityRef + '|' + @validators.CharRef
@validators.PEReference = '%' + @validators.Name + ';'
if @decorators.Quote == '"'
@validators.EntityValue = '(?:[^%&"]|%' + @validators.Name + ';|&' + @validators.Name + ';)*'
else
@validators.EntityValue = "'(?:[^%&']|%" + @validators.Name + ';|&' + @validators.Name + ';)*'
if @decorators.Quote == '"'
@validators.AttValue = '(?:[^<&"]|' + @validators.Reference + ')*'
else
@validators.AttValue = "(?:[^<&']|" + @validators.Reference + ')*'
if @decorators.Quote == '"'
@validators.SystemLiteral = '[^"]*'
else
@validators.SystemLiteral = "[^']*"
if @decorators.Quote == '"'
@validators.PubIDChar = "\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%]"
else
@validators.PubIDChar = "\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-()+,./:=?;!*#@$_%]"
@validators.PubIDLiteral = '(?:' + @validators.PubIDChar + ')*'
@validators.CommentChar = '(?!-)' + '(?:' + @validators.Char + ')'
@validators.Comment = '<!--' + '(?:' + @validators.CommentChar + '|' +
'-' + @validators.CommentChar + ')*' + '-->'
reset: () =>
@elements = []
@level = 0
prolog: () =>
# FIXME
@elements.push '<?xml version="1.0"?>'
element: (name, attributes, callback) =>
value = undefined
if typeof callback is "string"
value = callback
callback = undefined
# open tag
if not name?
throw new Error "Missing element name"
if @settings.validate and not name.match "^" + @validators.Name + "$"
throw new Error "Invalid element name: " + name
@elements.push @decorators.OpenTag + name
# attributes
if attributes?
for attName, attValue of attributes
if @settings.validate and not attName.match "^" + @validators.Name + "$"
throw new Error "Invalid attribute name: " + attName
if @settings.validate and not attValue.match "^" + @validators.AttValue + "$"
throw new Error "Invalid attribute value: " + attValue
@elements.push @decorators.Space + attName + @decorators.Equals +
@decorators.Quote + attValue + @decorators.Quote
# value or inner tags
if value?
if @settings.validate and not value.match "^" + @validators.EntityValue + "$"
throw new Error "Invalid element value: " + value
@elements.push value
else if callback?
callback
# close tag
if value? or callback?
@elements.push @decorators.CloseTag
else
@elements.push @decorators.CloseSOTag
text: (value) =>
if not value?
throw new Error "Missing text value"
if @settings.validate and not value.match "^" + @validators.EntityValue + "$"
throw new Error "Invalid text value: " + value
@elements.push value
toString: ->
r = ""
for e in @elements
r += e
return r
# aliases
pro: () => @prolog
ele: (name, attributes, callback) => @element name, attributes, callback
txt: (value) => @text value
module.exports = XMLBuilder