bare-script 3.8.16 → 3.8.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/include/args.bare +5 -3
- package/lib/include/elementModel.bare +178 -0
- package/lib/include/qrcode.bare +173 -115
- package/lib/include/schemaDoc.bare +0 -1
- package/lib/library.js +24 -2
- package/lib/model.js +2 -0
- package/package.json +1 -1
package/lib/include/args.bare
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Licensed under the MIT License
|
|
2
2
|
# https://github.com/craigahobbs/bare-script/blob/main/LICENSE
|
|
3
3
|
|
|
4
|
-
include <dataTable.bare>
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
# The URL arguments model
|
|
8
6
|
argsTypes = schemaParse( \
|
|
@@ -183,7 +181,7 @@ endfunction
|
|
|
183
181
|
# $doc: Generate the [arguments model's](model.html#var.vName='ArgsArguments') help content
|
|
184
182
|
# $arg arguments: The [arguments model](model.html#var.vName='ArgsArguments')
|
|
185
183
|
# $return: The array of help Markdown line strings
|
|
186
|
-
function argsHelp(arguments):
|
|
184
|
+
async function argsHelp(arguments):
|
|
187
185
|
# Create the help data
|
|
188
186
|
helpData = []
|
|
189
187
|
anyDefault = false
|
|
@@ -210,6 +208,10 @@ function argsHelp(arguments):
|
|
|
210
208
|
anyDescription = anyDescription || (description != null)
|
|
211
209
|
endfor
|
|
212
210
|
|
|
211
|
+
# Dynamically include dataTable.bare
|
|
212
|
+
include <dataTable.bare>
|
|
213
|
+
dataTableMarkdown = systemGlobalGet('dataTableMarkdown')
|
|
214
|
+
|
|
213
215
|
# Render the help table
|
|
214
216
|
helpFields = ['Variable', 'Type']
|
|
215
217
|
if anyDefault:
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Licensed under the MIT License
|
|
2
|
+
# https://github.com/craigahobbs/bare-script/blob/main/LICENSE
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# $function: elementModelValidate
|
|
6
|
+
# $group: elementModel.bare
|
|
7
|
+
# $doc: Validate an element model
|
|
8
|
+
# $arg elements: The element model.
|
|
9
|
+
# $arg elements: An element model is either null, an element object, or an array of any of these.
|
|
10
|
+
# $return: The element model if valid, null otherwise
|
|
11
|
+
function elementModelValidate(elements):
|
|
12
|
+
# Null?
|
|
13
|
+
if elements == null:
|
|
14
|
+
return elements
|
|
15
|
+
endif
|
|
16
|
+
|
|
17
|
+
# Array?
|
|
18
|
+
if systemType(elements) == 'array':
|
|
19
|
+
for subElements in elements:
|
|
20
|
+
if subElements != null && elementModelValidate(subElements) == null:
|
|
21
|
+
return null
|
|
22
|
+
endif
|
|
23
|
+
endfor
|
|
24
|
+
return elements
|
|
25
|
+
endif
|
|
26
|
+
|
|
27
|
+
# Non-object?
|
|
28
|
+
if systemType(elements) != 'object':
|
|
29
|
+
return null
|
|
30
|
+
endif
|
|
31
|
+
|
|
32
|
+
# Check for element tag members and unknown members
|
|
33
|
+
tagMembers = []
|
|
34
|
+
unknownMembers = []
|
|
35
|
+
for elementMember in objectKeys(elements):
|
|
36
|
+
if elementMember == 'html' || elementMember == 'svg' || elementMember == 'text':
|
|
37
|
+
arrayPush(tagMembers, elementMember)
|
|
38
|
+
endif
|
|
39
|
+
if elementMember != 'html' && elementMember != 'svg' && elementMember != 'text' && \
|
|
40
|
+
elementMember != 'attr' && elementMember != 'elem' && elementMember != 'callback':
|
|
41
|
+
arrayPush(unknownMembers, elementMember)
|
|
42
|
+
endif
|
|
43
|
+
endfor
|
|
44
|
+
if arrayLength(tagMembers) != 1:
|
|
45
|
+
return null
|
|
46
|
+
endif
|
|
47
|
+
if arrayLength(unknownMembers) != 0:
|
|
48
|
+
return null
|
|
49
|
+
endif
|
|
50
|
+
|
|
51
|
+
# Validate the tag
|
|
52
|
+
tagMember = arrayGet(tagMembers, 0)
|
|
53
|
+
tag = objectGet(elements, tagMember)
|
|
54
|
+
if tagMember != 'text' && (systemType(tag) != 'string' || stringLength(tag) == 0):
|
|
55
|
+
return null
|
|
56
|
+
endif
|
|
57
|
+
|
|
58
|
+
# Validate attributes
|
|
59
|
+
attr = objectGet(elements, 'attr')
|
|
60
|
+
if objectHas(elements, 'attr') && tagMember == 'text':
|
|
61
|
+
return null
|
|
62
|
+
endif
|
|
63
|
+
if attr != null:
|
|
64
|
+
if systemType(attr) != 'object':
|
|
65
|
+
return null
|
|
66
|
+
endif
|
|
67
|
+
endif
|
|
68
|
+
|
|
69
|
+
# Validate child elements
|
|
70
|
+
elem = objectGet(elements, 'elem')
|
|
71
|
+
if objectHas(elements, 'elem') && tagMember == 'text':
|
|
72
|
+
return null
|
|
73
|
+
endif
|
|
74
|
+
if elem != null:
|
|
75
|
+
if elementModelValidate(elem) == null:
|
|
76
|
+
return null
|
|
77
|
+
endif
|
|
78
|
+
endif
|
|
79
|
+
|
|
80
|
+
# Validate creation callback
|
|
81
|
+
callback = objectGet(elements, 'callback')
|
|
82
|
+
if callback != null && systemType(callback) != 'function':
|
|
83
|
+
return null
|
|
84
|
+
endif
|
|
85
|
+
|
|
86
|
+
return elements
|
|
87
|
+
endfunction
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# $function: elementModelToString
|
|
91
|
+
# $group: elementModel.bare
|
|
92
|
+
# $doc: Render an element model to an HTML or SVG string
|
|
93
|
+
# $arg elements: The element model.
|
|
94
|
+
# $arg elements: An element model is either null, an element object, or an array of any of these.
|
|
95
|
+
# $arg indent: Optional (default is null). The indentation string or number of spaces
|
|
96
|
+
# $return: The HTML or SVG string
|
|
97
|
+
function elementModelToString(elements, indent):
|
|
98
|
+
# Compute the indent string
|
|
99
|
+
indentStr = ''
|
|
100
|
+
if systemType(indent) == 'string':
|
|
101
|
+
indentStr = indent
|
|
102
|
+
elif systemType(indent) == 'number':
|
|
103
|
+
indentStr = stringRepeat(' ', mathMax(0, indent))
|
|
104
|
+
endif
|
|
105
|
+
|
|
106
|
+
# Render the element model as an HTML/SVG string
|
|
107
|
+
elementStr = elementModelToStringHelper(elements, indentStr, 0)
|
|
108
|
+
if stringStartsWith(elementStr, '<html'):
|
|
109
|
+
elementStr = '<!DOCTYPE html>' + stringFromCharCode(10) + elementStr
|
|
110
|
+
endif
|
|
111
|
+
return elementStr
|
|
112
|
+
endfunction
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Helper function to render elements to string
|
|
116
|
+
function elementModelToStringHelper(elements, indentStr, level):
|
|
117
|
+
# Null?
|
|
118
|
+
if elements == null:
|
|
119
|
+
return ''
|
|
120
|
+
endif
|
|
121
|
+
|
|
122
|
+
# Array?
|
|
123
|
+
if systemType(elements) == 'array':
|
|
124
|
+
results = []
|
|
125
|
+
for element in elements:
|
|
126
|
+
arrayPush(results, elementModelToStringHelper(element, indentStr, level))
|
|
127
|
+
endfor
|
|
128
|
+
return arrayJoin(results, '')
|
|
129
|
+
endif
|
|
130
|
+
|
|
131
|
+
# Text node
|
|
132
|
+
indentPrefix = stringRepeat(indentStr, level)
|
|
133
|
+
newline = if(indentStr, stringFromCharCode(10), '')
|
|
134
|
+
if objectHas(elements, 'text'):
|
|
135
|
+
text = stringNew(objectGet(elements, 'text'))
|
|
136
|
+
text = stringReplace(text, '&', '&')
|
|
137
|
+
text = stringReplace(text, '<', '<')
|
|
138
|
+
text = stringReplace(text, '>', '>')
|
|
139
|
+
text = stringReplace(text, '"', '"')
|
|
140
|
+
text = stringReplace(text, "'", ''')
|
|
141
|
+
return indentPrefix + text + newline
|
|
142
|
+
endif
|
|
143
|
+
|
|
144
|
+
# Attributes
|
|
145
|
+
tag = stringLower(objectGet(elements, 'html') || objectGet(elements, 'svg'))
|
|
146
|
+
attrStr = ''
|
|
147
|
+
attr = objectGet(elements, 'attr')
|
|
148
|
+
if tag == 'svg':
|
|
149
|
+
if attr == null:
|
|
150
|
+
attr = {}
|
|
151
|
+
endif
|
|
152
|
+
objectSet(attr, 'xmlns', 'http://www.w3.org/2000/svg')
|
|
153
|
+
endif
|
|
154
|
+
if attr != null:
|
|
155
|
+
attrNames = arraySort(objectKeys(attr))
|
|
156
|
+
for attrName in attrNames:
|
|
157
|
+
value = objectGet(attr, attrName)
|
|
158
|
+
if value != null:
|
|
159
|
+
attrStr = attrStr + ' ' + attrName + '="' + stringReplace(stringReplace(stringNew(value), '&', '&'), '"', '"') + '"'
|
|
160
|
+
endif
|
|
161
|
+
endfor
|
|
162
|
+
endif
|
|
163
|
+
|
|
164
|
+
# HTML void element?
|
|
165
|
+
isVoid = arrayIndexOf(elementModelVoidTags, tag) != -1
|
|
166
|
+
if isVoid:
|
|
167
|
+
return indentPrefix + '<' + tag + attrStr + ' />' + newline
|
|
168
|
+
endif
|
|
169
|
+
|
|
170
|
+
# Render the element
|
|
171
|
+
elem = objectGet(elements, 'elem')
|
|
172
|
+
childrenStr = if(elem != null, elementModelToStringHelper(elem, indentStr, level + 1), '')
|
|
173
|
+
return indentPrefix + '<' + tag + attrStr + '>' + newline + childrenStr + indentPrefix + '</' + tag + '>' + newline
|
|
174
|
+
endfunction
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# Void elements (simplified list)
|
|
178
|
+
elementModelVoidTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']
|
package/lib/include/qrcode.bare
CHANGED
|
@@ -32,12 +32,9 @@ function qrcodeDraw(message, x, y, size, level):
|
|
|
32
32
|
while ixCol < qrcodeSize:
|
|
33
33
|
pixelValue = arrayGet(arrayGet(matrix, ixRow), ixCol)
|
|
34
34
|
if pixelValue:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
pixelSizeRound, \
|
|
39
|
-
pixelSizeRound \
|
|
40
|
-
)
|
|
35
|
+
pixelLeft = mathRound(borderSize + x + ixCol * pixelSize, precision)
|
|
36
|
+
pixelTop = mathRound(borderSize + y + ixRow * pixelSize, precision)
|
|
37
|
+
drawPathRect(pixelLeft, pixelTop, pixelSizeRound, pixelSizeRound)
|
|
41
38
|
endif
|
|
42
39
|
ixCol = ixCol + 1
|
|
43
40
|
endwhile
|
|
@@ -46,6 +43,69 @@ function qrcodeDraw(message, x, y, size, level):
|
|
|
46
43
|
endfunction
|
|
47
44
|
|
|
48
45
|
|
|
46
|
+
# $function: qrcodeElements
|
|
47
|
+
# $group: qrcode.bare
|
|
48
|
+
# $doc: Generate the element model for a QR code
|
|
49
|
+
# $arg message: The QR code message or the QR code matrix
|
|
50
|
+
# $arg size: The size of the QR code, in pixels
|
|
51
|
+
# $arg level: Optional (default is 'low'). The error correction level: 'low', 'medium', 'quartile', or 'high'.
|
|
52
|
+
# $return: The QR code SVG [element model](https://github.com/craigahobbs/element-model#readme)
|
|
53
|
+
function qrcodeElements(message, size, level):
|
|
54
|
+
# Get the QR code pixel matrix
|
|
55
|
+
matrix = if(systemType(message) == 'string', qrcodeMatrix(message, level), message)
|
|
56
|
+
qrcodeSize = arrayLength(matrix)
|
|
57
|
+
|
|
58
|
+
# Compute the black pixel paths
|
|
59
|
+
# Note: The standard recommendation is a 4-module quiet zone on all sides.
|
|
60
|
+
cellPaths = []
|
|
61
|
+
ixRow = 0
|
|
62
|
+
pixelSize = size / (qrcodeSize + 8)
|
|
63
|
+
borderSize = pixelSize * 4
|
|
64
|
+
precision = 6
|
|
65
|
+
while ixRow < qrcodeSize:
|
|
66
|
+
ixCol = 0
|
|
67
|
+
while ixCol < qrcodeSize:
|
|
68
|
+
pixelValue = arrayGet(arrayGet(matrix, ixRow), ixCol)
|
|
69
|
+
if pixelValue:
|
|
70
|
+
pixelLeft = borderSize + ixCol * pixelSize
|
|
71
|
+
pixelTop = borderSize + ixRow * pixelSize
|
|
72
|
+
arrayPush(cellPaths, arrayJoin([ \
|
|
73
|
+
'M', numberToFixed(pixelLeft, precision), numberToFixed(pixelTop, precision), \
|
|
74
|
+
'H', numberToFixed(pixelLeft + pixelSize, precision), \
|
|
75
|
+
'V', numberToFixed(pixelTop + pixelSize, precision), \
|
|
76
|
+
'H', numberToFixed(pixelLeft, precision), \
|
|
77
|
+
'Z' \
|
|
78
|
+
], ' '))
|
|
79
|
+
endif
|
|
80
|
+
ixCol = ixCol + 1
|
|
81
|
+
endwhile
|
|
82
|
+
ixRow = ixRow + 1
|
|
83
|
+
endwhile
|
|
84
|
+
|
|
85
|
+
# Create the QR code SVG element model
|
|
86
|
+
return { \
|
|
87
|
+
'svg': 'svg', \
|
|
88
|
+
'attr': {'width': size, 'height': size}, \
|
|
89
|
+
'elem': [ \
|
|
90
|
+
{ \
|
|
91
|
+
'svg': 'rect', \
|
|
92
|
+
'attr': { \
|
|
93
|
+
'x': '0', 'y': '0', 'width': size, 'height': size, \
|
|
94
|
+
'fill': 'white', 'stroke': 'none', 'stroke-width': '0', 'stroke-dasharray': 'none' \
|
|
95
|
+
} \
|
|
96
|
+
}, \
|
|
97
|
+
{ \
|
|
98
|
+
'svg': 'path', \
|
|
99
|
+
'attr': { \
|
|
100
|
+
'fill': 'black', 'stroke': 'none', 'stroke-width': '0', 'stroke-dasharray': 'none', \
|
|
101
|
+
'd': arrayJoin(cellPaths, ' ') \
|
|
102
|
+
} \
|
|
103
|
+
} \
|
|
104
|
+
] \
|
|
105
|
+
}
|
|
106
|
+
endfunction
|
|
107
|
+
|
|
108
|
+
|
|
49
109
|
# $function: qrcodeMatrix
|
|
50
110
|
# $group: qrcode.bare
|
|
51
111
|
# $doc: Generate a QR code pixel matrix
|
|
@@ -55,90 +115,92 @@ endfunction
|
|
|
55
115
|
function qrcodeMatrix(message, level):
|
|
56
116
|
level = if(level != null, level, 'low')
|
|
57
117
|
|
|
58
|
-
# Find the QR code model
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# Create the pixel matrix
|
|
66
|
-
matrix = []
|
|
67
|
-
ixRow = 0
|
|
68
|
-
while ixRow < size:
|
|
69
|
-
arrayPush(matrix, arrayNewSize(size, null))
|
|
70
|
-
ixRow = ixRow + 1
|
|
71
|
-
endwhile
|
|
72
|
-
|
|
73
|
-
# Position squares
|
|
74
|
-
qrcodeMatrixPositionSquare(matrix, 3, 3)
|
|
75
|
-
qrcodeMatrixPositionSquare(matrix, 3, size - 4)
|
|
76
|
-
qrcodeMatrixPositionSquare(matrix, size - 4, 3)
|
|
77
|
-
|
|
78
|
-
# Alignment patterns
|
|
79
|
-
if alignmentPatterns:
|
|
80
|
-
ixLast = arrayLength(alignmentPatterns) - 1
|
|
81
|
-
for alignX, ixX in alignmentPatterns:
|
|
82
|
-
for alignY, ixY in alignmentPatterns:
|
|
83
|
-
if !(ixX == 0 && ixY == 0) && !(ixX == 0 && ixY == ixLast) && !(ixX == ixLast && ixY == 0):
|
|
84
|
-
qrcodeMatrixAlignmentPattern(matrix, alignX, alignY)
|
|
85
|
-
endif
|
|
86
|
-
endfor
|
|
87
|
-
endfor
|
|
118
|
+
# Find the QR code model that fits the message bits (mode, version, message, terminator)
|
|
119
|
+
qrcode = null
|
|
120
|
+
for qrcodeFind in qrcodeVersions:
|
|
121
|
+
messageBits = qrcodeMessageBits(qrcodeFind, level, message)
|
|
122
|
+
if messageBits != null:
|
|
123
|
+
qrcode = qrcodeFind
|
|
124
|
+
break
|
|
88
125
|
endif
|
|
126
|
+
endfor
|
|
127
|
+
if qrcode == null:
|
|
128
|
+
systemLogDebug('qrcode.bare: qrcodeMatrix - Message too long (' + stringLength(message) + ')')
|
|
129
|
+
return null
|
|
130
|
+
endif
|
|
131
|
+
size = objectGet(qrcode, 'size')
|
|
132
|
+
alignmentPatterns = objectGet(qrcode, 'alignmentPatterns')
|
|
133
|
+
totalCodewords = objectGet(objectGet(qrcode, level), 'totalCodewords')
|
|
89
134
|
|
|
90
|
-
|
|
91
|
-
|
|
135
|
+
# Create the pixel matrix
|
|
136
|
+
matrix = []
|
|
137
|
+
ixRow = 0
|
|
138
|
+
while ixRow < size:
|
|
139
|
+
arrayPush(matrix, arrayNewSize(size, null))
|
|
140
|
+
ixRow = ixRow + 1
|
|
141
|
+
endwhile
|
|
142
|
+
|
|
143
|
+
# Position squares
|
|
144
|
+
qrcodeMatrixPositionSquare(matrix, 3, 3)
|
|
145
|
+
qrcodeMatrixPositionSquare(matrix, 3, size - 4)
|
|
146
|
+
qrcodeMatrixPositionSquare(matrix, size - 4, 3)
|
|
147
|
+
|
|
148
|
+
# Alignment patterns
|
|
149
|
+
if alignmentPatterns:
|
|
150
|
+
ixLast = arrayLength(alignmentPatterns) - 1
|
|
151
|
+
for alignX, ixAX in alignmentPatterns:
|
|
152
|
+
for alignY, ixAY in alignmentPatterns:
|
|
153
|
+
if !(ixAX == 0 && ixAY == 0) && !(ixAX == 0 && ixAY == ixLast) && !(ixAX == ixLast && ixAY == 0):
|
|
154
|
+
qrcodeMatrixAlignmentPattern(matrix, alignX, alignY)
|
|
155
|
+
endif
|
|
156
|
+
endfor
|
|
157
|
+
endfor
|
|
158
|
+
endif
|
|
92
159
|
|
|
93
|
-
|
|
94
|
-
|
|
160
|
+
# Timing strips
|
|
161
|
+
qrcodeMatrixTimingStrips(matrix)
|
|
95
162
|
|
|
96
|
-
|
|
97
|
-
|
|
163
|
+
# Format strips - use mask 0 as placeholder bits for qrcodeDataPixels
|
|
164
|
+
qrcodeMatrixFormatStrips(matrix, level, 0)
|
|
98
165
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if messageBits != null:
|
|
102
|
-
# Compute the data pixels
|
|
103
|
-
dataPixels = qrcodeDataPixels(matrix)
|
|
166
|
+
# Version blocks
|
|
167
|
+
qrcodeMatrixVersionBlocks(matrix, qrcode)
|
|
104
168
|
|
|
105
|
-
|
|
106
|
-
|
|
169
|
+
# Compute the data pixels
|
|
170
|
+
dataPixels = qrcodeDataPixels(matrix)
|
|
107
171
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if remainderLength > 0:
|
|
111
|
-
arrayExtend(dataBits, arrayNewSize(remainderLength, 0))
|
|
112
|
-
endif
|
|
172
|
+
# Compute the data bits
|
|
173
|
+
dataBits = qrcodeDataBits(qrcode, level, message)
|
|
113
174
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# Set full data bits with mask
|
|
120
|
-
maskMatrix = jsonParse(jsonStringify(matrix))
|
|
121
|
-
qrcodeMatrixPixels(maskMatrix, dataPixels, dataBits, mask)
|
|
122
|
-
|
|
123
|
-
# Set the format strips bits
|
|
124
|
-
qrcodeMatrixFormatStrips(maskMatrix, level, mask)
|
|
125
|
-
|
|
126
|
-
# Compute the mask's penalty
|
|
127
|
-
maskPenalty = qrcodeMatrixPenalty(maskMatrix)
|
|
128
|
-
if bestPenalty == null || maskPenalty < bestPenalty:
|
|
129
|
-
bestMatrix = maskMatrix
|
|
130
|
-
bestPenalty = maskPenalty
|
|
131
|
-
endif
|
|
132
|
-
mask = mask + 1
|
|
133
|
-
endwhile
|
|
175
|
+
# Add the data remainder bits
|
|
176
|
+
remainderLength = arrayLength(dataPixels) - totalCodewords * 8
|
|
177
|
+
if remainderLength > 0:
|
|
178
|
+
arrayExtend(dataBits, arrayNewSize(remainderLength, 0))
|
|
179
|
+
endif
|
|
134
180
|
|
|
135
|
-
|
|
181
|
+
# Compute the penalty for each mask
|
|
182
|
+
bestMatrix = null
|
|
183
|
+
bestPenalty = null
|
|
184
|
+
mask = 0
|
|
185
|
+
while mask < 8:
|
|
186
|
+
# Set full data bits with mask
|
|
187
|
+
maskMatrix = jsonParse(jsonStringify(matrix))
|
|
188
|
+
qrcodeMatrixPixels(maskMatrix, dataPixels, dataBits, mask)
|
|
189
|
+
|
|
190
|
+
# Set the format strips bits
|
|
191
|
+
qrcodeMatrixFormatStrips(maskMatrix, level, mask)
|
|
192
|
+
|
|
193
|
+
# Compute the mask's penalty
|
|
194
|
+
maskPenalty = qrcodeMatrixPenalty(maskMatrix)
|
|
195
|
+
if bestPenalty == null || maskPenalty < bestPenalty:
|
|
196
|
+
bestMatrix = maskMatrix
|
|
197
|
+
bestPenalty = maskPenalty
|
|
136
198
|
endif
|
|
137
|
-
endfor
|
|
138
199
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
200
|
+
mask = mask + 1
|
|
201
|
+
endwhile
|
|
202
|
+
|
|
203
|
+
return bestMatrix
|
|
142
204
|
endfunction
|
|
143
205
|
|
|
144
206
|
|
|
@@ -190,12 +252,13 @@ endfunction
|
|
|
190
252
|
|
|
191
253
|
# Set the format strips pixels
|
|
192
254
|
function qrcodeMatrixFormatStrips(matrix, level, mask):
|
|
193
|
-
# The "dark pixel"
|
|
194
255
|
size = arrayLength(matrix)
|
|
256
|
+
|
|
257
|
+
# The "dark pixel"
|
|
195
258
|
arraySet(arrayGet(matrix, size - 8), 8, 1)
|
|
196
259
|
|
|
197
260
|
# Set the format strip bits
|
|
198
|
-
formatStripBits = objectGet(objectGet(
|
|
261
|
+
formatStripBits = objectGet(objectGet(qrcodeMatrixFormatStripsBitsTable, level), stringNew(mask))
|
|
199
262
|
pixelsTopLeft = [ \
|
|
200
263
|
[0, 8], [1, 8], [2, 8], [3, 8], [4, 8], [5, 8], [7, 8], [8, 8], \
|
|
201
264
|
[8, 7], [8, 5], [8, 4], [8, 3], [8, 2], [8, 1], [8, 0] \
|
|
@@ -210,7 +273,7 @@ endfunction
|
|
|
210
273
|
|
|
211
274
|
|
|
212
275
|
# Format strip bits lookup table
|
|
213
|
-
|
|
276
|
+
qrcodeMatrixFormatStripsBitsTable = { \
|
|
214
277
|
'low': { \
|
|
215
278
|
'0': [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0], \
|
|
216
279
|
'1': [1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1], \
|
|
@@ -257,7 +320,7 @@ qrcodeMatrixFormatStripsBitsLookup = { \
|
|
|
257
320
|
# Set the version blocks pixels
|
|
258
321
|
function qrcodeMatrixVersionBlocks(matrix, qrcode):
|
|
259
322
|
version = objectGet(qrcode, 'version')
|
|
260
|
-
versionBits = objectGet(
|
|
323
|
+
versionBits = objectGet(qrcodeMatrixVersionBitsTable, stringNew(version))
|
|
261
324
|
if versionBits:
|
|
262
325
|
size = arrayLength(matrix)
|
|
263
326
|
bottomLeftPixels = [ \
|
|
@@ -277,7 +340,7 @@ endfunction
|
|
|
277
340
|
|
|
278
341
|
|
|
279
342
|
# Version bits lookup table
|
|
280
|
-
|
|
343
|
+
qrcodeMatrixVersionBitsTable = { \
|
|
281
344
|
'7': [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0], \
|
|
282
345
|
'10': [0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1], \
|
|
283
346
|
'15': [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0] \
|
|
@@ -438,8 +501,7 @@ endfunction
|
|
|
438
501
|
# Get the terminated message bits (mode, size, message, terminator)
|
|
439
502
|
function qrcodeMessageBits(qrcode, level, message):
|
|
440
503
|
version = objectGet(qrcode, 'version')
|
|
441
|
-
|
|
442
|
-
totalMessageCodewords = objectGet(ecLevel, 'totalMessageCodewords')
|
|
504
|
+
totalMessageCodewords = objectGet(objectGet(qrcode, level), 'totalMessageCodewords')
|
|
443
505
|
|
|
444
506
|
# Determine the message mode
|
|
445
507
|
# Note: Kanji and ECI modes not supported
|
|
@@ -573,60 +635,54 @@ function qrcodeMatrixPenalty(matrix):
|
|
|
573
635
|
size = arrayLength(matrix)
|
|
574
636
|
penalty = 0
|
|
575
637
|
|
|
576
|
-
#
|
|
577
|
-
stringRowsAndColumns = []
|
|
638
|
+
# Compute row penalties
|
|
578
639
|
for row in matrix:
|
|
579
|
-
|
|
640
|
+
stringRow = arrayJoin(row, '')
|
|
641
|
+
|
|
642
|
+
# Penalty 1: Five or more same-colored modules in a row
|
|
643
|
+
for runMatch in regexMatchAll(qrcodeMatrixPenaltyRunRegex, stringRow):
|
|
644
|
+
penalty = penalty + 3 + (stringLength(objectGet(objectGet(runMatch, 'groups'), '0')) - 5)
|
|
645
|
+
endfor
|
|
646
|
+
|
|
647
|
+
# Penalty 3: Finder-like patterns (1011101 with 4 white modules on either side)
|
|
648
|
+
penalty = penalty + 40 * arrayLength(regexMatchAll(qrcodeMatrixPenaltyPatternRegex, stringRow))
|
|
580
649
|
endfor
|
|
581
650
|
|
|
582
|
-
#
|
|
583
|
-
twoByCount = 0
|
|
584
|
-
twoBySize = size - 1
|
|
651
|
+
# Compute column penalties - also count dark modules
|
|
585
652
|
darkCount = 0
|
|
653
|
+
twoBySize = size - 1
|
|
586
654
|
ix = 0
|
|
587
655
|
while ix < size:
|
|
588
656
|
column = []
|
|
589
657
|
iy = 0
|
|
590
658
|
while iy < size:
|
|
591
659
|
row = arrayGet(matrix, iy)
|
|
592
|
-
row2 = if(ix < twoBySize && iy < twoBySize, arrayGet(matrix, iy + 1))
|
|
593
660
|
bit = arrayGet(row, ix)
|
|
594
|
-
|
|
595
|
-
# Add the column bit
|
|
596
661
|
arrayPush(column, bit)
|
|
597
662
|
|
|
598
|
-
#
|
|
663
|
+
# Penalty 2: 2x2 blocks of same color
|
|
664
|
+
row2 = if(ix < twoBySize && iy < twoBySize, arrayGet(matrix, iy + 1))
|
|
599
665
|
if row2 && bit == arrayGet(row, ix + 1) && bit == arrayGet(row2, ix) && bit == arrayGet(row2, ix + 1):
|
|
600
|
-
|
|
666
|
+
penalty = penalty + 3
|
|
601
667
|
endif
|
|
602
668
|
|
|
603
669
|
# Count "dark" pixels
|
|
604
|
-
|
|
605
|
-
darkCount = darkCount + 1
|
|
606
|
-
endif
|
|
670
|
+
darkCount = darkCount + bit
|
|
607
671
|
|
|
608
672
|
iy = iy + 1
|
|
609
673
|
endwhile
|
|
674
|
+
stringColumn = arrayJoin(column, '')
|
|
610
675
|
|
|
611
|
-
#
|
|
612
|
-
|
|
613
|
-
ix = ix + 1
|
|
614
|
-
endwhile
|
|
615
|
-
|
|
616
|
-
# Penalty 1: Five or more same-colored modules in a row/column
|
|
617
|
-
for stringRow in stringRowsAndColumns:
|
|
618
|
-
for runMatch in regexMatchAll(qrcodeMatrixPenaltyRunRegex, stringRow):
|
|
676
|
+
# Penalty 1: Five or more same-colored modules in a column
|
|
677
|
+
for runMatch in regexMatchAll(qrcodeMatrixPenaltyRunRegex, stringColumn):
|
|
619
678
|
penalty = penalty + 3 + (stringLength(objectGet(objectGet(runMatch, 'groups'), '0')) - 5)
|
|
620
679
|
endfor
|
|
621
|
-
endfor
|
|
622
680
|
|
|
623
|
-
|
|
624
|
-
|
|
681
|
+
# Penalty 3: Finder-like patterns (1011101 with 4 white modules on either side)
|
|
682
|
+
penalty = penalty + 40 * arrayLength(regexMatchAll(qrcodeMatrixPenaltyPatternRegex, stringColumn))
|
|
625
683
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
penalty = penalty + 40 * arrayLength(regexMatchAll(qrcodeMatrixPenaltyPatternRegex, stringRow))
|
|
629
|
-
endfor
|
|
684
|
+
ix = ix + 1
|
|
685
|
+
endwhile
|
|
630
686
|
|
|
631
687
|
# Penalty 4: Proportion of dark modules
|
|
632
688
|
darkPercent = (darkCount * 100) / (size * size)
|
|
@@ -675,9 +731,11 @@ function qrcodeRSEncode(bytesIn, numCodewords):
|
|
|
675
731
|
|
|
676
732
|
# XOR with existing value (subtraction in GF(2^8))
|
|
677
733
|
arraySet(bytesOut, i + j, oldVal ^ product)
|
|
734
|
+
|
|
678
735
|
j = j + 1
|
|
679
736
|
endwhile
|
|
680
737
|
endif
|
|
738
|
+
|
|
681
739
|
i = i + 1
|
|
682
740
|
endwhile
|
|
683
741
|
|
package/lib/library.js
CHANGED
|
@@ -1635,6 +1635,27 @@ const schemaValidateTypeModelArgs = valueArgsModel([
|
|
|
1635
1635
|
//
|
|
1636
1636
|
|
|
1637
1637
|
|
|
1638
|
+
// $function: stringCharAt
|
|
1639
|
+
// $group: string
|
|
1640
|
+
// $doc: Get the character of a string at an index
|
|
1641
|
+
// $arg string: The string
|
|
1642
|
+
// $arg index: The index of the character
|
|
1643
|
+
// $return: The character string
|
|
1644
|
+
function stringCharAt(args) {
|
|
1645
|
+
const [string, index] = valueArgsValidate(stringCharAtArgs, args);
|
|
1646
|
+
if (index >= string.length) {
|
|
1647
|
+
throw new ValueArgsError('index', index);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
return string[index];
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
const stringCharAtArgs = valueArgsModel([
|
|
1654
|
+
{'name': 'string', 'type': 'string'},
|
|
1655
|
+
{'name': 'index', 'type': 'number', 'integer': true, 'gte': 0}
|
|
1656
|
+
]);
|
|
1657
|
+
|
|
1658
|
+
|
|
1638
1659
|
// $function: stringCharCodeAt
|
|
1639
1660
|
// $group: string
|
|
1640
1661
|
// $doc: Get a string index's character code
|
|
@@ -1730,7 +1751,7 @@ function stringFromCharCode(charCodes) {
|
|
|
1730
1751
|
// $return: The first index of the search string; -1 if not found.
|
|
1731
1752
|
function stringIndexOf(args) {
|
|
1732
1753
|
const [string, search, index] = valueArgsValidate(stringIndexOfArgs, args, -1);
|
|
1733
|
-
if (index
|
|
1754
|
+
if (index > string.length) {
|
|
1734
1755
|
throw new ValueArgsError('index', index, -1);
|
|
1735
1756
|
}
|
|
1736
1757
|
|
|
@@ -1754,7 +1775,7 @@ const stringIndexOfArgs = valueArgsModel([
|
|
|
1754
1775
|
function stringLastIndexOf(args) {
|
|
1755
1776
|
const [string, search, indexArg] = valueArgsValidate(stringLastIndexOfArgs, args, -1);
|
|
1756
1777
|
const index = indexArg !== null ? indexArg : string.length - 1;
|
|
1757
|
-
if (index
|
|
1778
|
+
if (index > string.length) {
|
|
1758
1779
|
throw new ValueArgsError('index', index, -1);
|
|
1759
1780
|
}
|
|
1760
1781
|
|
|
@@ -2320,6 +2341,7 @@ export const scriptFunctions = {
|
|
|
2320
2341
|
schemaTypeModel,
|
|
2321
2342
|
schemaValidate,
|
|
2322
2343
|
schemaValidateTypeModel,
|
|
2344
|
+
stringCharAt,
|
|
2323
2345
|
stringCharCodeAt,
|
|
2324
2346
|
stringDecode,
|
|
2325
2347
|
stringEncode,
|
package/lib/model.js
CHANGED
|
@@ -569,6 +569,8 @@ function isAsyncStatement(statement, globals, isAsyncScope) {
|
|
|
569
569
|
const [statementKey] = Object.keys(statement);
|
|
570
570
|
if (statementKey === 'expr') {
|
|
571
571
|
return isAsyncExpression(statement.expr.expr, globals, isAsyncScope);
|
|
572
|
+
} else if (statementKey === 'include') {
|
|
573
|
+
return true;
|
|
572
574
|
} else if (statementKey === 'jump') {
|
|
573
575
|
return 'expr' in statement.jump ? isAsyncExpression(statement.jump.expr, globals, isAsyncScope) : false;
|
|
574
576
|
} else if (statementKey === 'return') {
|