diff --git a/compiler.jar b/compiler.jar
index 79dfe1866..e6afbcf1a 100644
Binary files a/compiler.jar and b/compiler.jar differ
diff --git a/js/openaf.js b/js/openaf.js
index ac6d78025..4e9b44992 100644
--- a/js/openaf.js
+++ b/js/openaf.js
@@ -1028,9 +1028,8 @@ const printTable = function(anArrayOfEntries, aWidthLimit, displayCount, useAnsi
output.push((useAnsi ? [ _colorMap.title, "...", "\u001b[m" ].join("") : "...")); outOfWidth = true
} else {
var ansiLengthCol = visibleLength(col);
- var _ps = ' '.repeat(Math.floor((maxsize[col] - ansiLengthCol)/2))
- var _pe = ' '.repeat(Math.round((maxsize[col] - ansiLengthCol) / 2))
- output.push(useAnsi ? [ _colorMap.title, _ps, col, _pe, "\u001b[m" ].join("") : _ps + col + _pe)
+ var _pe = ' '.repeat(maxsize[col] - ansiLengthCol)
+ output.push(useAnsi ? [ _colorMap.title, col, _pe, "\u001b[m" ].join("") : col + _pe)
if (colNum < colsLengthMinusOne) output.push(useAnsi ? [ _colorMap.lines, vLine, "\u001b[m" ].join("") : vLine)
}
colNum++
@@ -4771,6 +4770,7 @@ const inherit = function(Child, Parent) {
const __visibleLength = "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"
var __visibleLength2
const __visibleLengthAnsiRE = /\033(?:\[[0-9;?]*[ -\/]*[@-~]|\][^\u0007\u001b]*(?:\u0007|\u001b\\))/g
+const __visibleLengthChar = java.lang.Character
const __visibleLengthIsControl = cp => (
(cp >= 0 && cp <= 0x1F) ||
(cp >= 0x7F && cp <= 0x9F)
@@ -4782,12 +4782,22 @@ const __visibleLengthIsCombining = cp => (
(cp >= 0x20D0 && cp <= 0x20FF) ||
(cp >= 0xFE20 && cp <= 0xFE2F)
)
-const __visibleLengthIsEmojiModifier = cp => (
- (cp >= 0x1F3FB && cp <= 0x1F3FF) ||
- cp == 0xFE0E || cp == 0xFE0F ||
- cp == 0x20E3
+const __visibleLengthIsEmojiModifier = cp => cp >= 0x1F3FB && cp <= 0x1F3FF
+const __visibleLengthIsVariationSelector = cp => (
+ (cp >= 0xFE00 && cp <= 0xFE0F) ||
+ (cp >= 0xE0100 && cp <= 0xE01EF)
)
+const __visibleLengthIsKeycapMark = cp => cp == 0x20E3
+const __visibleLengthIsTag = cp => cp >= 0xE0020 && cp <= 0xE007F
const __visibleLengthIsRegionalIndicator = cp => cp >= 0x1F1E6 && cp <= 0x1F1FF
+const __visibleLengthIsEmojiLike = cp => (
+ (cp >= 0x231A && cp <= 0x231B) ||
+ (cp >= 0x23E9 && cp <= 0x23EC) ||
+ cp == 0x23F0 || cp == 0x23F3 ||
+ (cp >= 0x25FD && cp <= 0x25FE) ||
+ (cp >= 0x2600 && cp <= 0x27BF) ||
+ (cp >= 0x1F000 && cp <= 0x1FAFF)
+)
const __visibleLengthIsWide = cp => (
cp >= 0x1100 && (
cp <= 0x115F ||
@@ -4804,7 +4814,7 @@ const __visibleLengthIsWide = cp => (
)
)
const __visibleLengthCodePointWidth = cp => {
- if (__visibleLengthIsControl(cp) || __visibleLengthIsCombining(cp) || __visibleLengthIsEmojiModifier(cp) || cp == 0x200D) return 0
+ if (__visibleLengthIsControl(cp) || __visibleLengthIsCombining(cp) || __visibleLengthIsEmojiModifier(cp) || __visibleLengthIsVariationSelector(cp) || __visibleLengthIsKeycapMark(cp) || __visibleLengthIsTag(cp) || cp == 0x200D) return 0
return __visibleLengthIsWide(cp) ? 2 : 1
}
const visibleLength = str => {
@@ -4825,13 +4835,33 @@ const visibleLength = str => {
}
var width = __visibleLengthCodePointWidth(cp)
+ var emojiCluster = __visibleLengthIsEmojiLike(cp) || width == 2
+ var emojiPresentation = false
+ var textPresentation = false
var joinedEmoji = false
var keycapEmoji = false
+ var taggedEmoji = false
while(i < str.length) {
var nextCp = str.codePointAt(i)
+ if (__visibleLengthIsKeycapMark(nextCp)) {
+ keycapEmoji = true
+ i += nextCp > 0xFFFF ? 2 : 1
+ continue
+ }
if (__visibleLengthIsCombining(nextCp) || __visibleLengthIsEmojiModifier(nextCp)) {
- if (nextCp == 0x20E3) keycapEmoji = true
+ emojiCluster = emojiCluster || __visibleLengthIsEmojiLike(nextCp)
+ i += nextCp > 0xFFFF ? 2 : 1
+ continue
+ }
+ if (__visibleLengthIsVariationSelector(nextCp)) {
+ if (nextCp == 0xFE0F) emojiPresentation = true
+ if (nextCp == 0xFE0E) textPresentation = true
+ i += nextCp > 0xFFFF ? 2 : 1
+ continue
+ }
+ if (__visibleLengthIsTag(nextCp)) {
+ taggedEmoji = true
i += nextCp > 0xFFFF ? 2 : 1
continue
}
@@ -4841,6 +4871,7 @@ const visibleLength = str => {
if (i < str.length) {
var zwjCp = str.codePointAt(i)
if (isDef(zwjCp)) {
+ emojiCluster = emojiCluster || __visibleLengthIsEmojiLike(zwjCp) || __visibleLengthIsWide(zwjCp)
width = Math.max(width, __visibleLengthCodePointWidth(zwjCp))
i += zwjCp > 0xFFFF ? 2 : 1
continue
@@ -4850,7 +4881,7 @@ const visibleLength = str => {
break
}
- if (joinedEmoji || keycapEmoji) width = Math.max(width, 2)
+ if (!textPresentation && ((emojiCluster && (joinedEmoji || emojiPresentation || taggedEmoji)) || keycapEmoji)) width = Math.max(width, 2)
l += width
}
return l
@@ -5254,10 +5285,6 @@ const $path = function(aObj, aPath, customFunctions) {
_func: ar => af.fromTOML(ar[0]),
_signature: [ { types: [ jmespath.types.string ] } ]
},
- trim: {
- _func: ar => ar[0].trim(),
- _signature: [ { types: [ jmespath.types.string ] } ]
- },
nvl: {
_func: ar => (isNull(ar[0]) || isUnDef(ar[0]) ? ar[1] : ar[0]),
_signature: [ { types: [ jmespath.types.any ] }, { types: [ jmespath.types.any ] } ]
diff --git a/js/owrap.template.js b/js/owrap.template.js
index 9df05243a..e3c4beed1 100644
--- a/js/owrap.template.js
+++ b/js/owrap.template.js
@@ -807,6 +807,22 @@ OpenWrap.template.prototype.loadCompiledHBS = function(aFilename) {
OpenWrap.template.prototype.parseMD2HTML = function(aMarkdownString, isFull, removeMaxWidth, extraDownOptions, forceDark, aURIPrefix) {
extraDownOptions = _$(extraDownOptions).isMap().default(__flags.MD_SHOWDOWN_OPTIONS)
aURIPrefix = ow.loadServer().httpd.getHTMLPrefix(aURIPrefix)
+ var _withPrefix = aURI => {
+ if (!isString(aURI)) return aURI
+ if (aURI.match(/^[a-z]+:\/\//i) || aURI.startsWith("//")) return aURI
+ if (aURIPrefix == "" || !aURI.startsWith("/")) return aURI
+ if (aURI == aURIPrefix || aURI.startsWith(aURIPrefix + "/")) return aURI
+ return aURIPrefix + aURI
+ }
+ var _normalizeExtra = aExtra => {
+ if (!isString(aExtra)) return aExtra
+ return String(aExtra)
+ .replace(/\b(src|href)=(["'])(\/[^"']*)\2/g, (m, attr, quote, uri) => attr + "=" + quote + _withPrefix(uri) + quote)
+ .replace(/url\((["']?)(\/[^)"']*)\1\)/g, (m, quote, uri) => "url(" + quote + _withPrefix(uri) + quote + ")")
+ }
+ var _registerMDAsset = (aURI, aPath) => {
+ if (isString(aURI) && isString(aPath) && io.fileExists(aPath)) ow.template.__srcPath[aURI] = aPath
+ }
removeMaxWidth = _$(removeMaxWidth, "removeMaxWidth").isBoolean().default(__flags.MD_NOMAXWIDTH)
var mdString = aMarkdownString
@@ -863,6 +879,22 @@ OpenWrap.template.prototype.parseMD2HTML = function(aMarkdownString, isFull, rem
this.__templatemd = io.readFileString(getOpenAFJar() + "::hbs/md.hbs")
}
+ if (isDef(getOPackPath("KaTeX"))) {
+ var _katexPath = getOPackPath("KaTeX")
+ _registerMDAsset("/js/katex.min.js", _katexPath + "/lib/katex.min.js")
+ _registerMDAsset("/js/auto-render.min.js", _katexPath + "/lib/auto-render.min.js")
+ _registerMDAsset("/js/showdown-katex.min.js", _katexPath + "/lib/showdown-katex.min.js")
+ _registerMDAsset("/css/katex.min.css", _katexPath + "/lib/katex.min.css")
+
+ var _katexFontsPath = _katexPath + "/lib/fonts"
+ if (io.fileExists(_katexFontsPath)) {
+ io.listFiles(_katexFontsPath).files.forEach(f => {
+ _registerMDAsset("/fonts/" + f.filename, f.canonicalPath)
+ _registerMDAsset("/css/fonts/" + f.filename, f.canonicalPath)
+ })
+ }
+ }
+
var _extras = [], _posextras = []
if (__flags.MD_CHART) {
_extras.push('')
@@ -1130,6 +1162,9 @@ OpenWrap.template.prototype.parseMD2HTML = function(aMarkdownString, isFull, rem
if (mdString.indexOf(r.t) >= 0) _posextras.push(r.e)
})
+ _extras = _extras.map(_normalizeExtra)
+ _posextras = _posextras.map(_normalizeExtra)
+
var html = converter.makeHtml(mdString).replace("", "")
if (__flags.MD_RENDER_SVG && svgBlocks.length > 0) {
svgBlocks.forEach((svg, idx) => {
@@ -1668,6 +1703,40 @@ OpenWrap.template.prototype.html = {
}
}
};
+ var resolveRelativeURL = (aBaseURL, aRelativeURL) => {
+ if (!isString(aRelativeURL)) return aRelativeURL
+ if (aRelativeURL.match(/^[a-z]+:\/\//i) || aRelativeURL.startsWith("//") || aRelativeURL.startsWith("data:") || aRelativeURL.startsWith("blob:") || aRelativeURL.startsWith("#")) return aRelativeURL
+ if (aRelativeURL.startsWith("/")) return aRelativeURL
+
+ aBaseURL = String(aBaseURL).replace(/[?#].*$/, "")
+ var baseParts = aBaseURL.split("/")
+ if (baseParts.length > 0) baseParts.pop()
+
+ aRelativeURL.split("/").forEach(part => {
+ if (part == "" || part == ".") return
+ if (part == "..") {
+ if (baseParts.length > 1 || (baseParts.length == 1 && baseParts[0] != "")) baseParts.pop()
+ } else {
+ baseParts.push(part)
+ }
+ })
+
+ var out = baseParts.join("/")
+ if (aBaseURL.startsWith("/") && !out.startsWith("/")) out = "/" + out
+ return out
+ }
+ var inlineCSSURLs = (aCSS, aBaseURL) => {
+ if (!isString(aCSS)) return aCSS
+ return aCSS.replace(/url\((["']?)([^)"']+)\1\)/g, (m, quote, aURL) => {
+ if (!isString(aURL) || aURL == "" || aURL.startsWith("data:") || aURL.startsWith("blob:") || aURL.startsWith("#")) return m
+
+ var resolvedURL = resolveRelativeURL(aBaseURL, aURL)
+ var inlinedURL = testURL(resolvedURL, false)
+ if (!isString(inlinedURL) || inlinedURL == resolvedURL) return m
+
+ return "url(" + quote + inlinedURL + quote + ")"
+ })
+ }
// src=
var srcs_replaces = [];
@@ -1682,7 +1751,8 @@ OpenWrap.template.prototype.html = {
srcs = anOriginalHTML.match(//g);
srcs_replaces = [];
for(var ii in srcs) {
- srcs_replaces[srcs[ii]] = testURL(srcs[ii].match(//)[1], true);
+ var href = srcs[ii].match(//)[1]
+ srcs_replaces[srcs[ii]] = inlineCSSURLs(testURL(href, true), href);
}
for(var ii in srcs_replaces) {
anOriginalHTML = anOriginalHTML.replace(ii, "");
diff --git a/lib/jna-platform-5.18.1.jar b/lib/jna-platform-5.19.0.jar
similarity index 74%
rename from lib/jna-platform-5.18.1.jar
rename to lib/jna-platform-5.19.0.jar
index b6394a5b0..42530f518 100644
Binary files a/lib/jna-platform-5.18.1.jar and b/lib/jna-platform-5.19.0.jar differ
diff --git a/pom.json b/pom.json
index 307bce929..45005237b 100644
--- a/pom.json
+++ b/pom.json
@@ -1 +1 @@
-{"{\"artifactId\":\"rhino\"}":{"groupId":"org.mozilla","artifactId":"rhino","version":"1.8.1"},"{\"artifactId\":\"rhino-xml\"}":{"groupId":"org.mozilla","artifactId":"rhino-xml","version":"1.8.1"},"{\"artifactId\":\"rhino-engine\"}":{"groupId":"org.mozilla","artifactId":"rhino-engine","version":"1.8.1"},"{\"artifactId\":\"asciilist-j7\"}":{"groupId":"de.vandermeer","artifactId":"asciilist-j7","version":"1.0.0"},"{\"artifactId\":\"asciitable-j7\"}":{"groupId":"de.vandermeer","artifactId":"asciitable-j7","version":"1.0.1"},"{\"artifactId\":\"commons-cli\"}":{"groupId":"commons-cli","artifactId":"commons-cli","version":"1.11.0"},"{\"artifactId\":\"commons-codec\"}":{"groupId":"commons-codec","artifactId":"commons-codec","version":"1.22.0"},"{\"artifactId\":\"commons-collections4\"}":{"groupId":"org.apache.commons","artifactId":"commons-collections4","version":"4.5.0"},"{\"artifactId\":\"commons-compress\"}":{"groupId":"org.apache.commons","artifactId":"commons-compress","version":"1.28.0"},"{\"artifactId\":\"commons-csv\"}":{"groupId":"org.apache.commons","artifactId":"commons-csv","version":"1.14.1"},"{\"artifactId\":\"commons-email\"}":{"groupId":"org.apache.commons","artifactId":"commons-email","version":"1.6.0"},"{\"artifactId\":\"commons-io\"}":{"groupId":"commons-io","artifactId":"commons-io","version":"2.22.0"},"{\"artifactId\":\"commons-lang3\"}":{"groupId":"org.apache.commons","artifactId":"commons-lang3","version":"3.20.0"},"{\"artifactId\":\"commons-logging\"}":{"groupId":"commons-logging","artifactId":"commons-logging","version":"1.3.6"},"{\"artifactId\":\"commons-math3\"}":{"groupId":"org.apache.commons","artifactId":"commons-math3","version":"3.6.1"},"{\"artifactId\":\"commons-net\"}":{"groupId":"commons-net","artifactId":"commons-net","version":"3.13.0"},"{\"artifactId\":\"googleauth\"}":{"groupId":"com.warrenstrange","artifactId":"googleauth","version":"1.5.0"},"{\"artifactId\":\"gson\"}":{"groupId":"com.google.code.gson","artifactId":"gson","version":"2.14.0"},"{\"artifactId\":\"h2\"}":{"groupId":"com.h2database","artifactId":"h2","version":"2.4.240"},"{\"artifactId\":\"jackson-annotations\"}":{"groupId":"com.fasterxml.jackson.core","artifactId":"jackson-annotations","version":"2.21"},"{\"artifactId\":\"jackson-core\"}":{"groupId":"com.fasterxml.jackson.core","artifactId":"jackson-core","version":"2.21.3"},"{\"artifactId\":\"jackson-databind\"}":{"groupId":"com.fasterxml.jackson.core","artifactId":"jackson-databind","version":"2.21.3"},"{\"artifactId\":\"jansi\"}":{"groupId":"org.fusesource.jansi","artifactId":"jansi","version":"1.18"},"{\"artifactId\":\"jaxb-api\"}":{"groupId":"javax.xml.bind","artifactId":"jaxb-api","version":"2.3.1"},"{\"artifactId\":\"jbsdiff\"}":{"groupId":"io.sigpipe","artifactId":"jbsdiff","version":"1.0"},"{\"artifactId\":\"jetty-client\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-client","version":"12.1.9"},"{\"artifactId\":\"jetty-http\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-http","version":"12.1.9"},"{\"artifactId\":\"jetty-io\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-io","version":"12.1.9"},"{\"artifactId\":\"jetty-util\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-util","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-jetty-common\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-jetty-common","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-jetty-api\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-jetty-api","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-jetty-client\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-jetty-client","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-core-client\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-core-client","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-core-common\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-core-common","version":"12.1.9"},"{\"artifactId\":\"jline\"}":{"groupId":"jline","artifactId":"jline","version":"2.14.6"},"{\"artifactId\":\"jna-platform\"}":{"groupId":"net.java.dev.jna","artifactId":"jna-platform","version":"5.18.1"},"{\"artifactId\":\"jna\"}":{"groupId":"net.java.dev.jna","artifactId":"jna","version":"5.18.1"},"{\"artifactId\":\"postgresql\"}":{"groupId":"org.postgresql","artifactId":"postgresql","version":"42.7.11"},"{\"artifactId\":\"snmp4j\"}":{"groupId":"org.snmp4j","artifactId":"snmp4j","version":"3.9.7"},"{\"artifactId\":\"snmp4j-agent\"}":{"groupId":"org.snmp4j","artifactId":"snmp4j-agent","version":"3.8.3"},"{\"artifactId\":\"jjwt-impl\"}":{"groupId":"io.jsonwebtoken","artifactId":"jjwt-impl","version":"0.13.0"},"{\"artifactId\":\"jjwt-api\"}":{"groupId":"io.jsonwebtoken","artifactId":"jjwt-api","version":"0.13.0"},"{\"artifactId\":\"jjwt-gson\"}":{"groupId":"io.jsonwebtoken","artifactId":"jjwt-gson","version":"0.13.0"},"{\"artifactId\":\"okhttp\"}":{"groupId":"com.squareup.okhttp3","artifactId":"okhttp","version":"5.3.2"},"{\"artifactId\":\"okhttp-jvm\"}":{"groupId":"com.squareup.okhttp3","artifactId":"okhttp-jvm","version":"5.3.2"},"{\"artifactId\":\"kotlin-stdlib\"}":{"groupId":"org.jetbrains.kotlin","artifactId":"kotlin-stdlib","version":"2.3.20"},"{\"artifactId\":\"okio\"}":{"groupId":"com.squareup.okio","artifactId":"okio","version":"3.17.0"},"{\"artifactId\":\"okio-jvm\"}":{"groupId":"com.squareup.okio","artifactId":"okio-jvm","version":"3.17.0"},"{\"artifactId\":\"dnsjava\"}":{"groupId":"dnsjava","artifactId":"dnsjava","version":"3.6.5"},"{\"artifactId\":\"slf4j-api\"}":{"groupId":"org.slf4j","artifactId":"slf4j-api","version":"2.0.18"},"{\"artifactId\":\"slf4j-nop\"}":{"groupId":"org.slf4j","artifactId":"slf4j-nop","version":"2.0.18"},"{\"artifactId\":\"jsch\"}":{"groupId":"com.github.mwiede","artifactId":"jsch","version":"2.28.2"},"{\"artifactId\":\"sql-formatter\"}":{"groupId":"com.github.vertical-blank","artifactId":"sql-formatter","version":"2.0.5"},"{\"artifactId\":\"semver4j\"}":{"groupId":"org.semver4j","artifactId":"semver4j","version":"6.0.0"},"{\"artifactId\":\"ojdbc11\"}":{"groupId":"com.oracle.database.jdbc","artifactId":"ojdbc11","version":"23.9.0.25.07"},"{\"artifactId\":\"ojdbc17\"}":{"groupId":"com.oracle.database.jdbc","artifactId":"ojdbc17","version":"23.26.2.0.0"},"{\"artifactId\":\"jetty-compression-common\"}":{"groupId":"org.eclipse.jetty.compression","artifactId":"jetty-compression-common","version":"12.1.9"},"{\"artifactId\":\"jackson-dataformat-toml\"}":{"groupId":"com.fasterxml.jackson.dataformat","artifactId":"jackson-dataformat-toml","version":"2.21.3"},"{\"artifactId\":\"jakarta.mail\"}":{"groupId":"com.sun.mail","artifactId":"jakarta.mail","version":"1.6.8"}}
\ No newline at end of file
+{"{\"artifactId\":\"rhino\"}":{"groupId":"org.mozilla","artifactId":"rhino","version":"1.8.1"},"{\"artifactId\":\"rhino-xml\"}":{"groupId":"org.mozilla","artifactId":"rhino-xml","version":"1.8.1"},"{\"artifactId\":\"rhino-engine\"}":{"groupId":"org.mozilla","artifactId":"rhino-engine","version":"1.8.1"},"{\"artifactId\":\"asciilist-j7\"}":{"groupId":"de.vandermeer","artifactId":"asciilist-j7","version":"1.0.0"},"{\"artifactId\":\"asciitable-j7\"}":{"groupId":"de.vandermeer","artifactId":"asciitable-j7","version":"1.0.1"},"{\"artifactId\":\"commons-cli\"}":{"groupId":"commons-cli","artifactId":"commons-cli","version":"1.11.0"},"{\"artifactId\":\"commons-codec\"}":{"groupId":"commons-codec","artifactId":"commons-codec","version":"1.22.0"},"{\"artifactId\":\"commons-collections4\"}":{"groupId":"org.apache.commons","artifactId":"commons-collections4","version":"4.5.0"},"{\"artifactId\":\"commons-compress\"}":{"groupId":"org.apache.commons","artifactId":"commons-compress","version":"1.28.0"},"{\"artifactId\":\"commons-csv\"}":{"groupId":"org.apache.commons","artifactId":"commons-csv","version":"1.14.1"},"{\"artifactId\":\"commons-email\"}":{"groupId":"org.apache.commons","artifactId":"commons-email","version":"1.6.0"},"{\"artifactId\":\"commons-io\"}":{"groupId":"commons-io","artifactId":"commons-io","version":"2.22.0"},"{\"artifactId\":\"commons-lang3\"}":{"groupId":"org.apache.commons","artifactId":"commons-lang3","version":"3.20.0"},"{\"artifactId\":\"commons-logging\"}":{"groupId":"commons-logging","artifactId":"commons-logging","version":"1.3.6"},"{\"artifactId\":\"commons-math3\"}":{"groupId":"org.apache.commons","artifactId":"commons-math3","version":"3.6.1"},"{\"artifactId\":\"commons-net\"}":{"groupId":"commons-net","artifactId":"commons-net","version":"3.13.0"},"{\"artifactId\":\"googleauth\"}":{"groupId":"com.warrenstrange","artifactId":"googleauth","version":"1.5.0"},"{\"artifactId\":\"gson\"}":{"groupId":"com.google.code.gson","artifactId":"gson","version":"2.14.0"},"{\"artifactId\":\"h2\"}":{"groupId":"com.h2database","artifactId":"h2","version":"2.4.240"},"{\"artifactId\":\"jackson-annotations\"}":{"groupId":"com.fasterxml.jackson.core","artifactId":"jackson-annotations","version":"2.21"},"{\"artifactId\":\"jackson-core\"}":{"groupId":"com.fasterxml.jackson.core","artifactId":"jackson-core","version":"2.21.3"},"{\"artifactId\":\"jackson-databind\"}":{"groupId":"com.fasterxml.jackson.core","artifactId":"jackson-databind","version":"2.21.3"},"{\"artifactId\":\"jansi\"}":{"groupId":"org.fusesource.jansi","artifactId":"jansi","version":"1.18"},"{\"artifactId\":\"jaxb-api\"}":{"groupId":"javax.xml.bind","artifactId":"jaxb-api","version":"2.3.1"},"{\"artifactId\":\"jbsdiff\"}":{"groupId":"io.sigpipe","artifactId":"jbsdiff","version":"1.0"},"{\"artifactId\":\"jetty-client\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-client","version":"12.1.9"},"{\"artifactId\":\"jetty-http\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-http","version":"12.1.9"},"{\"artifactId\":\"jetty-io\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-io","version":"12.1.9"},"{\"artifactId\":\"jetty-util\"}":{"groupId":"org.eclipse.jetty","artifactId":"jetty-util","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-jetty-common\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-jetty-common","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-jetty-api\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-jetty-api","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-jetty-client\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-jetty-client","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-core-client\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-core-client","version":"12.1.9"},"{\"artifactId\":\"jetty-websocket-core-common\"}":{"groupId":"org.eclipse.jetty.websocket","artifactId":"jetty-websocket-core-common","version":"12.1.9"},"{\"artifactId\":\"jline\"}":{"groupId":"jline","artifactId":"jline","version":"2.14.6"},"{\"artifactId\":\"jna-platform\"}":{"groupId":"net.java.dev.jna","artifactId":"jna-platform","version":"5.19.0"},"{\"artifactId\":\"jna\"}":{"groupId":"net.java.dev.jna","artifactId":"jna","version":"5.18.1"},"{\"artifactId\":\"postgresql\"}":{"groupId":"org.postgresql","artifactId":"postgresql","version":"42.7.11"},"{\"artifactId\":\"snmp4j\"}":{"groupId":"org.snmp4j","artifactId":"snmp4j","version":"3.9.7"},"{\"artifactId\":\"snmp4j-agent\"}":{"groupId":"org.snmp4j","artifactId":"snmp4j-agent","version":"3.8.3"},"{\"artifactId\":\"jjwt-impl\"}":{"groupId":"io.jsonwebtoken","artifactId":"jjwt-impl","version":"0.13.0"},"{\"artifactId\":\"jjwt-api\"}":{"groupId":"io.jsonwebtoken","artifactId":"jjwt-api","version":"0.13.0"},"{\"artifactId\":\"jjwt-gson\"}":{"groupId":"io.jsonwebtoken","artifactId":"jjwt-gson","version":"0.13.0"},"{\"artifactId\":\"okhttp\"}":{"groupId":"com.squareup.okhttp3","artifactId":"okhttp","version":"5.3.2"},"{\"artifactId\":\"okhttp-jvm\"}":{"groupId":"com.squareup.okhttp3","artifactId":"okhttp-jvm","version":"5.3.2"},"{\"artifactId\":\"kotlin-stdlib\"}":{"groupId":"org.jetbrains.kotlin","artifactId":"kotlin-stdlib","version":"2.3.20"},"{\"artifactId\":\"okio\"}":{"groupId":"com.squareup.okio","artifactId":"okio","version":"3.17.0"},"{\"artifactId\":\"okio-jvm\"}":{"groupId":"com.squareup.okio","artifactId":"okio-jvm","version":"3.17.0"},"{\"artifactId\":\"dnsjava\"}":{"groupId":"dnsjava","artifactId":"dnsjava","version":"3.6.5"},"{\"artifactId\":\"slf4j-api\"}":{"groupId":"org.slf4j","artifactId":"slf4j-api","version":"2.0.18"},"{\"artifactId\":\"slf4j-nop\"}":{"groupId":"org.slf4j","artifactId":"slf4j-nop","version":"2.0.18"},"{\"artifactId\":\"jsch\"}":{"groupId":"com.github.mwiede","artifactId":"jsch","version":"2.28.2"},"{\"artifactId\":\"sql-formatter\"}":{"groupId":"com.github.vertical-blank","artifactId":"sql-formatter","version":"2.0.5"},"{\"artifactId\":\"semver4j\"}":{"groupId":"org.semver4j","artifactId":"semver4j","version":"6.0.0"},"{\"artifactId\":\"ojdbc11\"}":{"groupId":"com.oracle.database.jdbc","artifactId":"ojdbc11","version":"23.9.0.25.07"},"{\"artifactId\":\"ojdbc17\"}":{"groupId":"com.oracle.database.jdbc","artifactId":"ojdbc17","version":"23.26.2.0.0"},"{\"artifactId\":\"jetty-compression-common\"}":{"groupId":"org.eclipse.jetty.compression","artifactId":"jetty-compression-common","version":"12.1.9"},"{\"artifactId\":\"jackson-dataformat-toml\"}":{"groupId":"com.fasterxml.jackson.dataformat","artifactId":"jackson-dataformat-toml","version":"2.21.3"},"{\"artifactId\":\"jakarta.mail\"}":{"groupId":"com.sun.mail","artifactId":"jakarta.mail","version":"1.6.8"}}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 799b2eb6b..c8ff874be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -290,7 +290,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/ma
net.java.dev.jna
jna-platform
- 5.18.1
+ 5.19.0
net.java.dev.jna
diff --git a/src/openaf/AFBase.java b/src/openaf/AFBase.java
index 2817f3af5..a0f0eeff0 100644
--- a/src/openaf/AFBase.java
+++ b/src/openaf/AFBase.java
@@ -26,11 +26,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.zip.ZipFile;
-import java.util.Iterator;
-import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.zip.ZipFile;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
@@ -86,10 +87,11 @@
Β * Copyright 2023 Nuno Aguiar
*
*/
-public class AFBase extends ScriptableObject {
-
- /**
- *
+public class AFBase extends ScriptableObject {
+ private static final Pattern VISIBLE_LENGTH_ANSI_RE = Pattern.compile("\u001B(?:\\[[0-9;?]*[ -/]*[@-~]|\\][^\\u0007\\u001B]*(?:\\u0007|\\u001B\\\\))");
+
+ /**
+ *
*/
private static final long serialVersionUID = 1L;
private static String K = "openappframework";
@@ -1502,18 +1504,157 @@ public static String getOpenAFJar() {
return AFCmdBase.getJarFilePath(AFCmdBase.class);
}
- /**
- *
- * af.visibleLength(aString) : int
- * Given aString will try to remove ansi characters and just count code point (e.g. removing combined
- * characters like emojis).
- *
- */
- @JSFunction
- public static int visibleLength(String s) {
- s = s.replaceAll("\\033\\[[0-9;]*m", "");
- return s.codePointCount(0, s.length());
- }
+ /**
+ *
+ * af.visibleLength(aString) : int
+ * Given aString returns its visible terminal width in columns, removing ANSI sequences and collapsing
+ * combined characters and emoji grapheme sequences.
+ *
+ */
+ @JSFunction
+ public static int visibleLength(String s) {
+ if (s == null) return 0;
+
+ s = VISIBLE_LENGTH_ANSI_RE.matcher(s).replaceAll("");
+ int length = 0;
+
+ for (int i = 0; i < s.length();) {
+ int cp = s.codePointAt(i);
+ i += Character.charCount(cp);
+
+ if (visibleLengthIsControl(cp)) continue;
+
+ if (visibleLengthIsRegionalIndicator(cp)) {
+ if (i < s.length()) {
+ int nextRegional = s.codePointAt(i);
+ if (visibleLengthIsRegionalIndicator(nextRegional)) i += Character.charCount(nextRegional);
+ }
+ length += 2;
+ continue;
+ }
+
+ int width = visibleLengthCodePointWidth(cp);
+ boolean emojiCluster = visibleLengthIsEmojiLike(cp) || width == 2;
+ boolean emojiPresentation = false;
+ boolean textPresentation = false;
+ boolean joinedEmoji = false;
+ boolean keycapEmoji = false;
+ boolean taggedEmoji = false;
+
+ while (i < s.length()) {
+ int nextCp = s.codePointAt(i);
+
+ if (visibleLengthIsKeycapMark(nextCp)) {
+ keycapEmoji = true;
+ i += Character.charCount(nextCp);
+ continue;
+ }
+ if (visibleLengthIsCombining(nextCp) || visibleLengthIsEmojiModifier(nextCp)) {
+ emojiCluster = emojiCluster || visibleLengthIsEmojiLike(nextCp);
+ i += Character.charCount(nextCp);
+ continue;
+ }
+ if (visibleLengthIsVariationSelector(nextCp)) {
+ if (nextCp == 0xFE0F) emojiPresentation = true;
+ if (nextCp == 0xFE0E) textPresentation = true;
+ i += Character.charCount(nextCp);
+ continue;
+ }
+ if (visibleLengthIsTag(nextCp)) {
+ taggedEmoji = true;
+ i += Character.charCount(nextCp);
+ continue;
+ }
+ if (nextCp == 0x200D) {
+ joinedEmoji = true;
+ i += Character.charCount(nextCp);
+ if (i < s.length()) {
+ int zwjCp = s.codePointAt(i);
+ emojiCluster = emojiCluster || visibleLengthIsEmojiLike(zwjCp) || visibleLengthIsWide(zwjCp);
+ width = Math.max(width, visibleLengthCodePointWidth(zwjCp));
+ i += Character.charCount(zwjCp);
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (!textPresentation && ((emojiCluster && (joinedEmoji || emojiPresentation || taggedEmoji)) || keycapEmoji)) width = Math.max(width, 2);
+ length += width;
+ }
+
+ return length;
+ }
+
+ private static boolean visibleLengthIsControl(int cp) {
+ return (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F);
+ }
+
+ private static boolean visibleLengthIsCombining(int cp) {
+ if (visibleLengthIsVariationSelector(cp)) return false;
+ int type = Character.getType(cp);
+ return type == Character.NON_SPACING_MARK
+ || type == Character.COMBINING_SPACING_MARK
+ || type == Character.ENCLOSING_MARK;
+ }
+
+ private static boolean visibleLengthIsEmojiModifier(int cp) {
+ return cp >= 0x1F3FB && cp <= 0x1F3FF;
+ }
+
+ private static boolean visibleLengthIsVariationSelector(int cp) {
+ return (cp >= 0xFE00 && cp <= 0xFE0F) || (cp >= 0xE0100 && cp <= 0xE01EF);
+ }
+
+ private static boolean visibleLengthIsKeycapMark(int cp) {
+ return cp == 0x20E3;
+ }
+
+ private static boolean visibleLengthIsTag(int cp) {
+ return cp >= 0xE0020 && cp <= 0xE007F;
+ }
+
+ private static boolean visibleLengthIsRegionalIndicator(int cp) {
+ return cp >= 0x1F1E6 && cp <= 0x1F1FF;
+ }
+
+ private static boolean visibleLengthIsEmojiLike(int cp) {
+ return (cp >= 0x231A && cp <= 0x231B)
+ || (cp >= 0x23E9 && cp <= 0x23EC)
+ || cp == 0x23F0
+ || cp == 0x23F3
+ || (cp >= 0x25FD && cp <= 0x25FE)
+ || (cp >= 0x2600 && cp <= 0x27BF)
+ || (cp >= 0x1F000 && cp <= 0x1FAFF);
+ }
+
+ private static boolean visibleLengthIsWide(int cp) {
+ return cp >= 0x1100 && (
+ cp <= 0x115F
+ || cp == 0x2329 || cp == 0x232A
+ || (cp >= 0x2E80 && cp <= 0xA4CF && cp != 0x303F)
+ || (cp >= 0xAC00 && cp <= 0xD7A3)
+ || (cp >= 0xF900 && cp <= 0xFAFF)
+ || (cp >= 0xFE10 && cp <= 0xFE19)
+ || (cp >= 0xFE30 && cp <= 0xFE6F)
+ || (cp >= 0xFF00 && cp <= 0xFF60)
+ || (cp >= 0xFFE0 && cp <= 0xFFE6)
+ || (cp >= 0x1F300 && cp <= 0x1FAFF)
+ || (cp >= 0x20000 && cp <= 0x3FFFD)
+ );
+ }
+
+ private static int visibleLengthCodePointWidth(int cp) {
+ if (visibleLengthIsControl(cp)
+ || visibleLengthIsCombining(cp)
+ || visibleLengthIsEmojiModifier(cp)
+ || visibleLengthIsVariationSelector(cp)
+ || visibleLengthIsKeycapMark(cp)
+ || visibleLengthIsTag(cp)
+ || cp == 0x200D) return 0;
+
+ return visibleLengthIsWide(cp) ? 2 : 1;
+ }
/**
*
diff --git a/tests/autoTestAll.Format.js b/tests/autoTestAll.Format.js
index c6c646585..425999555 100644
--- a/tests/autoTestAll.Format.js
+++ b/tests/autoTestAll.Format.js
@@ -49,25 +49,102 @@
ow.test.assert(ow.format.escapeHTML("{ a: 1, b: \"2\", c: [1, 2] }"), "<json>{ a: 1, b: "2", c: [1, 2] }</json>", "Problem with escapeHTML");
};
- exports.testWordWrap = function() {
- ow.test.assert(ow.format.string.wordWrap("a long text to serve as an example", 10, "-"), "a long-text to-serve as-an example", "Problem with word wrap.");
-
- var wrappedAnsi = ow.format.string.wordWrap(ansiColor("RED", "1234 5678 90"), 6);
- ow.test.assert(wrappedAnsi.replace(/\033\[[0-9;?]*[ -\/]*[@-~]/g, ""), "1234\n5678\n90", "Problem with word wrap and ANSI sequences.");
- wrappedAnsi.split("\n").forEach(line => {
- ow.test.assert(visibleLength(line) <= 6, true, "Problem with ANSI-aware wrapped line width.");
- });
-
- ow.test.assert(ow.format.string.wordWrap("alpha π beta π gamma", 10), "alpha π\nbeta π\ngamma", "Problem with word wrap and emoji width.");
- };
-
- exports.testWithMDWrap = function() {
- var _oldCon = __con;
- var _oldConStatus = __conStatus;
-
- __con = {
- getTerminal: () => ({
- getWidth: () => 10
+ exports.testWordWrap = function() {
+ ow.test.assert(ow.format.string.wordWrap("a long text to serve as an example", 10, "-"), "a long-text to-serve as-an example", "Problem with word wrap.");
+
+ var wrappedAnsi = ow.format.string.wordWrap(ansiColor("RED", "1234 5678 90"), 6);
+ ow.test.assert(wrappedAnsi.replace(/\033\[[0-9;?]*[ -\/]*[@-~]/g, ""), "1234\n5678\n90", "Problem with word wrap and ANSI sequences.");
+ wrappedAnsi.split("\n").forEach(line => {
+ ow.test.assert(visibleLength(line) <= 6, true, "Problem with ANSI-aware wrapped line width.");
+ });
+
+ ow.test.assert(ow.format.string.wordWrap("alpha π beta π gamma", 10), "alpha π\nbeta π\ngamma", "Problem with word wrap and emoji width.");
+ };
+
+ exports.testVisibleLengthUnicode = function() {
+ var subdivisionFlag = String.fromCodePoint(0x1F3F4, 0xE0067, 0xE0062, 0xE0065, 0xE006E, 0xE0067, 0xE007F);
+ var cases = [
+ ["abc", 3],
+ ["e\u0301", 1],
+ ["δΈ", 2],
+ ["π", 2],
+ ["ππ½", 2],
+ ["π¨βπ©βπ§βπ¦", 2],
+ ["π³οΈβπ", 2],
+ ["π΅πΉ", 2],
+ ["1οΈβ£", 2],
+ [subdivisionFlag, 2]
+ ];
+
+ cases.forEach(function(entry) {
+ ow.test.assert(visibleLength(entry[0]), entry[1], "Problem with visibleLength unicode width for '" + entry[0] + "'.");
+ ow.test.assert(af.visibleLength(entry[0]), entry[1], "Problem with af.visibleLength unicode width for '" + entry[0] + "'.");
+ });
+
+ ow.test.assert(visibleLength(ansiColor("RED", "π¨βπ©βπ§βπ¦")), 2, "Problem with visibleLength ANSI unicode width.");
+ ow.test.assert(af.visibleLength(ansiColor("RED", "π¨βπ©βπ§βπ¦")), 2, "Problem with af.visibleLength ANSI unicode width.");
+ };
+
+ exports.testVisibleLengthEmojiPresentation = function() {
+ ow.test.assert(visibleLength("β½"), 1, "Problem with bare emoji-presentation symbol visible width.");
+ ow.test.assert(af.visibleLength("β½"), 1, "Problem with af.visibleLength bare emoji-presentation symbol visible width.");
+ ow.test.assert(visibleLength("β½οΈ"), 2, "Problem with explicit emoji-presentation symbol visible width.");
+ ow.test.assert(af.visibleLength("β½οΈ"), 2, "Problem with af.visibleLength explicit emoji-presentation symbol visible width.");
+ ow.test.assert(visibleLength("β½οΈ"), 1, "Problem with text-presentation symbol visible width.");
+ ow.test.assert(af.visibleLength("β½οΈ"), 1, "Problem with af.visibleLength text-presentation symbol visible width.");
+ };
+
+ exports.testVisibleLengthSubdivisionFlag = function() {
+ var england = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}";
+ ow.test.assert(visibleLength(england), 2, "Problem with subdivision flag visible width.");
+ ow.test.assert(af.visibleLength(england), 2, "Problem with af.visibleLength subdivision flag visible width.");
+ };
+
+ exports.testPrintTableEmojiAlignment = function() {
+ var rendered = printTable([
+ { "A": "β½ abcdef", "B": "1" },
+ { "A": "123456789", "B": "2" }
+ ], __, false, false, "utf").replace(/\033\[[0-9;?]*[ -\/]*[@-~]/g, "");
+ var lines = rendered.split("\n");
+
+ ow.test.assert(
+ visibleLength(lines[2].split("β")[0]),
+ visibleLength(lines[3].split("β")[0]),
+ "Problem with printTable emoji alignment."
+ );
+ };
+
+ exports.testPrintTableSubdivisionFlagAlignment = function() {
+ var england = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}";
+ var rendered = printTable([
+ { "Team": england + " England", "Pts": "1827.05", "Change": "π’ +1.08" },
+ { "Team": "π΅πΉ Portugal", "Pts": "1766.18", "Change": "π’ +2.34" }
+ ], __, false, false, "utf").replace(/\033\[[0-9;?]*[ -\/]*[@-~]/g, "");
+ var lines = rendered.split("\n");
+
+ ow.test.assert(lines[2].indexOf("β"), lines[3].indexOf("β"), "Problem with printTable subdivision flag alignment.");
+ ow.test.assert(lines[2].lastIndexOf("β"), lines[3].lastIndexOf("β"), "Problem with printTable reference alignment for subdivision flags.");
+ };
+
+ exports.testPrintTableHeaderLeftAlignment = function() {
+ var rendered = printTable([
+ { "Region": "Americas", "Flag": "πΊπΈ", "Country/Entity": "United States" },
+ { "Region": "Asia", "Flag": "π―π΅", "Country/Entity": "Japan" }
+ ], __, false, false, "utf").replace(/\033\[[0-9;?]*[ -\/]*[@-~]/g, "");
+ var lines = rendered.split("\n");
+
+ ow.test.assert(lines[0].indexOf("β"), lines[2].indexOf("β"), "Problem with printTable header first separator alignment.");
+ ow.test.assert(lines[0].lastIndexOf("β"), lines[2].lastIndexOf("β"), "Problem with printTable header second separator alignment.");
+ ow.test.assert(lines[1].indexOf("βΌ"), lines[2].indexOf("β"), "Problem with printTable separator alignment.");
+ };
+
+ exports.testWithMDWrap = function() {
+ var _oldCon = __con;
+ var _oldConStatus = __conStatus;
+
+ __con = {
+ getTerminal: () => ({
+ getWidth: () => 10
})
};
__conStatus = true;
@@ -145,9 +222,9 @@
ow.test.assert(lines[0], "β some comment", "Problem with markdown blockquote rendering a single side line.");
};
- exports.testWithSideLineUnicodeHeaderFooter = function() {
- var rendered = ow.format.withSideLine("x", 8, __, __, ow.format.withSideLineThemes().closedRect, {
- header: "π΅πΉ",
+ exports.testWithSideLineUnicodeHeaderFooter = function() {
+ var rendered = ow.format.withSideLine("x", 8, __, __, ow.format.withSideLineThemes().closedRect, {
+ header: "π΅πΉ",
headerAlign: "left",
footer: "π΅πΉ",
footerAlign: "left"
@@ -158,10 +235,28 @@
ow.test.assert(lines[0], "βββπ΅πΉβββ", "Problem with withSideLine unicode header width.");
ow.test.assert(lines[1], "β x β", "Problem with withSideLine unicode body width.");
ow.test.assert(lines[2], "βββπ΅πΉβββ", "Problem with withSideLine unicode footer width.");
- lines.forEach(line => {
- ow.test.assert(visibleLength(line), 8, "Problem with withSideLine unicode visible width.");
- });
- };
+ lines.forEach(line => {
+ ow.test.assert(visibleLength(line), 8, "Problem with withSideLine unicode visible width.");
+ });
+ };
+
+ exports.testWithSideLineEmojiHeaderFooter = function() {
+ var rendered = ow.format.withSideLine("x", 8, __, __, ow.format.withSideLineThemes().closedRect, {
+ header: "π¨βπ©βπ§βπ¦",
+ headerAlign: "left",
+ footer: "π³οΈβπ",
+ footerAlign: "left"
+ });
+ var plain = rendered.replace(/\033\[[0-9;?]*[ -\/]*[@-~]/g, "");
+ var lines = plain.split("\n");
+
+ ow.test.assert(lines[0], "βββπ¨βπ©βπ§βπ¦βββ", "Problem with withSideLine emoji header width.");
+ ow.test.assert(lines[1], "β x β", "Problem with withSideLine emoji body width.");
+ ow.test.assert(lines[2], "βββπ³οΈβπβββ", "Problem with withSideLine emoji footer width.");
+ lines.forEach(line => {
+ ow.test.assert(visibleLength(line), 8, "Problem with withSideLine emoji visible width.");
+ });
+ };
exports.testPad = function() {
ow.test.assert(ow.format.string.leftPad(".", 2, "-") + ow.format.string.rightPad(".", 2, "-"), "-..-", "Problem with left and right padding.");
diff --git a/tests/autoTestAll.Format.yaml b/tests/autoTestAll.Format.yaml
index 6a05ed3d9..c978434c3 100644
--- a/tests/autoTestAll.Format.yaml
+++ b/tests/autoTestAll.Format.yaml
@@ -63,26 +63,75 @@ jobs:
- Format::Load Format
exec: args.func = args.tests.testEscape;
- - name: Format::Word wrap
- from: Format::Init
- to : oJob Test
- deps:
- - Format::Load Format
- exec: args.func = args.tests.testWordWrap;
-
- - name: Format::Markdown wrap
- from: Format::Init
- to : oJob Test
- deps:
- - Format::Load Format
- exec: args.func = args.tests.testWithMDWrap;
+ - name: Format::Word wrap
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testWordWrap;
+
+ - name: Format::Visible length emoji presentation
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testVisibleLengthEmojiPresentation;
+
+ - name: Format::Visible length subdivision flag
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testVisibleLengthSubdivisionFlag;
+
+ - name: Format::Visible length unicode
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testVisibleLengthUnicode;
+
+ - name: Format::PrintTable emoji alignment
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testPrintTableEmojiAlignment;
+
+ - name: Format::PrintTable subdivision flag alignment
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testPrintTableSubdivisionFlagAlignment;
+
+ - name: Format::PrintTable header alignment
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testPrintTableHeaderLeftAlignment;
+
+ - name: Format::Markdown wrap
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testWithMDWrap;
- - name: Format::String pad
- from: Format::Init
- to : oJob Test
- deps:
- - Format::Load Format
- exec: args.func = args.tests.testPad;
+ - name: Format::String pad
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testPad;
+
+ - name: Format::Side line emoji header footer
+ from: Format::Init
+ to : oJob Test
+ deps:
+ - Format::Load Format
+ exec: args.func = args.tests.testWithSideLineEmojiHeaderFooter;
- name: Format::Terminal capabilities
from: Format::Init
@@ -250,10 +299,13 @@ todo:
- Format::Date to/from
- Format::Unix date to/from
- Format::LDAP date to/from
- - Format::Escape strings
- - Format::Word wrap
- - Format::Markdown wrap
- - Format::String pad
+ - Format::Escape strings
+ - Format::Word wrap
+ - Format::PrintTable emoji alignment
+ - Format::PrintTable subdivision flag alignment
+ - Format::PrintTable header alignment
+ - Format::Markdown wrap
+ - Format::String pad
- Format::Terminal capabilities
- Format::Viz frame diff
- Format::Viz benchmark
diff --git a/tests/autoTestAll.Template.js b/tests/autoTestAll.Template.js
index 013798381..9e237b389 100644
--- a/tests/autoTestAll.Template.js
+++ b/tests/autoTestAll.Template.js
@@ -19,6 +19,7 @@
exports.testMD2HTMLWithPrefix = function() {
var md = "# test 1"
+ var extraTag = ''
ow.loadTemplate()
@@ -29,6 +30,36 @@
out = ow.template.html.parseMapInHTML({ a: 1 }, __, "/myprefix")
ow.test.assert(out.indexOf('src="/myprefix/js/openafsigil.js"') >= 0, true, "Problem with ow.template.html.parseMapInHTML prefix support")
ow.test.assert(out.indexOf('href="/myprefix/css/nJSMap.css"') >= 0, true, "Problem with ow.template.html.parseMapInHTML stylesheet prefix support")
+
+ ow.template.__mdHTMLTExtras.push({ t: "OAFTESTKATEX", e: extraTag })
+ out = ow.template.parseMD2HTML("OAFTESTKATEX", true, __, __, __, "/myprefix")
+ ow.test.assert(out.indexOf('src="/myprefix/js/katex.min.js"') >= 0, true, "Problem with ow.template.parseMD2HTML extra script prefix support")
+ ow.test.assert(out.indexOf('href="/myprefix/css/katex.min.css"') >= 0, true, "Problem with ow.template.parseMD2HTML extra stylesheet prefix support")
+ ow.template.__mdHTMLTExtras = $from(ow.template.__mdHTMLTExtras).notEquals("t", "OAFTESTKATEX").select()
+
+ var tmpBase = io.createTempFile("oaf_template_", "")
+ io.rm(tmpBase)
+ io.mkdir(tmpBase)
+ io.mkdir(tmpBase + "/fonts")
+
+ var cssFile = tmpBase + "/katex.min.css"
+ var jsFile = tmpBase + "/katex.min.js"
+ var fontFile = tmpBase + "/fonts/test.woff2"
+
+ io.writeFileString(cssFile, '@font-face{src:url(fonts/test.woff2)} body{font-family:test;}')
+ io.writeFileString(jsFile, 'console.log("katex");')
+ io.writeFileString(fontFile, 'font-data')
+
+ ow.template.__srcPath["/css/katex.min.css"] = cssFile
+ ow.template.__srcPath["/js/katex.min.js"] = jsFile
+ ow.template.__srcPath["/css/fonts/test.woff2"] = fontFile
+ ow.template.__srcPath["/fonts/test.woff2"] = fontFile
+
+ out = ow.template.html.genStaticVersion('')
+ ow.test.assert(out.indexOf('/js/katex.min.js') < 0, true, "Problem with ow.template.html.genStaticVersion script inlining")
+ ow.test.assert(out.indexOf('/css/katex.min.css') < 0, true, "Problem with ow.template.html.genStaticVersion stylesheet inlining")
+ ow.test.assert(out.indexOf('url(fonts/') < 0, true, "Problem with ow.template.html.genStaticVersion stylesheet relative asset inlining")
+ ow.test.assert(out.indexOf('data:font/') >= 0 || out.indexOf('data:application/font-') >= 0 || out.indexOf('data:application/octet-stream') >= 0, true, "Problem with ow.template.html.genStaticVersion font data URL inlining")
};
exports.testSimpleTemplate = function() {