@@ -339,6 +339,7 @@ local function setup()
339339 -- Lua libraries.
340340 json = jsonLib,
341341 lfs = lfs,
342+ utf8 = utf8, -- @Doc
342343 xml = xmlLib,
343344
344345 toml = { -- @Cleanup: Don't use 3rd party TOML library.
@@ -369,9 +370,11 @@ local function setup()
369370 generatorMeta = generatorMeta,
370371 getFilename = getFilename,
371372 getKeys = getKeys,
373+ gmatchAndBetween = gmatchAndBetween, -- @Doc
372374 indexOf = indexOf,
373375 ipairsr = ipairsr,
374376 isAny = isAny,
377+ isValueHtml = doesValueLookLikeHtml, -- @Doc
375378 markdown = markdownToHtml,
376379 max = math.max,
377380 min = math.min,
@@ -713,12 +716,140 @@ local function setup()
713716 return _dataParsers
714717 end,
715718
719+ -- array = function( array, length ) -- @Doc
720+ clampArray = function(t, len)
721+ for i = #t, len+1, -1 do
722+ t[i] = nil
723+ end
724+ return t
725+ end,
726+
727+ -- html = summarize( html, maxCharacters [, keepAnchorsAndImages=false ] ) -- @Doc
728+ summarize = function(html, maxChars, keepSomeElements)
729+ html = "<div>" .. html .. "</div>"
730+ local doc = assert(xmlLib.parseHtml(html))
731+ local protected
732+
733+ -- Protect certain things, like links.
734+ if keepSomeElements then
735+ protected = {--[[ element1, ... ]]}
736+
737+ xmlLib.walk(doc, false, function(tag, el)
738+ for i, childNode in ipairs(el) do
739+ if xmlLib.isText(childNode) then
740+ -- void
741+
742+ elseif childNode.tag == "a" and childNode.attr.href and not childNode.attr.href:find"^javascript:" then
743+ table.insert(protected, xmlLib.element("a", {href=childNode.attr.href, childNode:getText()}))
744+ el[i] = "$$PLACEHOLDER" .. #protected .. "$$" -- @Robustness: Make sure the string doesn't exist in the original content.
745+
746+ elseif childNode.tag == "img" and childNode.attr.src then
747+ table.insert(protected, xmlLib.element("img", {src=childNode.attr.src, alt=(childNode.attr.alt or "")}))
748+ el[i] = "$$PLACEHOLDER" .. #protected .. "$$" -- @Robustness: Make sure the string doesn't exist in the original content.
749+ end
750+ end
751+ end)
752+ end
753+
754+ -- Make the content into a list of paragraphs or similar.
755+ for i, childNode in ipairsr(doc) do
756+ if xmlLib.isText(childNode) then
757+ table.remove(doc, i)
758+
759+ else
760+ if childNode.tag == "ul" or childNode.tag == "ol" then
761+ doc[i] = xmlLib.newElement(childNode.tag)
762+
763+ for li in childNode:eachChildElement() do
764+ local text = trim(li:getText():gsub("%s+", " "))
765+ table.insert(doc[i], xmlLib.element("li", text))
766+ end
767+
768+ else
769+ local text = trim(childNode:getText():gsub("%s+", " "))
770+ doc[i] = xmlLib.element("p", text)
771+ end
772+
773+ if doc[i+1] then table.insert(doc, i+1, "\n") end -- Not needed, but it looks nicer.
774+ end
775+ end
776+
777+ -- Remove protections.
778+ if keepSomeElements then
779+ xmlLib.walk(doc, true, function(tag, el)
780+ for i, childNode in ipairsr(el) do
781+ if xmlLib.isText(childNode) and childNode:find("$$PLACEHOLDER", 1, true) then
782+ local newNodes = {}
783+
784+ for pos, isMatch, textOrProtIndex in gmatchAndBetween(childNode, "%$%$PLACEHOLDER(%d+)%$%$") do
785+ if isMatch then
786+ table.insert(newNodes, protected[tonumber(textOrProtIndex)])
787+ else
788+ table.insert(newNodes, textOrProtIndex)
789+ end
790+ end
791+
792+ table.remove(el, i)
793+
794+ for _, node in ipairsr(newNodes) do
795+ table.insert(el, i, node)
796+ end
797+ end
798+ end
799+ end)
800+ end
801+
802+ -- Limit text length.
803+ local charsRemaining = maxChars
804+ local hasText = {}
805+
806+ local function limit(node, parentEl, i)
807+ if charsRemaining <= 0 then
808+ parentEl[i] = ""
809+
810+ elseif xmlLib.isText(node) then
811+ hasText[parentEl] = true
812+ local len = utf8.getLength(node)
813+ charsRemaining = charsRemaining - len
814+
815+ if charsRemaining < 0 then
816+ !local ELLIPSIS = "(...)"
817+ local nextCharPos = #node + 1
818+
819+ for i = charsRemaining, !(#ELLIPSIS-1) do
820+ nextCharPos = utf8.getStartOfCharacter(node, nextCharPos-1)
821+
822+ if not nextCharPos then
823+ nextCharPos = 1
824+ break
825+ end
826+ end
827+
828+ parentEl[i] = node:sub(1, nextCharPos-1) .. !(ELLIPSIS)
829+ end
830+
831+ else
832+ for i, childNode in ipairs(node) do
833+ limit(childNode, node, i)
834+ end
835+
836+ if hasText[node] and parentEl then
837+ hasText[parentEl] = true
838+ end
839+ end
840+ end
841+
842+ limit(doc, nil, nil)
843+
844+ return (doc:contentsToHtml())
845+ end,
846+
716847 -- Context functions.
717848 ----------------------------------------------------------------
718849
719- echo = function(s )
850+ echo = function(v )
720851 assertContext("template", "echo")
721- s = tostringForTemplates(s )
852+ local s = tostringForTemplates(v )
722853
723854 local ctx = getContext"template"
724855 if ctx.enableHtmlEncoding then
@@ -733,6 +864,17 @@ local function setup()
733864 table.insert(getContext"template".out, tostringForTemplates(s))
734865 end,
735866
867+ echoSmart = function(v) -- @Doc
868+ assertContext("template", "echoSmart")
869+ if v == nil then
870+ -- void Echo nothing.
871+ elseif doesValueLookLikeHtml(v) then
872+ scriptEnvironmentGlobals.echoRaw(v)
873+ else
874+ scriptEnvironmentGlobals.echo(v)
875+ end
876+ end,
877+
736878 echof = function(s, ...)
737879 assertContext("template", "echof")
738880 !ARGS "s:string"
@@ -928,6 +1070,7 @@ local function setup()
9281070 -- These functions are used by metaprograms.
9291071 scriptEnvironmentGlobals.echo__ = scriptEnvironmentGlobals.echo
9301072 scriptEnvironmentGlobals.echoRaw__ = scriptEnvironmentGlobals.echoRaw
1073+ scriptEnvironmentGlobals.echoSmart__ = scriptEnvironmentGlobals.echoSmart
9311074 scriptEnvironmentGlobals.fori__ = ipairs
9321075 scriptEnvironmentGlobals.foriReverse__ = ipairsr
9331076 scriptEnvironmentGlobals.lock__ = scriptEnvironmentGlobals.lock
@@ -1067,10 +1210,11 @@ local function buildWebsite()
10671210 )
10681211 end
10691212
1070- site.title.v = getV("title", "", "string")
1071- site.baseUrl.v = getV("baseUrl", "", "string")
1072- site.languageCode.v = getV("languageCode", "", "string")
1073- site.defaultLayout.v = getV("defaultLayout", "page", "string")
1213+ site.title.v = getV("title", site.title.v, "string")
1214+ site.description.v = getV("description", site.description.v, "string")
1215+ site.baseUrl.v = getV("baseUrl", site.baseUrl.v, "string")
1216+ site.languageCode.v = getV("languageCode", site.languageCode.v, "string")
1217+ site.defaultLayout.v = getV("defaultLayout", site.defaultLayout.v, "string")
10741218
10751219 site.redirections.v = getT("redirections", site.redirections.v, "string", "string")
10761220
@@ -1080,8 +1224,8 @@ local function buildWebsite()
10801224
10811225 site._fileTypes = getT("types", site._fileTypes, "string", "string")
10821226 site._fileProcessors = getT("processors", site._fileProcessors, "string", "function")
1083- local customDataTextParsers = getT("dataTextParsers", {}, "string", "function") -- @Doc
1084- local customDataBinaryParsers = getT("dataBinaryParsers", {}, "string", "function") -- @Doc
1227+ local customDataTextParsers = getT("dataTextParsers", {}, "string", "function")
1228+ local customDataBinaryParsers = getT("dataBinaryParsers", {}, "string", "function")
10851229
10861230 local htaRedirect = getV("htaccess.redirect", false, "boolean")
10871231 local htaWww = getV("htaccess.www", false, "boolean")
@@ -1211,7 +1355,7 @@ local function buildWebsite()
12111355 -- Template.
12121356 elseif site._fileTypes[extLower] then
12131357 -- Generate these later so e.g. urlExists() works better.
1214- local pagesArray = (site._fileTypes[extLower] == "othertemplate" and nonPagePages or site._pages)
1358+ local pagesArray = (( site._fileTypes[extLower] == "markdown" or site._fileTypes[extLower] == "html") and site._pages or nonPagePages )
12151359 table.insert(pagesArray, newPage(pathRel, false))
12161360
12171361 -- Special.
0 commit comments