bare-script 5.0.0__tar.gz → 5.0.3__tar.gz
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.
- {bare_script-5.0.0/src/bare_script.egg-info → bare_script-5.0.3}/PKG-INFO +1 -1
- {bare_script-5.0.0 → bare_script-5.0.3}/setup.cfg +1 -1
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/include_source.py +251 -211
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/value.py +21 -3
- {bare_script-5.0.0 → bare_script-5.0.3/src/bare_script.egg-info}/PKG-INFO +1 -1
- {bare_script-5.0.0 → bare_script-5.0.3}/LICENSE +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/README.md +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/pyproject.toml +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/setup.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/__init__.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/__main__.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/bare.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/include.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/library.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/lint.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/model.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/options.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/parser.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/runtime.py +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script/runtime_c.c +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script.egg-info/SOURCES.txt +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script.egg-info/dependency_links.txt +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script.egg-info/entry_points.txt +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script.egg-info/requires.txt +0 -0
- {bare_script-5.0.0 → bare_script-5.0.3}/src/bare_script.egg-info/top_level.txt +0 -0
|
@@ -138,7 +138,7 @@ function argsURL(arguments, args, explicit, headerText, url):
|
|
|
138
138
|
default = objectGet(argument, \'default\')
|
|
139
139
|
|
|
140
140
|
# Add the argument name (for unknown argument check below)
|
|
141
|
-
objectSet(argNames, name,
|
|
141
|
+
objectSet(argNames, name, true)
|
|
142
142
|
|
|
143
143
|
# Add the URL variable, if any
|
|
144
144
|
value = null
|
|
@@ -168,7 +168,7 @@ function argsURL(arguments, args, explicit, headerText, url):
|
|
|
168
168
|
|
|
169
169
|
# Create the URL
|
|
170
170
|
return \'#\' + if(url != null, \'url=\' + urlEncodeComponent(url) + \'&\', \'\') + \\
|
|
171
|
-
if(
|
|
171
|
+
if(urlVars, arrayJoin(urlVars, \'&\'), \'var=\') + \\
|
|
172
172
|
if(headerText != null, \'&\' + if(headerText == argsTopHeaderId, argsTopHeaderId, markdownHeaderId(headerText)), \'\')
|
|
173
173
|
endfunction
|
|
174
174
|
|
|
@@ -265,14 +265,12 @@ function argsFormatValue(value, type):
|
|
|
265
265
|
return "\'" + datetimeISOFormat(value, true) + "\'"
|
|
266
266
|
elif type == \'datetime\':
|
|
267
267
|
return "\'" + datetimeISOFormat(value) + "\'"
|
|
268
|
-
elif type == \'float\':
|
|
269
|
-
return stringNew(value)
|
|
270
|
-
elif type == \'int\':
|
|
268
|
+
elif type == \'float\' || type == \'int\':
|
|
271
269
|
return stringNew(value)
|
|
272
270
|
endif
|
|
273
271
|
|
|
274
|
-
# type == \'string\'
|
|
275
|
-
return "\'" + value + "\'"
|
|
272
|
+
# type == \'string\' - escape backslashes and single-quotes for the URL argument expression
|
|
273
|
+
return "\'" + stringReplace(stringReplace(value, \'\\\\\', \'\\\\\\\\\'), "\'", "\\\\\'") + "\'"
|
|
276
274
|
endfunction
|
|
277
275
|
|
|
278
276
|
|
|
@@ -300,7 +298,8 @@ function argsValidateValue(value, type, global, warn):
|
|
|
300
298
|
value = datetimeISOParse(value)
|
|
301
299
|
valueType = systemType(value)
|
|
302
300
|
endif
|
|
303
|
-
if valueType != \'datetime\' || datetimeHour(value) != 0 || datetimeMinute(value) != 0 || datetimeSecond(value) != 0
|
|
301
|
+
if valueType != \'datetime\' || datetimeHour(value) != 0 || datetimeMinute(value) != 0 || datetimeSecond(value) != 0 || \\
|
|
302
|
+
datetimeMillisecond(value) != 0:
|
|
304
303
|
if warn != false:
|
|
305
304
|
systemLogDebug(\'args.bare: Invalid value \' + jsonStringify(valueOrig) + \' for URL argument "\' + global + \'"\')
|
|
306
305
|
endif
|
|
@@ -558,7 +557,7 @@ async function baredocSinglePage(config, args, sectionGroups, groups):
|
|
|
558
557
|
arrayPush(urls, url)
|
|
559
558
|
endif
|
|
560
559
|
endfor
|
|
561
|
-
texts = systemFetch(urls)
|
|
560
|
+
texts = if(urls, systemFetch(urls), [])
|
|
562
561
|
groupContent = {}
|
|
563
562
|
for groupName, ixName in contentNames:
|
|
564
563
|
text = arrayGet(texts, ixName)
|
|
@@ -837,11 +836,14 @@ baredocCLIMainArguments = argsValidate([ \\
|
|
|
837
836
|
function baredocCLIParse(functions, source, filename):
|
|
838
837
|
errors = []
|
|
839
838
|
|
|
839
|
+
# Note the previously-parsed functions - only functions added by this call are validated below
|
|
840
|
+
functionsPrev = objectCopy(functions)
|
|
841
|
+
|
|
840
842
|
# Process documentation comments line-by-line
|
|
841
843
|
func = null
|
|
842
844
|
for line, ixLine in stringSplitLines(source):
|
|
843
845
|
# function/group/doc/return documentation keywords?
|
|
844
|
-
matchKey = regexMatch(
|
|
846
|
+
matchKey = regexMatch(baredocCLIParseRegexKey, line)
|
|
845
847
|
if matchKey != null:
|
|
846
848
|
groupsKey = objectGet(matchKey, \'groups\')
|
|
847
849
|
key = objectGet(groupsKey, \'key\')
|
|
@@ -885,7 +887,7 @@ function baredocCLIParse(functions, source, filename):
|
|
|
885
887
|
arrayPush(errors, filename + \':\' + (ixLine + 1) + \': Invalid ignore value "\' + textTrim + \'"\')
|
|
886
888
|
continue
|
|
887
889
|
endif
|
|
888
|
-
objectSet(func, key,
|
|
890
|
+
objectSet(func, key, textTrim == \'true\')
|
|
889
891
|
|
|
890
892
|
else:
|
|
891
893
|
# key == \'function\'
|
|
@@ -907,7 +909,7 @@ function baredocCLIParse(functions, source, filename):
|
|
|
907
909
|
endif
|
|
908
910
|
|
|
909
911
|
# arg keyword?
|
|
910
|
-
matchArg = regexMatch(
|
|
912
|
+
matchArg = regexMatch(baredocCLIParseRegexArg, line)
|
|
911
913
|
if matchArg != null:
|
|
912
914
|
groupsArg = objectGet(matchArg, \'groups\')
|
|
913
915
|
name = objectGet(groupsArg, \'name\')
|
|
@@ -955,7 +957,7 @@ function baredocCLIParse(functions, source, filename):
|
|
|
955
957
|
endif
|
|
956
958
|
|
|
957
959
|
# Unknown documentation comment?
|
|
958
|
-
matchUnknown = regexMatch(
|
|
960
|
+
matchUnknown = regexMatch(baredocCLIParseRegexUnknown, line)
|
|
959
961
|
if matchUnknown != null:
|
|
960
962
|
groupsUnknown = objectGet(matchUnknown, \'groups\')
|
|
961
963
|
unknown = objectGet(groupsUnknown, \'unknown\')
|
|
@@ -964,15 +966,12 @@ function baredocCLIParse(functions, source, filename):
|
|
|
964
966
|
endif
|
|
965
967
|
endfor
|
|
966
968
|
|
|
967
|
-
#
|
|
968
|
-
libraryFunctions = []
|
|
969
|
+
# Validate the function documentation models added by this call
|
|
969
970
|
for funcName in arraySort(objectKeys(functions)):
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
for funcObj in libraryFunctions:
|
|
975
|
-
funcName = objectGet(funcObj, \'name\')
|
|
971
|
+
if objectHas(functionsPrev, funcName):
|
|
972
|
+
continue
|
|
973
|
+
endif
|
|
974
|
+
funcObj = objectGet(functions, funcName)
|
|
976
975
|
if !objectHas(funcObj, \'group\'):
|
|
977
976
|
arrayPush(errors, \'error: Function "\' + funcName + \'" missing group\')
|
|
978
977
|
endif
|
|
@@ -989,9 +988,9 @@ endfunction
|
|
|
989
988
|
|
|
990
989
|
|
|
991
990
|
# baredocCLIParse regular expressions
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
991
|
+
baredocCLIParseRegexKey = regexNew(\'^\\\\s*(?:\\\\/\\\\/|#)\\\\s*\\\\$(?<key>function|group|doc|return|ignore):\\\\s?(?<text>.*)$\')
|
|
992
|
+
baredocCLIParseRegexArg = regexNew(\'^\\\\s*(?:\\\\/\\\\/|#)\\\\s*\\\\$arg\\\\s+(?<name>[A-Za-z_][A-Za-z0-9_]*(?:\\\\.\\\\.\\\\.)?):\\\\s?(?<text>.*)$\')
|
|
993
|
+
baredocCLIParseRegexUnknown = regexNew(\'^\\\\s*(?:\\\\/\\\\/|#)\\\\s*\\\\$(?<unknown>[^:]+):\')
|
|
995
994
|
'''
|
|
996
995
|
|
|
997
996
|
|
|
@@ -1007,8 +1006,9 @@ include <schemaParser.bare>
|
|
|
1007
1006
|
# $function: dataParseCSV
|
|
1008
1007
|
# $group: data.bare
|
|
1009
1008
|
# $doc: Parse CSV text to a data array
|
|
1010
|
-
# $arg text: The CSV text
|
|
1011
|
-
# $return: The data array
|
|
1009
|
+
# $arg text: The CSV text or array of CSV text
|
|
1010
|
+
# $return: The data array. String values are parsed into typed values; if a value fails to parse as its
|
|
1011
|
+
# $return: column\'s type, a debug message is logged and the remaining column values are left as strings.
|
|
1012
1012
|
function dataParseCSV(text):
|
|
1013
1013
|
# Collect all lines from variadic text arguments
|
|
1014
1014
|
if systemType(text) == \'array\':
|
|
@@ -1039,7 +1039,9 @@ function dataParseCSV(text):
|
|
|
1039
1039
|
if mQuoted:
|
|
1040
1040
|
groups = objectGet(mQuoted, \'groups\')
|
|
1041
1041
|
arrayPush(row, stringReplace(objectGet(groups, \'1\'), \'""\', \'"\'))
|
|
1042
|
-
|
|
1042
|
+
|
|
1043
|
+
# Continue parsing fields only on a comma delimiter - at end-of-line the field is the last
|
|
1044
|
+
linePart = if(objectGet(groups, \'2\') == \',\', stringSlice(linePart, stringLength(objectGet(groups, \'0\'))))
|
|
1043
1045
|
continue
|
|
1044
1046
|
endif
|
|
1045
1047
|
|
|
@@ -1244,7 +1246,8 @@ endfunction
|
|
|
1244
1246
|
# $arg joinExpr: The join expression
|
|
1245
1247
|
# $arg rightExpr: Optional (default is null).
|
|
1246
1248
|
# $arg rightExpr: The right join expression
|
|
1247
|
-
# $arg isLeftJoin: Optional (default is false).
|
|
1249
|
+
# $arg isLeftJoin: Optional (default is false). By default, all left rows are included in the result.
|
|
1250
|
+
# $arg isLeftJoin: If true, left rows with no matching right row are excluded.
|
|
1248
1251
|
# $arg variables: Optional (default is null). A variables object for join expression evaluation.
|
|
1249
1252
|
# $return: The joined data array
|
|
1250
1253
|
function dataJoin(leftData, rightData, joinExpr, rightExpr, isLeftJoin, variables):
|
|
@@ -1252,32 +1255,30 @@ function dataJoin(leftData, rightData, joinExpr, rightExpr, isLeftJoin, variable
|
|
|
1252
1255
|
leftNames = {}
|
|
1253
1256
|
for leftRow in leftData:
|
|
1254
1257
|
for fieldName in objectKeys(leftRow):
|
|
1255
|
-
|
|
1256
|
-
objectSet(leftNames, fieldName, fieldName)
|
|
1257
|
-
endif
|
|
1258
|
+
objectSet(leftNames, fieldName, fieldName)
|
|
1258
1259
|
endfor
|
|
1259
1260
|
endfor
|
|
1260
1261
|
|
|
1261
1262
|
rightNamesRaw = {}
|
|
1262
1263
|
for rightRow in rightData:
|
|
1263
1264
|
for fieldName in objectKeys(rightRow):
|
|
1264
|
-
|
|
1265
|
-
objectSet(rightNamesRaw, fieldName, fieldName)
|
|
1266
|
-
endif
|
|
1265
|
+
objectSet(rightNamesRaw, fieldName, fieldName)
|
|
1267
1266
|
endfor
|
|
1268
1267
|
endfor
|
|
1269
1268
|
|
|
1270
1269
|
rightNames = {}
|
|
1270
|
+
rightNamesUsed = {}
|
|
1271
1271
|
for fieldName in objectKeys(rightNamesRaw):
|
|
1272
1272
|
if !objectHas(leftNames, fieldName):
|
|
1273
1273
|
objectSet(rightNames, fieldName, fieldName)
|
|
1274
1274
|
else:
|
|
1275
1275
|
ixUnique = 2
|
|
1276
1276
|
uniqueName = fieldName + ixUnique
|
|
1277
|
-
while objectHas(leftNames, uniqueName) || objectHas(
|
|
1277
|
+
while objectHas(leftNames, uniqueName) || objectHas(rightNamesUsed, uniqueName) || objectHas(rightNamesRaw, uniqueName):
|
|
1278
1278
|
ixUnique = ixUnique + 1
|
|
1279
1279
|
uniqueName = fieldName + ixUnique
|
|
1280
1280
|
endwhile
|
|
1281
|
+
objectSet(rightNamesUsed, uniqueName, uniqueName)
|
|
1281
1282
|
objectSet(rightNames, fieldName, uniqueName)
|
|
1282
1283
|
endif
|
|
1283
1284
|
endfor
|
|
@@ -1291,12 +1292,12 @@ function dataJoin(leftData, rightData, joinExpr, rightExpr, isLeftJoin, variable
|
|
|
1291
1292
|
for rightRow in rightData:
|
|
1292
1293
|
rightRowVars = if(variables != null, objectAssign(objectCopy(rightRow), variables), rightRow)
|
|
1293
1294
|
categoryKey = jsonStringify(barescriptEvaluateExpression(rightExprParsed, rightRowVars))
|
|
1294
|
-
|
|
1295
|
-
if
|
|
1296
|
-
|
|
1297
|
-
objectSet(rightCategoryRows, categoryKey,
|
|
1295
|
+
rightRows = objectGet(rightCategoryRows, categoryKey)
|
|
1296
|
+
if rightRows == null:
|
|
1297
|
+
rightRows = []
|
|
1298
|
+
objectSet(rightCategoryRows, categoryKey, rightRows)
|
|
1298
1299
|
endif
|
|
1299
|
-
arrayPush(
|
|
1300
|
+
arrayPush(rightRows, rightRow)
|
|
1300
1301
|
endfor
|
|
1301
1302
|
|
|
1302
1303
|
# Join the left with the right
|
|
@@ -1327,7 +1328,7 @@ endfunction
|
|
|
1327
1328
|
# $arg data: The data array
|
|
1328
1329
|
# $arg fieldName: The calculated field name
|
|
1329
1330
|
# $arg expr: The calculated field expression
|
|
1330
|
-
# $arg variables: Optional (default is null). A variables object the expression evaluation.
|
|
1331
|
+
# $arg variables: Optional (default is null). A variables object for the expression evaluation.
|
|
1331
1332
|
# $return: The updated data array
|
|
1332
1333
|
function dataCalculatedField(data, fieldName, expr, variables):
|
|
1333
1334
|
# Parse the calculation expression
|
|
@@ -1353,7 +1354,7 @@ endfunction
|
|
|
1353
1354
|
# $doc: Filter a data array
|
|
1354
1355
|
# $arg data: The data array
|
|
1355
1356
|
# $arg expr: The filter expression
|
|
1356
|
-
# $arg variables: Optional (default is null). A variables object the expression evaluation.
|
|
1357
|
+
# $arg variables: Optional (default is null). A variables object for the expression evaluation.
|
|
1357
1358
|
# $return: The filtered data array
|
|
1358
1359
|
function dataFilter(data, expr, variables):
|
|
1359
1360
|
result = []
|
|
@@ -1604,7 +1605,7 @@ function dataTop(data, count, categoryFields):
|
|
|
1604
1605
|
if categoryFields != null:
|
|
1605
1606
|
categoryValues = []
|
|
1606
1607
|
for field in categoryFields:
|
|
1607
|
-
arrayPush(categoryValues,
|
|
1608
|
+
arrayPush(categoryValues, objectGet(row, field))
|
|
1608
1609
|
endfor
|
|
1609
1610
|
categoryKey = jsonStringify(categoryValues)
|
|
1610
1611
|
else:
|
|
@@ -1621,18 +1622,18 @@ function dataTop(data, count, categoryFields):
|
|
|
1621
1622
|
endfor
|
|
1622
1623
|
|
|
1623
1624
|
# Take only the top rows per category
|
|
1624
|
-
|
|
1625
|
+
result = []
|
|
1625
1626
|
for categoryKey in categoryOrder:
|
|
1626
1627
|
categoryKeyRows = objectGet(categoryRows, categoryKey)
|
|
1627
1628
|
categoryKeyLength = arrayLength(categoryKeyRows)
|
|
1628
1629
|
ixRow = 0
|
|
1629
1630
|
while ixRow < count && ixRow < categoryKeyLength:
|
|
1630
|
-
arrayPush(
|
|
1631
|
+
arrayPush(result, arrayGet(categoryKeyRows, ixRow))
|
|
1631
1632
|
ixRow = ixRow + 1
|
|
1632
1633
|
endwhile
|
|
1633
1634
|
endfor
|
|
1634
1635
|
|
|
1635
|
-
return
|
|
1636
|
+
return result
|
|
1636
1637
|
endfunction
|
|
1637
1638
|
'''
|
|
1638
1639
|
|
|
@@ -1862,15 +1863,17 @@ function dataLineChartElements(data, lineChart, options):
|
|
|
1862
1863
|
colorValue = dataUtilFormatValue(objectGet(row, colorField), chartPrecision, chartDatetime)
|
|
1863
1864
|
for yField in yFields:
|
|
1864
1865
|
rowKey = if(isSingleY, colorValue, yField + \', \' + colorValue)
|
|
1865
|
-
objectSet(colorValueSet, rowKey, true)
|
|
1866
1866
|
yRow = objectGet(row, yField)
|
|
1867
1867
|
if xRow != null && yRow != null:
|
|
1868
|
+
objectSet(colorValueSet, rowKey, true)
|
|
1868
1869
|
rowPoints = objectGet(pointsMap, rowKey)
|
|
1869
1870
|
if rowPoints == null:
|
|
1870
1871
|
rowPoints = []
|
|
1871
1872
|
objectSet(pointsMap, rowKey, rowPoints)
|
|
1872
1873
|
endif
|
|
1873
1874
|
arrayPush(rowPoints, [xRow, yRow])
|
|
1875
|
+
|
|
1876
|
+
# The rows are sorted by the X field, so the first X value is the min and the last is the max
|
|
1874
1877
|
xMin = if(xMin == null, xRow, xMin)
|
|
1875
1878
|
yMin = if(yMin == null, yRow, if(yRow < yMin, yRow, yMin))
|
|
1876
1879
|
xMax = xRow
|
|
@@ -1896,10 +1899,9 @@ function dataLineChartElements(data, lineChart, options):
|
|
|
1896
1899
|
# Create a line for each y-field
|
|
1897
1900
|
for yField, ixField in yFields:
|
|
1898
1901
|
color = arrayGet(dataLineChartCategoricalColors, ixField % arrayLength(dataLineChartCategoricalColors))
|
|
1899
|
-
points = []
|
|
1900
|
-
arrayPush(linePoints, {\'label\': yField, \'color\': color, \'points\': points})
|
|
1901
1902
|
|
|
1902
|
-
# Add the points
|
|
1903
|
+
# Add the points - each y-field iterates the sorted rows separately, so compare in full for min/max
|
|
1904
|
+
points = []
|
|
1903
1905
|
for row in data:
|
|
1904
1906
|
xRow = objectGet(row, xField)
|
|
1905
1907
|
yRow = objectGet(row, yField)
|
|
@@ -1907,10 +1909,15 @@ function dataLineChartElements(data, lineChart, options):
|
|
|
1907
1909
|
arrayPush(points, [xRow, yRow])
|
|
1908
1910
|
xMin = if(xMin == null, xRow, if(xRow < xMin, xRow, xMin))
|
|
1909
1911
|
yMin = if(yMin == null, yRow, if(yRow < yMin, yRow, yMin))
|
|
1910
|
-
xMax = xRow
|
|
1912
|
+
xMax = if(xMax == null, xRow, if(xRow > xMax, xRow, xMax))
|
|
1911
1913
|
yMax = if(yMax == null, yRow, if(yRow > yMax, yRow, yMax))
|
|
1912
1914
|
endif
|
|
1913
1915
|
endfor
|
|
1916
|
+
|
|
1917
|
+
# Skip y-fields with no points
|
|
1918
|
+
if points:
|
|
1919
|
+
arrayPush(linePoints, {\'label\': yField, \'color\': color, \'points\': points})
|
|
1920
|
+
endif
|
|
1914
1921
|
endfor
|
|
1915
1922
|
endif
|
|
1916
1923
|
linePointsReversed = arrayReverse(arrayCopy(linePoints))
|
|
@@ -2614,20 +2621,30 @@ function dataTableMarkdown(data, model):
|
|
|
2614
2621
|
endfor
|
|
2615
2622
|
arrayExtend(fields, objectKeys(fieldNames))
|
|
2616
2623
|
endif
|
|
2617
|
-
if !
|
|
2624
|
+
if !fields:
|
|
2618
2625
|
return null
|
|
2619
2626
|
endif
|
|
2620
2627
|
|
|
2621
2628
|
# Get precision and formatting
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2629
|
+
formatDatetime = if(model != null, objectGet(model, \'datetime\'))
|
|
2630
|
+
formatPrecision = if(model != null, objectGet(model, \'precision\'))
|
|
2631
|
+
formatTrim = if(model != null, objectGet(model, \'trim\'))
|
|
2625
2632
|
formats = if(model != null, objectGet(model, \'formats\'))
|
|
2626
2633
|
|
|
2627
|
-
#
|
|
2634
|
+
# Precompute per-field rendering metadata, [field, align, header, isCategory], and
|
|
2635
|
+
# initialize widths to the field header width
|
|
2636
|
+
fieldMeta = []
|
|
2628
2637
|
widths = {}
|
|
2629
2638
|
for field in fields:
|
|
2630
|
-
|
|
2639
|
+
format = if(formats != null, objectGet(formats, field))
|
|
2640
|
+
header = if(format != null, objectGet(format, \'header\', field), field)
|
|
2641
|
+
arrayPush(fieldMeta, [ \\
|
|
2642
|
+
field, \\
|
|
2643
|
+
if(format != null, objectGet(format, \'align\')), \\
|
|
2644
|
+
header, \\
|
|
2645
|
+
modelCategories != null && arrayIndexOf(modelCategories, field) != -1 \\
|
|
2646
|
+
])
|
|
2647
|
+
objectSet(widths, field, stringLength(header))
|
|
2631
2648
|
endfor
|
|
2632
2649
|
|
|
2633
2650
|
# Compute the formatted field value strings and widths
|
|
@@ -2640,7 +2657,7 @@ function dataTableMarkdown(data, model):
|
|
|
2640
2657
|
value = objectGet(row, field)
|
|
2641
2658
|
valueType = systemType(value)
|
|
2642
2659
|
if valueType == \'number\' || valueType == \'datetime\':
|
|
2643
|
-
valueFormat = dataUtilFormatValue(value,
|
|
2660
|
+
valueFormat = dataUtilFormatValue(value, formatPrecision, formatDatetime, formatTrim)
|
|
2644
2661
|
else:
|
|
2645
2662
|
valueFormat = if(valueType == \'string\', stringTrim(value), stringNew(value))
|
|
2646
2663
|
valueFormat = regexReplace(dataTableMarkdownRegexMultipleNewline, valueFormat, \'<br><br>\')
|
|
@@ -2658,24 +2675,16 @@ function dataTableMarkdown(data, model):
|
|
|
2658
2675
|
endfor
|
|
2659
2676
|
endfor
|
|
2660
2677
|
|
|
2661
|
-
#
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
format = if(formats != null, objectGet(formats, field))
|
|
2665
|
-
arrayPush(fieldMeta, [ \\
|
|
2666
|
-
field, \\
|
|
2667
|
-
objectGet(widths, field), \\
|
|
2668
|
-
if(format != null, objectGet(format, \'align\')), \\
|
|
2669
|
-
if(format != null, objectGet(format, \'header\', field), field), \\
|
|
2670
|
-
modelCategories != null && arrayIndexOf(modelCategories, field) != -1 \\
|
|
2671
|
-
])
|
|
2678
|
+
# Add each field\'s computed width to its metadata, [field, align, header, isCategory, width]
|
|
2679
|
+
for meta in fieldMeta:
|
|
2680
|
+
arrayPush(meta, objectGet(widths, arrayGet(meta, 0)))
|
|
2672
2681
|
endfor
|
|
2673
2682
|
|
|
2674
2683
|
# Compute the field header separator
|
|
2675
2684
|
headerSeparator = \'\'
|
|
2676
2685
|
for meta in fieldMeta:
|
|
2677
|
-
|
|
2678
|
-
|
|
2686
|
+
align = arrayGet(meta, 1)
|
|
2687
|
+
width = arrayGet(meta, 4)
|
|
2679
2688
|
alignLeft = if(align == \'center\', \':\', \'-\')
|
|
2680
2689
|
alignRight = if(align == \'center\' || align == \'right\', \':\', \'-\')
|
|
2681
2690
|
headerSeparator = headerSeparator + \'|\' + alignLeft + dataTableMarkdownField(\'\', width, align, \'-\') + alignRight
|
|
@@ -2685,9 +2694,9 @@ function dataTableMarkdown(data, model):
|
|
|
2685
2694
|
# Compute the table header fields
|
|
2686
2695
|
headerFields = \'\'
|
|
2687
2696
|
for meta in fieldMeta:
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2697
|
+
align = arrayGet(meta, 1)
|
|
2698
|
+
header = arrayGet(meta, 2)
|
|
2699
|
+
width = arrayGet(meta, 4)
|
|
2691
2700
|
headerFields = headerFields + \'| \' + dataTableMarkdownField(header, width, align, \' \') + \' \'
|
|
2692
2701
|
endfor
|
|
2693
2702
|
headerFields = headerFields + \'|\'
|
|
@@ -2705,12 +2714,12 @@ function dataTableMarkdown(data, model):
|
|
|
2705
2714
|
parts = []
|
|
2706
2715
|
for meta in fieldMeta:
|
|
2707
2716
|
field = arrayGet(meta, 0)
|
|
2708
|
-
|
|
2709
|
-
|
|
2717
|
+
align = arrayGet(meta, 1)
|
|
2718
|
+
width = arrayGet(meta, 4)
|
|
2710
2719
|
|
|
2711
2720
|
# Skip this value? (blank a category cell matching the previous row)
|
|
2712
2721
|
if skip:
|
|
2713
|
-
isCategory = arrayGet(meta,
|
|
2722
|
+
isCategory = arrayGet(meta, 3)
|
|
2714
2723
|
skip = isCategory && systemCompare(objectGet(rowRaw, field), objectGet(rowPrevRaw, field)) == 0
|
|
2715
2724
|
endif
|
|
2716
2725
|
|
|
@@ -2807,7 +2816,7 @@ function dataTableElements(data, dataTable):
|
|
|
2807
2816
|
isCategory, \\
|
|
2808
2817
|
dataTableElementsFieldAttr(fieldFormat), \\
|
|
2809
2818
|
fieldFormat != null && objectGet(fieldFormat, \'markdown\'), \\
|
|
2810
|
-
if(fieldFormat != null, objectGet(fieldFormat, \'header\')
|
|
2819
|
+
if(fieldFormat != null, objectGet(fieldFormat, \'header\', field), field) \\
|
|
2811
2820
|
])
|
|
2812
2821
|
endfor
|
|
2813
2822
|
endfor
|
|
@@ -2842,14 +2851,12 @@ function dataTableElements(data, dataTable):
|
|
|
2842
2851
|
skip = isCategory && systemCompare(value, objectGet(rowPrev, field)) == 0
|
|
2843
2852
|
endif
|
|
2844
2853
|
|
|
2845
|
-
fieldElements = if(isMarkdown, \\
|
|
2846
|
-
markdownElements(markdownParse(stringNew(value))), \\
|
|
2847
|
-
{\'text\': dataUtilFormatValue(value, formatPrecision, formatDatetime, formatTrim)} \\
|
|
2848
|
-
)
|
|
2849
2854
|
arrayPush(fieldRowElements, { \\
|
|
2850
2855
|
\'html\': \'td\', \\
|
|
2851
2856
|
\'attr\': fieldAttr, \\
|
|
2852
|
-
\'elem\': if(skip, null,
|
|
2857
|
+
\'elem\': if(skip, null, if(isMarkdown, \\
|
|
2858
|
+
markdownElements(markdownParse(stringNew(value))), \\
|
|
2859
|
+
{\'text\': dataUtilFormatValue(value, formatPrecision, formatDatetime, formatTrim)})) \\
|
|
2853
2860
|
})
|
|
2854
2861
|
endfor
|
|
2855
2862
|
arrayPush(resultRows, {\'html\': \'tr\', \'elem\': fieldRowElements})
|
|
@@ -2994,17 +3001,13 @@ function diffLines(left, right):
|
|
|
2994
3001
|
leftLength = arrayLength(leftLines)
|
|
2995
3002
|
rightLength = arrayLength(rightLines)
|
|
2996
3003
|
while ixLeft < leftLength || ixRight < rightLength:
|
|
2997
|
-
# If we\'ve run out of lines on either side
|
|
3004
|
+
# If we\'ve run out of lines on either side (the loop condition ensures lines remain on the other)
|
|
2998
3005
|
if ixLeft >= leftLength:
|
|
2999
|
-
|
|
3000
|
-
arrayPush(diffs, {\'type\': \'Add\', \'lines\': arraySlice(rightLines, ixRight)})
|
|
3001
|
-
endif
|
|
3006
|
+
arrayPush(diffs, {\'type\': \'Add\', \'lines\': arraySlice(rightLines, ixRight)})
|
|
3002
3007
|
break
|
|
3003
3008
|
endif
|
|
3004
3009
|
if ixRight >= rightLength:
|
|
3005
|
-
|
|
3006
|
-
arrayPush(diffs, {\'type\': \'Remove\', \'lines\': arraySlice(leftLines, ixLeft)})
|
|
3007
|
-
endif
|
|
3010
|
+
arrayPush(diffs, {\'type\': \'Remove\', \'lines\': arraySlice(leftLines, ixLeft)})
|
|
3008
3011
|
break
|
|
3009
3012
|
endif
|
|
3010
3013
|
|
|
@@ -3036,16 +3039,12 @@ function diffLines(left, right):
|
|
|
3036
3039
|
ixLeftTmp = ixLeftTmp + 1
|
|
3037
3040
|
endwhile
|
|
3038
3041
|
|
|
3039
|
-
# If no match found, use remaining lines
|
|
3042
|
+
# If no match found, use remaining lines (lines remain on both sides or we\'d have broken above)
|
|
3040
3043
|
if matchIxLeft == null:
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
if ixRight < rightLength:
|
|
3046
|
-
arrayPush(diffs, {\'type\': \'Add\', \'lines\': arraySlice(rightLines, ixRight)})
|
|
3047
|
-
ixRight = rightLength
|
|
3048
|
-
endif
|
|
3044
|
+
arrayPush(diffs, {\'type\': \'Remove\', \'lines\': arraySlice(leftLines, ixLeft)})
|
|
3045
|
+
arrayPush(diffs, {\'type\': \'Add\', \'lines\': arraySlice(rightLines, ixRight)})
|
|
3046
|
+
ixLeft = leftLength
|
|
3047
|
+
ixRight = rightLength
|
|
3049
3048
|
continue
|
|
3050
3049
|
endif
|
|
3051
3050
|
|
|
@@ -3359,7 +3358,7 @@ endfunction
|
|
|
3359
3358
|
# $function: drawOnClick
|
|
3360
3359
|
# $group: draw.bare
|
|
3361
3360
|
# $doc: Set the most recent drawing object\'s on-click event handler
|
|
3362
|
-
# $arg callback: The on-click event callback function (x, y)
|
|
3361
|
+
# $arg callback: The on-click event callback function (x, y, width, height)
|
|
3363
3362
|
function drawOnClick(callback):
|
|
3364
3363
|
drawing = drawGetCurrentDrawing()
|
|
3365
3364
|
svg = objectGet(drawing, \'svg\')
|
|
@@ -3429,13 +3428,19 @@ endfunction
|
|
|
3429
3428
|
# $arg strokeDashArray: [dash array](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray#usage_notes).
|
|
3430
3429
|
function drawStyle(stroke, strokeWidth, fill, strokeDashArray):
|
|
3431
3430
|
drawing = drawGetCurrentDrawing()
|
|
3431
|
+
|
|
3432
|
+
# Apply the style defaults and compare with the current styles - an unchanged style continues the current path
|
|
3433
|
+
stroke = if(stroke != null, stroke, \'black\')
|
|
3434
|
+
strokeWidth = if(strokeWidth != null, strokeWidth, 1)
|
|
3435
|
+
fill = if(fill != null, fill, \'none\')
|
|
3436
|
+
strokeDashArray = if(strokeDashArray != null, strokeDashArray, \'none\')
|
|
3432
3437
|
if stroke != objectGet(drawing, \'stroke\') || strokeWidth != objectGet(drawing, \'strokeWidth\') || \\
|
|
3433
3438
|
strokeDashArray != objectGet(drawing, \'strokeDashArray\') || fill != objectGet(drawing, \'fill\'):
|
|
3434
3439
|
drawFinishDrawingPath(drawing)
|
|
3435
|
-
objectSet(drawing, \'stroke\',
|
|
3436
|
-
objectSet(drawing, \'strokeWidth\',
|
|
3437
|
-
objectSet(drawing, \'strokeDashArray\',
|
|
3438
|
-
objectSet(drawing, \'fill\',
|
|
3440
|
+
objectSet(drawing, \'stroke\', stroke)
|
|
3441
|
+
objectSet(drawing, \'strokeWidth\', strokeWidth)
|
|
3442
|
+
objectSet(drawing, \'strokeDashArray\', strokeDashArray)
|
|
3443
|
+
objectSet(drawing, \'fill\', fill)
|
|
3439
3444
|
endif
|
|
3440
3445
|
endfunction
|
|
3441
3446
|
|
|
@@ -3639,10 +3644,22 @@ function elementModelValidateEx(elements):
|
|
|
3639
3644
|
endif
|
|
3640
3645
|
endif
|
|
3641
3646
|
|
|
3642
|
-
# Validate
|
|
3647
|
+
# Validate the element callback - a map of event name to event callback function
|
|
3643
3648
|
callback = objectGet(elements, \'callback\')
|
|
3644
|
-
if callback != null
|
|
3645
|
-
|
|
3649
|
+
if callback != null:
|
|
3650
|
+
callbackType = systemType(callback)
|
|
3651
|
+
if callbackType != \'object\':
|
|
3652
|
+
return {\'error\': \'Invalid element callback function \' + elementModelTruncate(jsonStringify(callback)) + \' (type "\' + callbackType + \'")\'}
|
|
3653
|
+
endif
|
|
3654
|
+
|
|
3655
|
+
# Validate the map\'s event callback functions
|
|
3656
|
+
for callbackEvent in objectKeys(callback):
|
|
3657
|
+
eventCallback = objectGet(callback, callbackEvent)
|
|
3658
|
+
if systemType(eventCallback) != \'function\':
|
|
3659
|
+
return {\'error\': \'Invalid element event callback function \' + elementModelTruncate(jsonStringify(eventCallback)) + \\
|
|
3660
|
+
\' (type "\' + systemType(eventCallback) + \'")\'}
|
|
3661
|
+
endif
|
|
3662
|
+
endfor
|
|
3646
3663
|
endif
|
|
3647
3664
|
|
|
3648
3665
|
return {\'result\': elements}
|
|
@@ -3714,9 +3731,8 @@ function elementModelToStringHelper(elements, indentStr, level):
|
|
|
3714
3731
|
attrStr = \'\'
|
|
3715
3732
|
attr = objectGet(elements, \'attr\')
|
|
3716
3733
|
if tag == \'svg\':
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
endif
|
|
3734
|
+
# Add the SVG namespace attribute (copy the attributes - the caller\'s model is not modified)
|
|
3735
|
+
attr = if(attr != null, objectCopy(attr), {})
|
|
3720
3736
|
objectSet(attr, \'xmlns\', \'http://www.w3.org/2000/svg\')
|
|
3721
3737
|
endif
|
|
3722
3738
|
if attr != null:
|
|
@@ -3914,7 +3930,7 @@ function markdownParserGetSpanText(span):
|
|
|
3914
3930
|
endfor
|
|
3915
3931
|
return arrayJoin(parts, \'\')
|
|
3916
3932
|
endif
|
|
3917
|
-
return objectGet(span, \'text\')
|
|
3933
|
+
return objectGet(span, \'text\', \'\')
|
|
3918
3934
|
endfunction
|
|
3919
3935
|
|
|
3920
3936
|
|
|
@@ -5664,7 +5680,6 @@ INCLUDE_SOURCE_MARKDOWN_PARSER = '''\
|
|
|
5664
5680
|
|
|
5665
5681
|
|
|
5666
5682
|
# Markdown regex
|
|
5667
|
-
markdownParserRegexLineSplit = regexNew(\'\\r?\\n\')
|
|
5668
5683
|
markdownParserRegexParagraphEmpty = regexNew(\'^\\\\s*$\')
|
|
5669
5684
|
markdownParserRegexIndent = regexNew(\'^(?<indent> *)(?<notIndent>.*)$\')
|
|
5670
5685
|
markdownParserRegexHeading = regexNew(\'^ {0,3}(?<heading>#{1,6})\\\\s+(?<text>.*?)(?:\\\\s+#+)?\\\\s*$\')
|
|
@@ -5828,9 +5843,6 @@ function markdownParseInternal(markdown, startLineNumber, linkRefsRaw):
|
|
|
5828
5843
|
markdownParserCloseParagraph( \\
|
|
5829
5844
|
markdownParts, paragraphLines, paragraphPart, paragraphLineNumber, fencedMark, linkRefs, null \\
|
|
5830
5845
|
)
|
|
5831
|
-
paragraphPart = null
|
|
5832
|
-
paragraphLineNumber = null
|
|
5833
|
-
tablePart = null
|
|
5834
5846
|
matchHeadingGroups = objectGet(matchHeading, \'groups\')
|
|
5835
5847
|
paragraphLines = [objectGet(matchHeadingGroups, \'text\')]
|
|
5836
5848
|
headingStyle = \'h\' + stringLength(objectGet(matchHeadingGroups, \'heading\'))
|
|
@@ -6085,7 +6097,7 @@ function markdownParserCloseParagraph(markdownParts, paragraphLines, paragraphPa
|
|
|
6085
6097
|
endif
|
|
6086
6098
|
|
|
6087
6099
|
# Check for more link reference definitions
|
|
6088
|
-
text = stringSlice(text, stringLength(objectGet(
|
|
6100
|
+
text = stringSlice(text, stringLength(objectGet(matchLinkDefGroups, \'0\')))
|
|
6089
6101
|
matchLinkDef = regexMatch(markdownParserRegexLinkDef, text)
|
|
6090
6102
|
endwhile
|
|
6091
6103
|
|
|
@@ -6381,7 +6393,8 @@ function markdownParserCreateLinkRefSpan(refText, optText, fullText, linkRefs, t
|
|
|
6381
6393
|
arrayPush(objectGet(linkRefs, \'links\'), { \\
|
|
6382
6394
|
\'refKey\': markdownParserGetLinkRefKey(refText), \\
|
|
6383
6395
|
\'linkSpan\': {\'link\': { \\
|
|
6384
|
-
\'spans\': if(systemType(optText) == \'array\', optText,
|
|
6396
|
+
\'spans\': if(systemType(optText) == \'array\', optText, \\
|
|
6397
|
+
markdownParserParagraphSpans(if(optText != null, optText, refText), linkRefs)) \\
|
|
6385
6398
|
}}, \\
|
|
6386
6399
|
\'linkRefSpan\': linkRefSpan \\
|
|
6387
6400
|
})
|
|
@@ -6394,7 +6407,7 @@ function markdownParserCreateImageRefSpan(refText, optText, fullText, linkRefs,
|
|
|
6394
6407
|
linkRefSpan = {\'linkRef\': {\'spans\': markdownParserCreateFallbackSpan(fullText, linkRefs, textFallback)}}
|
|
6395
6408
|
arrayPush(objectGet(linkRefs, \'links\'), { \\
|
|
6396
6409
|
\'refKey\': markdownParserGetLinkRefKey(refText), \\
|
|
6397
|
-
\'imageSpan\': {\'image\': {\'alt\': markdownParserGetImageAltText(optText
|
|
6410
|
+
\'imageSpan\': {\'image\': {\'alt\': markdownParserGetImageAltText(if(optText != null, optText, refText))}}, \\
|
|
6398
6411
|
\'linkRefSpan\': linkRefSpan \\
|
|
6399
6412
|
})
|
|
6400
6413
|
return linkRefSpan
|
|
@@ -6844,7 +6857,7 @@ endfunction
|
|
|
6844
6857
|
# $arg text: The text to write
|
|
6845
6858
|
# $arg type: The clipboard content type (default is "text/plain")
|
|
6846
6859
|
function windowClipboardWrite(text):
|
|
6847
|
-
|
|
6860
|
+
objectSet(markdownUpState, \'windowClipboard\', text)
|
|
6848
6861
|
endfunction
|
|
6849
6862
|
|
|
6850
6863
|
|
|
@@ -6931,6 +6944,7 @@ endfunction
|
|
|
6931
6944
|
# $arg contentType: Optional (default is "text/plain"). The object content type.
|
|
6932
6945
|
# $return: The object URL string
|
|
6933
6946
|
function windowURLObject(data, contentType):
|
|
6947
|
+
contentType = if(contentType != null, contentType, \'text/plain\')
|
|
6934
6948
|
return \'blob:\' + urlEncode(contentType) + \'-\' + urlEncode(if(stringLength(data) < 20, data, stringSlice(data, 0, 20)))
|
|
6935
6949
|
endfunction
|
|
6936
6950
|
|
|
@@ -7067,17 +7081,16 @@ async function pagerMain(pagerModel, options):
|
|
|
7067
7081
|
for page, ixPage in pages:
|
|
7068
7082
|
pageName = objectGet(page, \'name\')
|
|
7069
7083
|
pageHidden = objectGet(page, \'hidden\')
|
|
7070
|
-
|
|
7071
|
-
pageNavigable = pageTypeKey != \'link\'
|
|
7084
|
+
pageNavigable = !objectHas(objectGet(page, \'type\'), \'link\')
|
|
7072
7085
|
|
|
7073
7086
|
# Start page?
|
|
7074
|
-
if
|
|
7087
|
+
if !startPageExplicit && pageName == optionStart:
|
|
7075
7088
|
startPageName = pageName
|
|
7076
7089
|
startPageIndex = ixPage
|
|
7077
|
-
|
|
7090
|
+
startPageExplicit = true
|
|
7091
|
+
elif pageNavigable && !pageHidden && startPageName == null:
|
|
7078
7092
|
startPageName = pageName
|
|
7079
7093
|
startPageIndex = ixPage
|
|
7080
|
-
startPageExplicit = true
|
|
7081
7094
|
endif
|
|
7082
7095
|
|
|
7083
7096
|
# Update the end page
|
|
@@ -7112,8 +7125,7 @@ async function pagerMain(pagerModel, options):
|
|
|
7112
7125
|
curPageIndex = startPageIndex
|
|
7113
7126
|
for page, ixPage in pages:
|
|
7114
7127
|
pageName = objectGet(page, \'name\')
|
|
7115
|
-
|
|
7116
|
-
pageNavigable = pageTypeKey != \'link\'
|
|
7128
|
+
pageNavigable = !objectHas(objectGet(page, \'type\'), \'link\')
|
|
7117
7129
|
|
|
7118
7130
|
# Current page?
|
|
7119
7131
|
if pageNavigable && pageName == argPage:
|
|
@@ -7131,8 +7143,7 @@ async function pagerMain(pagerModel, options):
|
|
|
7131
7143
|
page = arrayGet(pages, ixPage)
|
|
7132
7144
|
pageName = objectGet(page, \'name\')
|
|
7133
7145
|
pageHidden = objectGet(page, \'hidden\')
|
|
7134
|
-
|
|
7135
|
-
pageNavigable = pageTypeKey != \'link\'
|
|
7146
|
+
pageNavigable = !objectHas(objectGet(page, \'type\'), \'link\')
|
|
7136
7147
|
if !pageHidden && pageNavigable:
|
|
7137
7148
|
nextPageName = pageName
|
|
7138
7149
|
break
|
|
@@ -7147,8 +7158,7 @@ async function pagerMain(pagerModel, options):
|
|
|
7147
7158
|
page = arrayGet(pages, ixPage)
|
|
7148
7159
|
pageName = objectGet(page, \'name\')
|
|
7149
7160
|
pageHidden = objectGet(page, \'hidden\')
|
|
7150
|
-
|
|
7151
|
-
pageNavigable = pageTypeKey != \'link\'
|
|
7161
|
+
pageNavigable = !objectHas(objectGet(page, \'type\'), \'link\')
|
|
7152
7162
|
if !pageHidden && pageNavigable:
|
|
7153
7163
|
prevPageName = pageName
|
|
7154
7164
|
break
|
|
@@ -7163,8 +7173,7 @@ async function pagerMain(pagerModel, options):
|
|
|
7163
7173
|
pageName = objectGet(page, \'name\')
|
|
7164
7174
|
pageHidden = objectGet(page, \'hidden\')
|
|
7165
7175
|
pageType = objectGet(page, \'type\')
|
|
7166
|
-
|
|
7167
|
-
pageNavigable = pageTypeKey != \'link\'
|
|
7176
|
+
pageLink = objectGet(pageType, \'link\')
|
|
7168
7177
|
|
|
7169
7178
|
# Hidden page?
|
|
7170
7179
|
if pageHidden:
|
|
@@ -7172,10 +7181,10 @@ async function pagerMain(pagerModel, options):
|
|
|
7172
7181
|
endif
|
|
7173
7182
|
|
|
7174
7183
|
# Render the menu link
|
|
7175
|
-
if
|
|
7176
|
-
pageLinkURL = objectGet(
|
|
7184
|
+
if pageLink != null:
|
|
7185
|
+
pageLinkURL = objectGet(pageLink, \'url\')
|
|
7177
7186
|
arrayPush(menuItems, \'[\' + markdownEscape(pageName) + \'](\' + urlEncode(pageLinkURL) + \')\')
|
|
7178
|
-
elif pageName ==
|
|
7187
|
+
elif pageName == curPageName:
|
|
7179
7188
|
arrayPush(menuItems, markdownEscape(pageName))
|
|
7180
7189
|
else:
|
|
7181
7190
|
arrayPush(menuItems, argsLink(arguments, pageName, {\'page\': pageName}))
|
|
@@ -7198,8 +7207,7 @@ async function pagerMain(pagerModel, options):
|
|
|
7198
7207
|
|
|
7199
7208
|
# Function page?
|
|
7200
7209
|
curPageType = objectGet(curPage, \'type\')
|
|
7201
|
-
|
|
7202
|
-
if curPageTypeKey == \'function\':
|
|
7210
|
+
if objectHas(curPageType, \'function\'):
|
|
7203
7211
|
# Set the title
|
|
7204
7212
|
title = objectGet(objectGet(curPageType, \'function\'), \'title\')
|
|
7205
7213
|
if title != null:
|
|
@@ -7210,7 +7218,7 @@ async function pagerMain(pagerModel, options):
|
|
|
7210
7218
|
# Call the page function
|
|
7211
7219
|
pageFn = objectGet(objectGet(curPageType, \'function\'), \'function\')
|
|
7212
7220
|
pageFn(args)
|
|
7213
|
-
elif
|
|
7221
|
+
elif objectHas(curPageType, \'markdown\'):
|
|
7214
7222
|
# Fetch the Markdown text
|
|
7215
7223
|
url = objectGet(objectGet(curPageType, \'markdown\'), \'url\')
|
|
7216
7224
|
markdownText = systemFetch(url)
|
|
@@ -7275,6 +7283,9 @@ include <draw.bare>
|
|
|
7275
7283
|
function qrcodeDraw(message, x, y, size, level):
|
|
7276
7284
|
# Get the QR code pixel matrix
|
|
7277
7285
|
matrix = if(systemType(message) == \'string\', qrcodeMatrix(message, level), message)
|
|
7286
|
+
if matrix == null:
|
|
7287
|
+
return
|
|
7288
|
+
endif
|
|
7278
7289
|
qrcodeSize = arrayLength(matrix)
|
|
7279
7290
|
|
|
7280
7291
|
# Draw the white background
|
|
@@ -7315,6 +7326,9 @@ endfunction
|
|
|
7315
7326
|
function qrcodeElements(message, size, level):
|
|
7316
7327
|
# Get the QR code pixel matrix
|
|
7317
7328
|
matrix = if(systemType(message) == \'string\', qrcodeMatrix(message, level), message)
|
|
7329
|
+
if matrix == null:
|
|
7330
|
+
return null
|
|
7331
|
+
endif
|
|
7318
7332
|
qrcodeSize = arrayLength(matrix)
|
|
7319
7333
|
|
|
7320
7334
|
# Compute the black pixel paths
|
|
@@ -7722,15 +7736,15 @@ function qrcodeDataBits(qrcode, level, message):
|
|
|
7722
7736
|
# Compute the message bits
|
|
7723
7737
|
messageBits = qrcodeMessageBits(qrcode, level, message)
|
|
7724
7738
|
|
|
7725
|
-
# Padding bits
|
|
7739
|
+
# Padding bits - the pad bytes 236 and 17 alternate
|
|
7726
7740
|
paddingBits = []
|
|
7727
7741
|
remainingBitCount = totalMessageCodewords * 8 - arrayLength(messageBits)
|
|
7728
7742
|
byteCount = mathFloor(remainingBitCount / 8)
|
|
7743
|
+
padBits236 = qrcodeBytesToBits([236])
|
|
7744
|
+
padBits17 = qrcodeBytesToBits([17])
|
|
7729
7745
|
ix = 0
|
|
7730
7746
|
while ix < byteCount:
|
|
7731
|
-
|
|
7732
|
-
padBits = qrcodeBytesToBits([padValue])
|
|
7733
|
-
arrayExtend(paddingBits, padBits)
|
|
7747
|
+
arrayExtend(paddingBits, if(ix % 2, padBits17, padBits236))
|
|
7734
7748
|
ix = ix + 1
|
|
7735
7749
|
endwhile
|
|
7736
7750
|
|
|
@@ -7844,7 +7858,7 @@ endfunction
|
|
|
7844
7858
|
|
|
7845
7859
|
# Mode determination regex
|
|
7846
7860
|
qrcodeModeNumericRegex = regexNew(\'^[0-9]+$\')
|
|
7847
|
-
qrcodeModeAlphanumericRegex = regexNew(\'^[0-9A-Z
|
|
7861
|
+
qrcodeModeAlphanumericRegex = regexNew(\'^[0-9A-Z $%*+\\\\-./:]+$\')
|
|
7848
7862
|
qrcodeModeAlphanumericStr = \'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:\'
|
|
7849
7863
|
|
|
7850
7864
|
|
|
@@ -8915,27 +8929,27 @@ async function schemaDocMain(url, title, hideNoGroup):
|
|
|
8915
8929
|
markdownPrint(\'\', argsLink(schemaDocArguments, if(single, \'Multi Page\', \'Single Page\'), {\'single\': !single}))
|
|
8916
8930
|
endif
|
|
8917
8931
|
|
|
8918
|
-
# Group the
|
|
8932
|
+
# Group the type names
|
|
8919
8933
|
groups = {}
|
|
8920
8934
|
typeNames = arraySort(objectKeys(types))
|
|
8921
|
-
typeGroups = {\'action\': \'Actions\', \'enum\': \'Enums\', \'struct\': \'Structs\', \'typedef\': \'Typedefs\'}
|
|
8922
8935
|
for typeName in typeNames:
|
|
8923
8936
|
type = objectGet(types, typeName)
|
|
8924
|
-
|
|
8937
|
+
typeKey = arrayGet(objectKeys(type), 0)
|
|
8938
|
+
group = objectGet(objectGet(type, typeKey), \'docGroup\')
|
|
8925
8939
|
|
|
8926
8940
|
# No group? Use the type\'s default group.
|
|
8927
8941
|
if group == null:
|
|
8928
8942
|
if hideNoGroup:
|
|
8929
8943
|
continue
|
|
8930
8944
|
endif
|
|
8931
|
-
group = objectGet(
|
|
8945
|
+
group = objectGet(schemaDocTypeGroups, typeKey)
|
|
8932
8946
|
endif
|
|
8933
8947
|
|
|
8934
|
-
# Add the type to the group
|
|
8948
|
+
# Add the type name to the group
|
|
8935
8949
|
if !objectHas(groups, group):
|
|
8936
8950
|
objectSet(groups, group, [])
|
|
8937
8951
|
endif
|
|
8938
|
-
arrayPush(objectGet(groups, group),
|
|
8952
|
+
arrayPush(objectGet(groups, group), typeName)
|
|
8939
8953
|
endfor
|
|
8940
8954
|
groupNames = arraySort(objectKeys(groups))
|
|
8941
8955
|
|
|
@@ -8958,9 +8972,7 @@ async function schemaDocMain(url, title, hideNoGroup):
|
|
|
8958
8972
|
endif
|
|
8959
8973
|
|
|
8960
8974
|
# Render the group type links
|
|
8961
|
-
|
|
8962
|
-
for groupType in groupTypes:
|
|
8963
|
-
groupTypeName = objectGet(objectGet(groupType, arrayGet(objectKeys(groupType), 0)), \'name\')
|
|
8975
|
+
for groupTypeName in objectGet(groups, groupName):
|
|
8964
8976
|
if single:
|
|
8965
8977
|
markdownPrint(\'\', schemaDocMarkdown(types, groupTypeName, {\'headerPrefix\': \'###\', \'hideReferenced\': true}))
|
|
8966
8978
|
else:
|
|
@@ -8971,6 +8983,10 @@ async function schemaDocMain(url, title, hideNoGroup):
|
|
|
8971
8983
|
endfunction
|
|
8972
8984
|
|
|
8973
8985
|
|
|
8986
|
+
# The map of type key to default documentation group name
|
|
8987
|
+
schemaDocTypeGroups = {\'action\': \'Actions\', \'enum\': \'Enums\', \'struct\': \'Structs\', \'typedef\': \'Typedefs\'}
|
|
8988
|
+
|
|
8989
|
+
|
|
8974
8990
|
# The Schema Markdown documentation viewer arguments
|
|
8975
8991
|
schemaDocArguments = argsValidate([ \\
|
|
8976
8992
|
{\'name\': \'name\'}, \\
|
|
@@ -9003,31 +9019,31 @@ function schemaDocMarkdown(types, typeName, options):
|
|
|
9003
9019
|
userType = objectGet(types, typeName)
|
|
9004
9020
|
action = objectGet(userType, \'action\')
|
|
9005
9021
|
|
|
9006
|
-
# Compute the referenced types
|
|
9007
|
-
referencedTypes = schemaGetReferencedTypes(types, typeName)
|
|
9008
|
-
if action != null:
|
|
9009
|
-
typesFilter = [ \\
|
|
9010
|
-
typeName, objectGet(action, \'path\'), objectGet(action, \'query\'), objectGet(action, \'input\'), \\
|
|
9011
|
-
objectGet(action, \'output\'), objectGet(action, \'errors\') \\
|
|
9012
|
-
]
|
|
9013
|
-
else:
|
|
9014
|
-
typesFilter = [typeName]
|
|
9015
|
-
endif
|
|
9016
|
-
|
|
9017
|
-
# Filter and sort referenced types
|
|
9022
|
+
# Compute, filter, and sort the referenced types (not needed when referenced types are hidden)
|
|
9018
9023
|
filteredTypeNames = []
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9024
|
+
if !hideReferenced:
|
|
9025
|
+
referencedTypes = schemaGetReferencedTypes(types, typeName)
|
|
9026
|
+
if action != null:
|
|
9027
|
+
typesFilter = [ \\
|
|
9028
|
+
typeName, objectGet(action, \'path\'), objectGet(action, \'query\'), objectGet(action, \'input\'), \\
|
|
9029
|
+
objectGet(action, \'output\'), objectGet(action, \'errors\') \\
|
|
9030
|
+
]
|
|
9031
|
+
else:
|
|
9032
|
+
typesFilter = [typeName]
|
|
9022
9033
|
endif
|
|
9023
|
-
|
|
9034
|
+
for refTypeName in arraySort(objectKeys(referencedTypes)):
|
|
9035
|
+
if arrayIndexOf(typesFilter, refTypeName) == -1:
|
|
9036
|
+
arrayPush(filteredTypeNames, refTypeName)
|
|
9037
|
+
endif
|
|
9038
|
+
endfor
|
|
9039
|
+
endif
|
|
9024
9040
|
|
|
9025
9041
|
# The user type documentation
|
|
9026
9042
|
lines = []
|
|
9027
9043
|
arrayExtend(lines, schemaDocUserTypeMarkdown(types, typeName, options, headerPrefix))
|
|
9028
9044
|
|
|
9029
9045
|
# Referenced type documentation
|
|
9030
|
-
if filteredTypeNames
|
|
9046
|
+
if filteredTypeNames:
|
|
9031
9047
|
arrayPush(lines, \'\')
|
|
9032
9048
|
arrayPush(lines, \'---\')
|
|
9033
9049
|
arrayPush(lines, \'\')
|
|
@@ -9188,7 +9204,7 @@ function schemaDocUserTypeMarkdown(types, typeName, options, headerPrefix, title
|
|
|
9188
9204
|
endif
|
|
9189
9205
|
|
|
9190
9206
|
# Type table data
|
|
9191
|
-
attrText =
|
|
9207
|
+
attrText = schemaDocAttrText(typedef)
|
|
9192
9208
|
tableData = [{\'Type\': schemaDocTypeText(types, objectGet(typedef, \'type\'))}]
|
|
9193
9209
|
if attrText != null:
|
|
9194
9210
|
objectSet(arrayGet(tableData, 0), \'Attributes\', attrText)
|
|
@@ -9413,7 +9429,7 @@ function schemaDocAttrParts(parts, noun, attr, optional):
|
|
|
9413
9429
|
arrayPush(parts, noun + \' >= \' + objectGet(attr, \'gte\'))
|
|
9414
9430
|
endif
|
|
9415
9431
|
if objectHas(attr, \'lt\'):
|
|
9416
|
-
arrayPush(parts, noun + \' < \'+ objectGet(attr, \'lt\'))
|
|
9432
|
+
arrayPush(parts, noun + \' < \' + objectGet(attr, \'lt\'))
|
|
9417
9433
|
endif
|
|
9418
9434
|
if objectHas(attr, \'lte\'):
|
|
9419
9435
|
arrayPush(parts, noun + \' <= \' + objectGet(attr, \'lte\'))
|
|
@@ -11053,7 +11069,7 @@ function unittestReport(options):
|
|
|
11053
11069
|
testFailCount = 0
|
|
11054
11070
|
for testName in testNames:
|
|
11055
11071
|
testFailures = objectGet(unittestTests, testName)
|
|
11056
|
-
if
|
|
11072
|
+
if testFailures:
|
|
11057
11073
|
testFailCount = testFailCount + 1
|
|
11058
11074
|
endif
|
|
11059
11075
|
endfor
|
|
@@ -11066,10 +11082,11 @@ function unittestReport(options):
|
|
|
11066
11082
|
|
|
11067
11083
|
# Render the page title
|
|
11068
11084
|
documentSetTitle(title)
|
|
11085
|
+
testSummary = \'Ran \' + testCount + \' tests - \' + testPassCount + \' passed, \' + testFailCount + \' failed\'
|
|
11069
11086
|
markdownPrint( \\
|
|
11070
11087
|
\'# \' + markdownEscape(title), \\
|
|
11071
11088
|
\'\', \\
|
|
11072
|
-
|
|
11089
|
+
testSummary \\
|
|
11073
11090
|
)
|
|
11074
11091
|
if !isReport:
|
|
11075
11092
|
if testNameArg:
|
|
@@ -11097,7 +11114,7 @@ function unittestReport(options):
|
|
|
11097
11114
|
if !hideTests || testNameArg:
|
|
11098
11115
|
for testName in testNames:
|
|
11099
11116
|
testFailures = objectGet(unittestTests, testName)
|
|
11100
|
-
if
|
|
11117
|
+
if testFailures:
|
|
11101
11118
|
failureLink = if(isReport, testName, \\
|
|
11102
11119
|
argsLink(unittestReportArguments, testName, {\'test\': testName}, false, \'_top\'))
|
|
11103
11120
|
failureLines = [\'\', failureLink + \' - FAIL\']
|
|
@@ -11126,7 +11143,7 @@ function unittestReport(options):
|
|
|
11126
11143
|
if !hideTests || testNameArg:
|
|
11127
11144
|
for testName in testNames:
|
|
11128
11145
|
testFailures = objectGet(unittestTests, testName)
|
|
11129
|
-
if !
|
|
11146
|
+
if !testFailures:
|
|
11130
11147
|
testLink = if(isReport, testName, \\
|
|
11131
11148
|
argsLink(unittestReportArguments, testName, {\'test\': testName}, false, \'_top\'))
|
|
11132
11149
|
markdownPrint(\'\', testLink + \' - OK\')
|
|
@@ -11135,23 +11152,26 @@ function unittestReport(options):
|
|
|
11135
11152
|
endif
|
|
11136
11153
|
endif
|
|
11137
11154
|
|
|
11138
|
-
#
|
|
11155
|
+
# Compute the coverage data, the total coverage percentage, and the coverage error, if any
|
|
11139
11156
|
coverage = unittestCoverageGlobal()
|
|
11157
|
+
coverageData = if(!testNameArg && coverage, unittestCoverageData(coverageExclude))
|
|
11158
|
+
coveragePercent = if(coverageData, objectGet(arrayGet(coverageData, arrayLength(coverageData) - 1), \'Coverage\'))
|
|
11159
|
+
coverageSummary = if(coveragePercent != null, \'Coverage: \' + numberToFixed(coveragePercent, 2) + \'%\' + \\
|
|
11160
|
+
if(coverageMin, \' (minimum \' + numberToFixed(coverageMin, 2) + \'%)\', \'\'))
|
|
11161
|
+
coverageError = if(coveragePercent != null && coveragePercent < coverageMin, \\
|
|
11162
|
+
\'**Error**: Coverage percentage, \' + numberToFixed(coveragePercent, 2) + \\
|
|
11163
|
+
\'%, is below the minimum coverage percentage of \' + numberToFixed(coverageMin, 2) + \'%.\')
|
|
11164
|
+
|
|
11165
|
+
# Coverage report
|
|
11140
11166
|
coverageFailCount = 0
|
|
11141
11167
|
if !testNameArg && coverage:
|
|
11142
11168
|
markdownPrint(\'\', \'## Coverage Report\')
|
|
11143
|
-
|
|
11144
|
-
# Compute the coverage data table
|
|
11145
|
-
coverageData = unittestCoverageData(coverageExclude)
|
|
11146
11169
|
if coverageData:
|
|
11170
|
+
markdownPrint(\'\', coverageSummary)
|
|
11171
|
+
|
|
11147
11172
|
# Verify the coverage percent
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
markdownPrint( \\
|
|
11151
|
-
\'\', \\
|
|
11152
|
-
\'**Error**: Coverage percentage, \' + numberToFixed(coveragePercent, 1, true) + \\
|
|
11153
|
-
\'%, is below the minimum coverage percentage of \' + numberToFixed(coverageMin, 1, true) + \'%.\' \\
|
|
11154
|
-
)
|
|
11173
|
+
if coverageError != null:
|
|
11174
|
+
markdownPrint(\'\', coverageError)
|
|
11155
11175
|
coverageFailCount = coverageFailCount + 1
|
|
11156
11176
|
endif
|
|
11157
11177
|
|
|
@@ -11161,7 +11181,7 @@ function unittestReport(options):
|
|
|
11161
11181
|
if !isReport && !stringStartsWith(scriptName, \'**\'):
|
|
11162
11182
|
objectSet(row, \'Script\', argsLink(unittestReportArguments, scriptName, {"script": scriptName}, false, "_top"))
|
|
11163
11183
|
endif
|
|
11164
|
-
objectSet(row, \'Coverage\', numberToFixed(objectGet(row, \'Coverage\'),
|
|
11184
|
+
objectSet(row, \'Coverage\', numberToFixed(objectGet(row, \'Coverage\'), 2) + \'%\')
|
|
11165
11185
|
endfor
|
|
11166
11186
|
markdownPrint(\'\', dataTableMarkdown(coverageData, { \\
|
|
11167
11187
|
\'fields\': [\'Script\', \'Statements\', \'Missing\', \'Coverage\'], \\
|
|
@@ -11176,6 +11196,15 @@ function unittestReport(options):
|
|
|
11176
11196
|
endif
|
|
11177
11197
|
endif
|
|
11178
11198
|
|
|
11199
|
+
# Render the summary section
|
|
11200
|
+
markdownPrint(\'\', \'## Summary\', \'\', testSummary)
|
|
11201
|
+
if coverageSummary != null:
|
|
11202
|
+
markdownPrint(\'\', coverageSummary)
|
|
11203
|
+
endif
|
|
11204
|
+
if coverageError != null:
|
|
11205
|
+
markdownPrint(\'\', coverageError)
|
|
11206
|
+
endif
|
|
11207
|
+
|
|
11179
11208
|
# Return status code
|
|
11180
11209
|
return testWarningCount + testFailCount + coverageFailCount
|
|
11181
11210
|
endfunction
|
|
@@ -11273,7 +11302,7 @@ function unittestReportScriptLines(codeLineElements, lines, color, noNewline):
|
|
|
11273
11302
|
endfunction
|
|
11274
11303
|
|
|
11275
11304
|
|
|
11276
|
-
# Get the coverage data table - columns are "Script", "
|
|
11305
|
+
# Get the coverage data table - columns are "Script", "Statements", "Missing", and "Coverage"
|
|
11277
11306
|
function unittestCoverageData(coverageExclude):
|
|
11278
11307
|
data = []
|
|
11279
11308
|
|
|
@@ -11328,8 +11357,7 @@ function unittestCoverageData(coverageExclude):
|
|
|
11328
11357
|
\'Script\': scriptName, \\
|
|
11329
11358
|
\'Statements\': statementCount, \\
|
|
11330
11359
|
\'Missing\': missingCount, \\
|
|
11331
|
-
\'Coverage\': coveragePercent
|
|
11332
|
-
\'CoverageStr\': numberToFixed(coveragePercent, 1) + \'%\' \\
|
|
11360
|
+
\'Coverage\': coveragePercent \\
|
|
11333
11361
|
})
|
|
11334
11362
|
endfor
|
|
11335
11363
|
|
|
@@ -11340,8 +11368,7 @@ function unittestCoverageData(coverageExclude):
|
|
|
11340
11368
|
\'Script\': \'**Total**\', \\
|
|
11341
11369
|
\'Statements\': totalStatements, \\
|
|
11342
11370
|
\'Missing\': totalMissing, \\
|
|
11343
|
-
\'Coverage\': coveragePercent
|
|
11344
|
-
\'CoverageStr\': numberToFixed(coveragePercent, 1) + \'%\' \\
|
|
11371
|
+
\'Coverage\': coveragePercent \\
|
|
11345
11372
|
})
|
|
11346
11373
|
endif
|
|
11347
11374
|
endif
|
|
@@ -11359,9 +11386,9 @@ function unittestScriptStatements(script):
|
|
|
11359
11386
|
arrayPush(statements, statement)
|
|
11360
11387
|
|
|
11361
11388
|
# Add function statements
|
|
11362
|
-
|
|
11363
|
-
if
|
|
11364
|
-
for funcStatement in objectGet(
|
|
11389
|
+
statementFunction = objectGet(statement, \'function\')
|
|
11390
|
+
if statementFunction != null:
|
|
11391
|
+
for funcStatement in objectGet(statementFunction, \'statements\'):
|
|
11365
11392
|
arrayPush(statements, funcStatement)
|
|
11366
11393
|
endfor
|
|
11367
11394
|
endif
|
|
@@ -11388,15 +11415,20 @@ unittestMockWindowHeight = 768
|
|
|
11388
11415
|
unittestMockWindowWidth = 1024
|
|
11389
11416
|
|
|
11390
11417
|
|
|
11418
|
+
# Create a new mocked MarkdownUp state
|
|
11419
|
+
function unittestMockStateNew():
|
|
11420
|
+
return { \\
|
|
11421
|
+
\'localStorage\': {}, \\
|
|
11422
|
+
\'sessionStorage\': {}, \\
|
|
11423
|
+
\'windowClipboard\': \'\' \\
|
|
11424
|
+
}
|
|
11425
|
+
endfunction
|
|
11426
|
+
|
|
11427
|
+
|
|
11391
11428
|
# The mocked MarkdownUp state
|
|
11392
11429
|
unittestMockCalls = []
|
|
11393
11430
|
unittestMockFunctions = {}
|
|
11394
|
-
|
|
11395
|
-
\'localStorage\': {}, \\
|
|
11396
|
-
\'sessionStorage\': {}, \\
|
|
11397
|
-
\'windowClipboard\': \'\' \\
|
|
11398
|
-
}
|
|
11399
|
-
unittestMockState = objectCopy(unittestMockStateDefault)
|
|
11431
|
+
unittestMockState = unittestMockStateNew()
|
|
11400
11432
|
|
|
11401
11433
|
|
|
11402
11434
|
# $function: unittestMockAll
|
|
@@ -11459,7 +11491,7 @@ endfunction
|
|
|
11459
11491
|
# $arg funcName: The name of the function to mock
|
|
11460
11492
|
# $arg mockFunc: The mock function
|
|
11461
11493
|
function unittestMockOne(funcName, mockFunc):
|
|
11462
|
-
# Replace the function
|
|
11494
|
+
# Replace the function with the mocked function
|
|
11463
11495
|
objectSet(unittestMockFunctions, funcName, systemGlobalGet(funcName))
|
|
11464
11496
|
systemGlobalSet(funcName, mockFunc)
|
|
11465
11497
|
endfunction
|
|
@@ -11494,7 +11526,7 @@ function unittestMockEnd():
|
|
|
11494
11526
|
mockCalls = unittestMockCalls
|
|
11495
11527
|
systemGlobalSet(\'unittestMockCalls\', [])
|
|
11496
11528
|
systemGlobalSet(\'unittestMockFunctions\', {})
|
|
11497
|
-
systemGlobalSet(\'unittestMockState\',
|
|
11529
|
+
systemGlobalSet(\'unittestMockState\', unittestMockStateNew())
|
|
11498
11530
|
|
|
11499
11531
|
return mockCalls
|
|
11500
11532
|
endfunction
|
|
@@ -11644,6 +11676,7 @@ endfunction
|
|
|
11644
11676
|
|
|
11645
11677
|
|
|
11646
11678
|
function unittestMock_windowURLObject(data, contentType):
|
|
11679
|
+
contentType = if(contentType != null, contentType, \'text/plain\')
|
|
11647
11680
|
return \'blob:\' + urlEncode(contentType) + \'-\' + urlEncode(if(stringLength(data) < 20, data, stringSlice(data, 0, 20)))
|
|
11648
11681
|
endfunction
|
|
11649
11682
|
|
|
@@ -11701,7 +11734,7 @@ endfunction
|
|
|
11701
11734
|
function urlEncodeQueryStringHelper(obj, memberFqn, keyValues):
|
|
11702
11735
|
objType = systemType(obj)
|
|
11703
11736
|
if objType == \'array\':
|
|
11704
|
-
if
|
|
11737
|
+
if !obj:
|
|
11705
11738
|
arrayPush(keyValues, if(memberFqn != null, memberFqn + \'=\', \'\'))
|
|
11706
11739
|
else:
|
|
11707
11740
|
for value, ixValue in obj:
|
|
@@ -11713,7 +11746,7 @@ function urlEncodeQueryStringHelper(obj, memberFqn, keyValues):
|
|
|
11713
11746
|
arrayPush(keyValues, if(memberFqn != null, memberFqn + \'=\' + objEncoded, objEncoded))
|
|
11714
11747
|
elif objType == \'object\':
|
|
11715
11748
|
keys = arraySort(objectKeys(obj))
|
|
11716
|
-
if
|
|
11749
|
+
if !keys:
|
|
11717
11750
|
arrayPush(keyValues, if(memberFqn != null, memberFqn + \'=\', \'\'))
|
|
11718
11751
|
else:
|
|
11719
11752
|
for key in keys:
|
|
@@ -11881,6 +11914,7 @@ function urlEncodeHelper(url, regexSafe, regexUnsafe):
|
|
|
11881
11914
|
# implementation, in which case null is returned below
|
|
11882
11915
|
parts = []
|
|
11883
11916
|
ixSafe = 0
|
|
11917
|
+
encodeFailed = false
|
|
11884
11918
|
for match in regexMatchAll(regexUnsafe, url):
|
|
11885
11919
|
ixUnsafe = objectGet(match, \'index\')
|
|
11886
11920
|
unsafe = objectGet(objectGet(match, \'groups\'), \'0\')
|
|
@@ -11893,6 +11927,7 @@ function urlEncodeHelper(url, regexSafe, regexUnsafe):
|
|
|
11893
11927
|
|
|
11894
11928
|
# Percent-encode the run\'s unsafe UTF-8 bytes
|
|
11895
11929
|
bytes = stringEncode(unsafe)
|
|
11930
|
+
encodeFailed = encodeFailed || bytes == null
|
|
11896
11931
|
for byte in if(bytes != null, bytes, []):
|
|
11897
11932
|
arrayPush(parts, arrayGet(urlEncodeBytes, byte))
|
|
11898
11933
|
endfor
|
|
@@ -11903,7 +11938,7 @@ function urlEncodeHelper(url, regexSafe, regexUnsafe):
|
|
|
11903
11938
|
arrayPush(parts, stringSlice(url, ixSafe))
|
|
11904
11939
|
endif
|
|
11905
11940
|
|
|
11906
|
-
return if(
|
|
11941
|
+
return if(!encodeFailed, arrayJoin(parts, \'\'))
|
|
11907
11942
|
endfunction
|
|
11908
11943
|
|
|
11909
11944
|
|
|
@@ -11934,9 +11969,12 @@ function urlDecodeComponent(string):
|
|
|
11934
11969
|
return string
|
|
11935
11970
|
endif
|
|
11936
11971
|
|
|
11937
|
-
# Compute the decoded UTF-8 byte array
|
|
11972
|
+
# Compute the decoded UTF-8 byte array - stringEncode fails on surrogate code points under
|
|
11973
|
+
# the Python implementation, in which case null is returned below
|
|
11938
11974
|
parts = stringSplit(string, \'%\')
|
|
11939
|
-
|
|
11975
|
+
partBytes = stringEncode(arrayGet(parts, 0))
|
|
11976
|
+
encodeFailed = partBytes == null
|
|
11977
|
+
bytes = if(partBytes != null, partBytes, [])
|
|
11940
11978
|
for part, ixPart in parts:
|
|
11941
11979
|
if ixPart == 0:
|
|
11942
11980
|
continue
|
|
@@ -11949,12 +11987,14 @@ function urlDecodeComponent(string):
|
|
|
11949
11987
|
endif
|
|
11950
11988
|
arrayPush(bytes, numberParseInt(stringSlice(part, 0, 2), 16))
|
|
11951
11989
|
if stringLength(part) > 2:
|
|
11952
|
-
|
|
11990
|
+
partBytes = stringEncode(stringSlice(part, 2))
|
|
11991
|
+
encodeFailed = encodeFailed || partBytes == null
|
|
11992
|
+
arrayExtend(bytes, if(partBytes != null, partBytes, []))
|
|
11953
11993
|
endif
|
|
11954
11994
|
endfor
|
|
11955
11995
|
|
|
11956
|
-
# Invalid UTF-8?
|
|
11957
|
-
decoded = stringDecode(bytes)
|
|
11996
|
+
# Invalid UTF-8 or unencodable input?
|
|
11997
|
+
decoded = if(!encodeFailed, stringDecode(bytes))
|
|
11958
11998
|
if decoded == null:
|
|
11959
11999
|
systemLogDebug("url.bare: Invalid percent-encoding \'" + urlTruncate(string) + "\'")
|
|
11960
12000
|
endif
|
|
@@ -507,8 +507,23 @@ def value_parse_datetime(text):
|
|
|
507
507
|
month = int(m_date.group('month'))
|
|
508
508
|
day = int(m_date.group('day'))
|
|
509
509
|
return datetime.datetime(year, month, day)
|
|
510
|
-
|
|
511
|
-
|
|
510
|
+
m_datetime = _R_DATETIME.match(text)
|
|
511
|
+
if m_datetime is not None:
|
|
512
|
+
text_zulu = _R_DATETIME_ZULU.sub('+00:00', text)
|
|
513
|
+
|
|
514
|
+
# fromisoformat's support for ISO 8601's hour-24 end-of-day special case varies across Python
|
|
515
|
+
# versions, so handle it explicitly rather than relying on stdlib behavior
|
|
516
|
+
is_hour24 = m_datetime.group('hour') == '24'
|
|
517
|
+
if is_hour24:
|
|
518
|
+
if m_datetime.group('minute') != '00' or m_datetime.group('second') != '00' or \
|
|
519
|
+
(m_datetime.group('fraction') is not None and int(m_datetime.group('fraction')) != 0):
|
|
520
|
+
return None
|
|
521
|
+
text_zulu = text_zulu[:11] + '00' + text_zulu[13:]
|
|
522
|
+
|
|
523
|
+
result = datetime.datetime.fromisoformat(text_zulu)
|
|
524
|
+
if is_hour24:
|
|
525
|
+
result += datetime.timedelta(days=1)
|
|
526
|
+
result = result.astimezone().replace(tzinfo=None)
|
|
512
527
|
return result.replace(microsecond=(result.microsecond // 1000) * 1000)
|
|
513
528
|
except ValueError:
|
|
514
529
|
return None
|
|
@@ -516,7 +531,10 @@ def value_parse_datetime(text):
|
|
|
516
531
|
return None
|
|
517
532
|
|
|
518
533
|
_R_DATE = re.compile(r'^(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})$')
|
|
519
|
-
_R_DATETIME = re.compile(
|
|
534
|
+
_R_DATETIME = re.compile(
|
|
535
|
+
r'^[0-9]{4}-[0-9]{2}-[0-9]{2}T(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2}):(?P<second>[0-9]{2})'
|
|
536
|
+
r'(?:\.(?P<fraction>[0-9]{1,6}))?(?:Z|[+-][0-9]{2}:[0-9]{2})$'
|
|
537
|
+
)
|
|
520
538
|
_R_DATETIME_ZULU = re.compile(r'Z$')
|
|
521
539
|
|
|
522
540
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|