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() {