@@ -637,9 +637,9 @@ local macroname = plainrdr.htmlSpan( ct.parse_span(m.macro, b.origin), b,s) local r = b.origin:ref(macroname) if type(r) ~= 'string' then - b.origin:fail('%s is an object, not a reference', t.ref) + b.origin:fail('%s is an object, not a reference', r.id) end local mctx = b.origin:clone() mctx.invocation = m return htmlSpan(ct.parse_span(r, mctx),b,s) @@ -735,9 +735,11 @@ if ct.sec.is(b.captions) then if not (opts['fossil-uv'] or opts.snippet) then addStyle 'header' end - local h = math.min(6,math.max(1,b.captions.depth)) + -- use correct styling in subdocuments + local visDepth = b.captions.depth + (b.origin.docDepth or 0) + local h = math.min(6,math.max(1,visDepth)) return tag(f('h%u',h), nil, sr.htmlSpan(b.spans, b, s), b) else -- handle other uses of labels here end @@ -789,20 +791,19 @@ return ''; end; } - function block_renderers.quote(b,s) + local function renderSubdoc(doc) local ir = {} - local toIR = block_renderers - for i, sec in ipairs(b.doc.secorder) do + for i, sec in ipairs(doc.secorder) do local secnodes = {} for i, bl in ipairs(sec.blocks) do - if toIR[bl.kind] then - table.insert(secnodes, toIR[bl.kind](bl,sec)) + if block_renderers[bl.kind] then + table.insert(secnodes, block_renderers[bl.kind](bl,sec)) end end if next(secnodes) then - if b.doc.secorder[2] then --#secs>1? + if doc.secorder[2] then --#secs>1? -- only wrap in a section if >1 section table.insert(ir, tag('section', {id = getSafeID(sec)}, secnodes)) @@ -810,12 +811,184 @@ ir = secnodes end end end - return tag('blockquote', b.id and {id=getSafeID(b)} or {}, catenate(ir)) + return ir + end + + local function flatten(t) + if t == nil then + return '' + elseif type(t) == 'string' then + return t + elseif type(t) == 'table' then + if t[1] then + return catenate(ss.map(flatten, t)) + elseif t.tag then + return tag(t.tag, t.attrs or {}, flatten(t.nodes)) + elseif t.elt then + return tag(t.elt, t.attrs or {}) + end + end + end + + function block_renderers.embed(b,s) + local obj + if b.rsrc + then obj = b.rsrc + else obj = b.origin:ref(b.ref) + end + local function htmlURI(u) + local family = u:canfetch() + if family == 'file' or + (family == 'http' and u.namespace == nil) then + -- TODO asset: + return u.path + else + return tostring(u) + end + end + local function uriForSource(s) + if s.mode == 'link' or s.mode == 'auto' then + return htmlURI(s.uri) + elseif s.mode == 'embed' then + local mime = s.mime:clone() + mime.opts = {} + return string.format('data:%s;base64,%s', mime, ss.str.b64e(s.raw)) + end + end + --figure out how to embed the given object + local embedActs = { + {ss.mime'image/*', function(s,ctr) + if s == nil then + return {tag = "picture", nodes = {}} + else + local uri = uriForSource(s) + local fbimg, idx + if next(ctr.nodes) == nil then + idx = 1 + fbimg = { + elt = 'img'; --fallback + attrs = { + alt = ''; + src = uri; + }; + } + else idx = #ctr.nodes end + table.insert(ctr.nodes, idx, { + elt = 'source'; --fallback + attrs = { srcset = uri; }; + }) + if fbimg then + table.insert(ctr.nodes,fbimg) + else + -- fallback should be lowest-prio image + ctr.nodes[#ctr.nodes].attrs.src = uri; + end + end + end}; + {ss.mime'text/x.cortav', function(s,ctr) + if s == nil then + return {} + elseif next(ctr) == nil then + if (s.mode == 'embed' or s.mode == 'auto') and s.doc then + ctr.tag = 'div'; -- kinda hacky, maybe fix + ctr.nodes = renderSubdoc(s.doc) + elseif s.mode == 'link' then + -- yeah this is not gonna work my dude + ctr.elt = 'embed'; + ctr.attrs = { + type = 'text/x.cortav'; + src = htmlURI(s.uri); + } + end + end + end}; + {ss.mime'text/html', function(s,ctr) + if s == nil then + return {} + elseif next(ctr) == nil then + if (s.mode == 'embed' or s.mode == 'auto') and s.raw then + ctr.tag = 'div' + ctr.nodes = s.raw + elseif s.mode == 'link' then + ctr.elt = 'embed'; + ctr.attrs = { + type = 'text/html'; + src = htmlURI(s.uri); + } + end + end + end}; + {ss.mime'text/*', function(s,ctr) + if s == nil then + return {} + elseif next(ctr) == nil then + local mime = s.mime:clone() + mime.opts={} + if (s.mode == 'embed' or s.mode == 'auto') and s.raw then + ctr.tag = 'pre'; + ctr.nodes = s.raw + elseif s.mode == 'link' then + ctr.elt = 'embed'; + ctr.attrs = { + type = tostring(mime); + src = htmlURI(s.uri); + } + end + end + end}; + } + + local rtype + local fallback + for n, src in ipairs(obj.srcs) do + if fallback == nil and (src.mode == 'link' or src.mode == 'auto') then + fallback = src + end + + for i, ea in ipairs(embedActs) do + if ea[1] < src.mime then -- fits! + rtype = ea + goto compatFound + end + end + end + -- nothing found; install fallback link + if fallback then + local lnk = htmlURI(fallback.uri) + return tag('a', {href=lnk}, + tag('div',{class=xref}, + string.format("→ %s [%s]", b.cap or '', tostring(fallback.mime)))) + else + addStyle 'docmeta' + return tag('div',{class="render-warn"}, + 'could not embed object type ' .. tostring(obj.srcs.mime)) + end + + ::compatFound:: + local top = rtype[2]() -- create container + for n, src in ipairs(obj.srcs) do + if rtype[1] < src.mime then + rtype[2](src, top) + end + end + local ft = flatten(top) + return ft + end + + function block_renderers.macro(b,s) + local all = renderSubdoc(b.doc) + local cat = catenate(ss.map(flatten,all)) + return tag('div', {}, cat) + end + + function block_renderers.quote(b,s) + local ir = renderSubdoc(b.doc) + return tag('blockquote', b.id and {id=getSafeID(b)} or {}, catenate(ss.map(flatten,ir))) end - return block_renderers; + return block_renderers end local function getRenderers(procs) local span_renderers = getSpanRenderers(procs)