-- ANTI-CAPITALIST SOFTWARE LICENSE (v 1.4) -- -- marigold-cgi Copyright (c) 2022 Kate Swanson -- -- This is anti-capitalist software, released for free use by individuals and -- organizations that do not operate by capitalist principles. -- -- Permission is hereby granted, free of charge, to any person or organization ( -- the "User") obtaining a copy of this software and associated documentation -- files (the "Software"), to use, copy, modify, merge, distribute, and/or sell -- copies of the Software, subject to the following conditions: -- -- 1. The above copyright notice and this permission notice shall be included in -- all copies or modified versions of the Software. -- -- 2. The User is one of the following: -- a. An individual person, laboring for themselves -- b. A non-profit organization -- c. An educational institution -- d. An organization that seeks shared profit for all of its members, and allows -- non-members to set the cost of their labor -- -- 3. If the User is an organization with owners, then all owners are workers and -- all workers are owners with equal equity and/or equal vote. -- -- 4. If the User is an organization, then the User is not law enforcement or -- military, or working for or under either. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY -- KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE -- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local marigold = {} marigold.h = function(tag_type, content, tbl) if type(content) == 'table' and tbl == nil then tbl = content content = '' end if content == nil then tbl = {} content = '' end local tag = {} tag.tag = tag_type tag.content = content tag.attributes = {} tag.children = {} if tbl then -- add attributes for k, v in pairs(tbl) do if type(k) == 'string' then tag.attributes[k] = v end end -- add children for _, child in ipairs(tbl) do table.insert(tag.children, child) end end return tag end local function render_internal(tbl, indent_level) indent_level = indent_level or 0 local indent = string.rep('\t', indent_level) -- generate attribute strings local attributes = {} for k, v in pairs(tbl.attributes) do table.insert(attributes, string.format(' %s="%s"', k, v)) end if test then -- sort alphabetically for well-defined testing table.sort(attributes) end local a = '' for _, attrib in ipairs(attributes) do a = a .. attrib end local open = string.format('<%s%s>', tbl.tag, a) local close = string.format('', tbl.tag) if #tbl.children == 0 then return string.format('%s%s%s%s', indent, open, tbl.content, close) end local children = '' for _, child in ipairs(tbl.children) do children = children .. render_internal(child, indent_level+1) .. '\n' end return string.format('%s%s%s\n%s%s%s', indent, open, tbl.content, children, indent, close) end marigold.prerender = render_internal marigold.render = function(tbl) local str = render_internal(tbl, 0) return '\n' .. str end -- built-in svg types local function set(tbl) local s = {} for _, v in ipairs(tbl) do s[v] = true end return s end local function validate(tagname, valid, tbl) local valid_set = set(valid) -- inject the core svg attributes valid_set.id = true valid_set.tabindex = true valid_set.lang = true valid_set['xml:space'] = true valid_set.class = true valid_set.style = true for k in pairs(tbl) do if not valid_set[k] then return string.format( "ERROR: attempted to set invalid attribute '%s' on tag of type %s", k, tagname ) end end return nil end local function maketag(tag, content, tbl, valid) local t = marigold.h(tag, content, tbl) local err = validate(tag, valid, t.attributes) if err then return nil, err end return t, nil end marigold.svg = function(tbl) local tbl = tbl or {} tbl.xmlns = 'http://www.w3.org/2000/svg' local tag, err = maketag('svg', '', tbl, { 'xmlns', 'viewBox', 'width', 'height' }) if err then error(err) end return tag end marigold.g = function(tbl) local tbl = tbl or {} local tag, err = maketag('g', '', tbl, { 'transform' }) if err then error(err) end return tag end marigold.rect = function(tbl) local tag, err = maketag('rect', '', tbl, { 'x', 'y', 'width', 'height', 'rx', 'ry', 'transform', }) if err then error(err) end return tag end marigold.text = function(content, tbl) local tag, err = maketag( 'text', content, tbl, { 'lengthAdjust', 'x', 'y', 'dx', 'dy', 'rotate', 'textLength' } ) if err then error(err) end return tag end return marigold