More actions
m (count images for regular Gallery Sections correctly) |
m (test classes) |
||
Line 9: | Line 9: | ||
--]] | --]] | ||
local cargo = mw.ext.cargo | local cargo = mw.ext.cargo | ||
local | ------------------------------------- | ||
-- Gallery Section Class | |||
------------------------------------- | |||
local GalleryTemplate = {} | |||
GalleryTemplate.__index = GalleryTemplate | |||
GalleryTemplate.fields = { | |||
character = true, | |||
source = true, | |||
section = true, | |||
limit = true, | |||
widths = true, | |||
header = true, | |||
title = true, | |||
sort = true, | |||
see_all = true, | |||
see_cameos = true, | |||
--Gallery Section (BBRadio) | |||
season = true, | |||
episode = true, | |||
widths = true, | |||
--Gallery Section | |||
cat = true, | |||
cat2 = true, | |||
--Misc/Shared | |||
count_all = true, | |||
count_cameos = true, | |||
images_in_section = true, | |||
} | |||
GalleryTemplate.templateMethods = { | |||
['Gallery Section (Cargo)'] = 'makeGallerySectionCargo', | |||
['Gallery Section (BBRadio)'] = 'makeGallerySectionBBRadio', | |||
['Gallery Section'] = 'makeGallerySection', | |||
} | |||
function GalleryTemplate.new(frame, templateType, params) | |||
local self = setmetatable({}, GalleryTemplate) | |||
self.images_in_section = 0 | |||
self.templateType= templateType | |||
self.frame = frame | |||
for field in pairs(GalleryTemplate.fields) do | |||
self[field] = params[field] or '' | |||
end | |||
return self | |||
end | |||
function GalleryTemplate:__tostring() | |||
local method = GalleryTemplate.templateMethods[self.templateType] | |||
if method then | |||
mw.log("GalleryTemplate:__toString(" .. method .. ")") | |||
--return self[method](self, self.frame) | |||
end | |||
return '' | |||
end | |||
function GalleryTemplate.makeGallerySectionCargo(frame) | |||
self.character = cargo_escape(self.character) | |||
self.count_all = Gallery._countImages_GallerySectionCargo(false) or 0 | |||
self.count_cameos = Gallery._countImages_GallerySectionCargo(true) or 0 | |||
local total_images = self.count_all + self.count_cameos | |||
if self.count_all > 0 then | |||
self.see_all = 'y' | |||
end | |||
if self.count_cameos > 0 then | |||
self.see_cameos = 'y' | |||
end | |||
if total_images > 0 then | |||
expanded_template = Gallery._expandTemplate_GallerySectionCargo(frame) | |||
--table.insert(t_galleries, expanded_template) | |||
self.images_in_section = self.images_in_section + total_images | |||
end | end | ||
return expanded_template | |||
return | end | ||
function GalleryTemplate.makeGallerySectionBBRadio(frame) | |||
self.character = cargo_escape(self.character) | |||
self.count_all = Gallery._countImages_GallerySectionBBRadio() or 0 | |||
expanded_template = Gallery._expandTemplate_GallerySectionBBRadio(frame) | |||
--table.insert(t_galleries, expanded_template) | |||
self.images_in_section = self.images_in_section + self.count_all | |||
return expanded_template | |||
end | |||
function GalleryTemplate.makeGallerySection(frame) | |||
self.count_all = Gallery._countImages_GallerySection() or 0 | |||
expanded_template = Gallery._expandTemplate_GallerySection(frame) | |||
--table.insert(t_galleries, expanded_template) | |||
self.images_in_section = self.images_in_section + self.count_all | |||
return expanded_template | |||
end | end | ||
-- counts the number of images with this character (or cameo) for sections using Template:Gallery Section (Cargo) | -- counts the number of images with this character (or cameo) for sections using Template:Gallery Section (Cargo) | ||
-- countCameos: boolean | -- countCameos: boolean | ||
function | function GalleryTemplate._countImages_GallerySectionCargo(countCameos) | ||
tables = 'Files' | tables = 'Files' | ||
fields = 'COUNT(Files._pageName)' | fields = 'COUNT(Files._pageName)' | ||
character = | character = self.character or '' | ||
source = | source = self.source or '' | ||
section = | section = self.section or '' | ||
where_clause_parts = {} | where_clause_parts = {} | ||
Line 182: | Line 140: | ||
end | end | ||
function | function GalleryTemplate._countImages_GallerySectionBBRadio() | ||
tables = 'Files,BBRadio_Cuts' | tables = 'Files,BBRadio_Cuts' | ||
fields = 'COUNT(Files._pageName)' | fields = 'COUNT(Files._pageName)' | ||
join_on = 'Files._pageName=BBRadio_Cuts._pageName' | join_on = 'Files._pageName=BBRadio_Cuts._pageName' | ||
mw.log( | mw.log("Count images for " .. self.character .. ' season ' .. self.season) | ||
where_clause = 'Files.Characters HOLDS "' .. | where_clause = 'Files.Characters HOLDS "' .. self.character .. '" AND BBRadio_Cuts.Season="' .. self.season .. '"' | ||
local args = { | local args = { | ||
Line 206: | Line 160: | ||
end | end | ||
function | function GalleryTemplate._countImages_GallerySection() | ||
cat1 = self.character or '' | |||
cat1 = | cat2 = self.cat or '' | ||
cat2 = | cat3 = self.cat2 or '' | ||
cat3 = | |||
local query = {} | local query = {} | ||
Line 221: | Line 174: | ||
return count | return count | ||
end | end | ||
function | function GalleryTemplate._expandTemplate_GallerySectionCargo(frame) | ||
_limit = tonumber(self.limit) or 4 | |||
if frame[expandTemplate] == nil then | |||
_limit = tonumber( | mw.log("NIL FRAME: _expandTemplate_GallerySectionCargo " .. self.title .. " could not be executed") | ||
return '' | |||
end | |||
gallery = frame:expandTemplate{ title = 'Gallery Section (Cargo)', args = { | gallery = frame:expandTemplate{ title = 'Gallery Section (Cargo)', args = { | ||
character = | character = self.character, | ||
source = | source = self.source, | ||
section = | section = self.section, | ||
limit = _limit, | limit = _limit, | ||
title = | title = self.title, | ||
header = | header = self.header, | ||
see_all = | see_all = self.see_all, | ||
see_cameos = | see_cameos = self.see_cameos, | ||
count_all = | count_all = self.count_all, | ||
count_cameos = | count_cameos = self.count_cameos | ||
}} | }} | ||
Line 252: | Line 200: | ||
end | end | ||
function | function GalleryTemplate._expandTemplate_GallerySectionBBRadio(frame) | ||
_limit = tonumber(self.limit) or 4 | |||
_widths = tonumber(self.widths) or 250 | |||
if frame[expandTemplate] == nil then | |||
mw.log("NIL FRAME: _expandTemplate_GallerySectionBBRadio " .. self.title .. " could not be executed") | |||
_limit = tonumber( | return '' | ||
_widths = tonumber( | end | ||
gallery = frame:expandTemplate { title = 'Gallery Section (BBRadio)', args = { | gallery = frame:expandTemplate { title = 'Gallery Section (BBRadio)', args = { | ||
self.character, | |||
self.season, | |||
self.episode, | |||
self.header, | |||
self.title, | |||
widths = _widths, | widths = _widths, | ||
limit = _limit, | limit = _limit, | ||
count_all = | count_all = self.count_all | ||
}} | }} | ||
Line 276: | Line 223: | ||
end | end | ||
function | function GalleryTemplate._expandTemplate_GallerySection(frame) | ||
_limit = tonumber(self.limit) or 4 | |||
_widths = tonumber(self.widths) or 250 | |||
_count_all = self.count_all or 0 | |||
if frame[expandTemplate] == nil then | |||
_limit = tonumber( | mw.log("NIL FRAME: _expandTemplate_GallerySection " .. _title .. " could not be executed") | ||
_widths = tonumber( | return '' | ||
_count_all = | end | ||
gallery = frame:expandTemplate { title = 'Gallery Section', args = { | gallery = frame:expandTemplate { title = 'Gallery Section', args = { | ||
self.character, | |||
self.cat, | |||
self.cat2, | |||
self.header, | |||
self.title, | |||
widths = _widths, | widths = _widths, | ||
limit = _limit, | limit = _limit, | ||
Line 299: | Line 246: | ||
return gallery | return gallery | ||
end | end | ||
function GalleryTemplate:getNumberOfImages() | |||
return self.images_in_section | |||
end | |||
------------------------------------- | |||
-- Gallery Class | |||
------------------------------------- | |||
local Gallery = {} | |||
Gallery.__index = Gallery | |||
function Gallery.new(data, frame) | |||
local self = setmetatable({}, Gallery) | |||
self.galleryData = mw.loadData(data) | |||
self.ordering = self.galleryData.ordering | |||
self.sections = self.galleryData.sections | |||
self.frame = frame | |||
return self | |||
end | |||
function Gallery:__tostring() | |||
local frame = self.frame | |||
text = {} | |||
_char = frame.args['character'] or '' | |||
for i,v in ipairs (self.ordering) do | |||
category = v | |||
local params = self.sections[v] or '' | |||
if params == '' then | |||
else | |||
local t_section = {} | |||
local t_galleries = {} | |||
local images_in_section = 0 | |||
local current_subheader = '' | |||
-- add a wrapper div if there's a class to apply to the section | |||
class = params.class or '' | |||
if class ~= '' then | |||
table.insert(t_section, '<div class="' .. class .. '">') | |||
end | |||
-- v is a table of templates in this section | |||
for k2,v2 in pairs(params) do | |||
template = v2.template or '' | |||
local title = params.title or 'TITLE PLACEHOLDER' | |||
mw.log(title .. ": " .. template) | |||
if template == 'Header' then | |||
-- if this is not the first subheader in this section | |||
if current_subheader ~= '' then | |||
-- check if the previous subheader's galleries had images in them | |||
if images_in_section > 0 then | |||
-- insert the previous subheader into the section | |||
table.insert(t_section, current_subheader) | |||
-- insert the previous subheader's galleries into the section | |||
g = table.concat(t_galleries) | |||
if g ~= "" then | |||
table.insert(t_section, g) | |||
end | |||
-- reset galleries and images for the next subsection | |||
clear_table(t_galleries) | |||
images_in_section = 0 | |||
end | |||
end | |||
-- TODO fix placeholder text | |||
local header = params.header or 'HEADER PLACEHOLDER' | |||
-- save the new subheader and wait to see if the following galleries have any images in them | |||
current_subheader = '<' .. header .. '>' .. title .. '</' .. header .. ">\n" | |||
elseif template ~= '' then | |||
images_in_section = images_in_section | |||
temp = GalleryTemplate.new(frame, template, params) | |||
table.insert(t_galleries, tostring(temp)) | |||
images_in_section = tonumber(temp:getNumberOfImages()) or 0 | |||
end | |||
end | |||
-- check the number of images in the section | |||
mw.log(category .. " images_in_section = " .. images_in_section) | |||
-- if there is a subheader to insert, then insert it | |||
if current_subheader ~= "" and images_in_section > 0 then | |||
table.insert(t_section, current_subheader) | |||
end | |||
-- if there are galleries to insert, then insert them | |||
g = table.concat(t_galleries) | |||
if g ~= "" then | |||
table.insert(t_section, g) | |||
else | |||
mw.log("t_galleries is empty, with " .. images_in_section .. " images total in this section") | |||
end | |||
-- close the wrapping div if one was added | |||
if class ~= "" then | |||
table.insert(t_section, "</div>") | |||
end | |||
s = table.concat(t_section) | |||
if s ~= "" then | |||
-- insert the section, prepended by the section header, to the final text | |||
table.insert(text, '<h2>' .. category .. '</h2>\n' .. s) | |||
end | |||
end | |||
end | |||
gallery = table.concat(text) | |||
return gallery | |||
end | |||
------------------------------------- | |||
-- Main | |||
------------------------------------- | |||
local p = {} | |||
function p.main(frame) | |||
data = frame.args.data or 'Module:Sandbox/Chao/data' | |||
gallery = Gallery.new(data, frame) | |||
--mw.log(dump(gallery)) | |||
--mw.log("rendered: \n" .. gallery:makeGallery()) | |||
return tostring(gallery) | |||
end | |||
------------------------------------- | |||
-- Tests | |||
------------------------------------- | |||
function p.testExpandTemplate_1(frame) | function p.testExpandTemplate_1(frame) | ||
Line 351: | Line 427: | ||
end | end | ||
end | end | ||
------------------------------------- | |||
-- Utility Functions | |||
------------------------------------- | |||
-- clear a table | -- clear a table |
Revision as of 03:23, 4 December 2019
--[[
{{#invoke:Sandbox/Chao|GallerySection|character=Noel Vermillion|section=Portraits}}
simulate passing a frame to the function in the console with:
myFrame = { args = { character='Noel Vermillion', section='Portraits' } }
=p.GallerySection ( myFrame )
=p.testExpandTemplate( myFrame )
=p.render(myFrame)
--]]
local cargo = mw.ext.cargo
-------------------------------------
-- Gallery Section Class
-------------------------------------
local GalleryTemplate = {}
GalleryTemplate.__index = GalleryTemplate
GalleryTemplate.fields = {
character = true,
source = true,
section = true,
limit = true,
widths = true,
header = true,
title = true,
sort = true,
see_all = true,
see_cameos = true,
--Gallery Section (BBRadio)
season = true,
episode = true,
widths = true,
--Gallery Section
cat = true,
cat2 = true,
--Misc/Shared
count_all = true,
count_cameos = true,
images_in_section = true,
}
GalleryTemplate.templateMethods = {
['Gallery Section (Cargo)'] = 'makeGallerySectionCargo',
['Gallery Section (BBRadio)'] = 'makeGallerySectionBBRadio',
['Gallery Section'] = 'makeGallerySection',
}
function GalleryTemplate.new(frame, templateType, params)
local self = setmetatable({}, GalleryTemplate)
self.images_in_section = 0
self.templateType= templateType
self.frame = frame
for field in pairs(GalleryTemplate.fields) do
self[field] = params[field] or ''
end
return self
end
function GalleryTemplate:__tostring()
local method = GalleryTemplate.templateMethods[self.templateType]
if method then
mw.log("GalleryTemplate:__toString(" .. method .. ")")
--return self[method](self, self.frame)
end
return ''
end
function GalleryTemplate.makeGallerySectionCargo(frame)
self.character = cargo_escape(self.character)
self.count_all = Gallery._countImages_GallerySectionCargo(false) or 0
self.count_cameos = Gallery._countImages_GallerySectionCargo(true) or 0
local total_images = self.count_all + self.count_cameos
if self.count_all > 0 then
self.see_all = 'y'
end
if self.count_cameos > 0 then
self.see_cameos = 'y'
end
if total_images > 0 then
expanded_template = Gallery._expandTemplate_GallerySectionCargo(frame)
--table.insert(t_galleries, expanded_template)
self.images_in_section = self.images_in_section + total_images
end
return expanded_template
end
function GalleryTemplate.makeGallerySectionBBRadio(frame)
self.character = cargo_escape(self.character)
self.count_all = Gallery._countImages_GallerySectionBBRadio() or 0
expanded_template = Gallery._expandTemplate_GallerySectionBBRadio(frame)
--table.insert(t_galleries, expanded_template)
self.images_in_section = self.images_in_section + self.count_all
return expanded_template
end
function GalleryTemplate.makeGallerySection(frame)
self.count_all = Gallery._countImages_GallerySection() or 0
expanded_template = Gallery._expandTemplate_GallerySection(frame)
--table.insert(t_galleries, expanded_template)
self.images_in_section = self.images_in_section + self.count_all
return expanded_template
end
-- counts the number of images with this character (or cameo) for sections using Template:Gallery Section (Cargo)
-- countCameos: boolean
function GalleryTemplate._countImages_GallerySectionCargo(countCameos)
tables = 'Files'
fields = 'COUNT(Files._pageName)'
character = self.character or ''
source = self.source or ''
section = self.section or ''
where_clause_parts = {}
if countCameos then
table.insert(where_clause_parts, "Files.Cameos HOLDS '" .. character .. "'")
else
table.insert(where_clause_parts, "Files.Characters HOLDS '" .. character .. "'")
end
if source == '' then else table.insert(where_clause_parts, "Files.Source HOLDS '" .. source .. "'") end
if section == '' then else table.insert(where_clause_parts, "Files.Gallery_Sections HOLDS '" .. section .. "'") end
where_clause = concatvalues(where_clause_parts, " AND ")
local args = {
where = where_clause
}
result = cargo.query( tables, fields, args )
count = result[1][fields] or '0'
return tonumber(count)
end
function GalleryTemplate._countImages_GallerySectionBBRadio()
tables = 'Files,BBRadio_Cuts'
fields = 'COUNT(Files._pageName)'
join_on = 'Files._pageName=BBRadio_Cuts._pageName'
mw.log("Count images for " .. self.character .. ' season ' .. self.season)
where_clause = 'Files.Characters HOLDS "' .. self.character .. '" AND BBRadio_Cuts.Season="' .. self.season .. '"'
local args = {
where = where_clause,
join = join_on
}
result = cargo.query( tables, fields, args )
count = result[1][fields] or '0'
return tonumber(count)
end
function GalleryTemplate._countImages_GallerySection()
cat1 = self.character or ''
cat2 = self.cat or ''
cat3 = self.cat2 or ''
local query = {}
if cat1 ~= '' then table.insert(query, '[[Category:' .. cat1 .. ']]') end
if cat2 ~= '' then table.insert(query, '[[Category:' .. cat2 .. ']]') end
if cat3 ~= '' then table.insert(query, '[[Category:' .. cat3 .. ']]') end
local result = mw.smw.getQueryResult( query )
count = result.meta.count or 0
return count
end
function GalleryTemplate._expandTemplate_GallerySectionCargo(frame)
_limit = tonumber(self.limit) or 4
if frame[expandTemplate] == nil then
mw.log("NIL FRAME: _expandTemplate_GallerySectionCargo " .. self.title .. " could not be executed")
return ''
end
gallery = frame:expandTemplate{ title = 'Gallery Section (Cargo)', args = {
character = self.character,
source = self.source,
section = self.section,
limit = _limit,
title = self.title,
header = self.header,
see_all = self.see_all,
see_cameos = self.see_cameos,
count_all = self.count_all,
count_cameos = self.count_cameos
}}
return gallery
end
function GalleryTemplate._expandTemplate_GallerySectionBBRadio(frame)
_limit = tonumber(self.limit) or 4
_widths = tonumber(self.widths) or 250
if frame[expandTemplate] == nil then
mw.log("NIL FRAME: _expandTemplate_GallerySectionBBRadio " .. self.title .. " could not be executed")
return ''
end
gallery = frame:expandTemplate { title = 'Gallery Section (BBRadio)', args = {
self.character,
self.season,
self.episode,
self.header,
self.title,
widths = _widths,
limit = _limit,
count_all = self.count_all
}}
return gallery
end
function GalleryTemplate._expandTemplate_GallerySection(frame)
_limit = tonumber(self.limit) or 4
_widths = tonumber(self.widths) or 250
_count_all = self.count_all or 0
if frame[expandTemplate] == nil then
mw.log("NIL FRAME: _expandTemplate_GallerySection " .. _title .. " could not be executed")
return ''
end
gallery = frame:expandTemplate { title = 'Gallery Section', args = {
self.character,
self.cat,
self.cat2,
self.header,
self.title,
widths = _widths,
limit = _limit,
count_all = _count_all,
}}
return gallery
end
function GalleryTemplate:getNumberOfImages()
return self.images_in_section
end
-------------------------------------
-- Gallery Class
-------------------------------------
local Gallery = {}
Gallery.__index = Gallery
function Gallery.new(data, frame)
local self = setmetatable({}, Gallery)
self.galleryData = mw.loadData(data)
self.ordering = self.galleryData.ordering
self.sections = self.galleryData.sections
self.frame = frame
return self
end
function Gallery:__tostring()
local frame = self.frame
text = {}
_char = frame.args['character'] or ''
for i,v in ipairs (self.ordering) do
category = v
local params = self.sections[v] or ''
if params == '' then
else
local t_section = {}
local t_galleries = {}
local images_in_section = 0
local current_subheader = ''
-- add a wrapper div if there's a class to apply to the section
class = params.class or ''
if class ~= '' then
table.insert(t_section, '<div class="' .. class .. '">')
end
-- v is a table of templates in this section
for k2,v2 in pairs(params) do
template = v2.template or ''
local title = params.title or 'TITLE PLACEHOLDER'
mw.log(title .. ": " .. template)
if template == 'Header' then
-- if this is not the first subheader in this section
if current_subheader ~= '' then
-- check if the previous subheader's galleries had images in them
if images_in_section > 0 then
-- insert the previous subheader into the section
table.insert(t_section, current_subheader)
-- insert the previous subheader's galleries into the section
g = table.concat(t_galleries)
if g ~= "" then
table.insert(t_section, g)
end
-- reset galleries and images for the next subsection
clear_table(t_galleries)
images_in_section = 0
end
end
-- TODO fix placeholder text
local header = params.header or 'HEADER PLACEHOLDER'
-- save the new subheader and wait to see if the following galleries have any images in them
current_subheader = '<' .. header .. '>' .. title .. '</' .. header .. ">\n"
elseif template ~= '' then
images_in_section = images_in_section
temp = GalleryTemplate.new(frame, template, params)
table.insert(t_galleries, tostring(temp))
images_in_section = tonumber(temp:getNumberOfImages()) or 0
end
end
-- check the number of images in the section
mw.log(category .. " images_in_section = " .. images_in_section)
-- if there is a subheader to insert, then insert it
if current_subheader ~= "" and images_in_section > 0 then
table.insert(t_section, current_subheader)
end
-- if there are galleries to insert, then insert them
g = table.concat(t_galleries)
if g ~= "" then
table.insert(t_section, g)
else
mw.log("t_galleries is empty, with " .. images_in_section .. " images total in this section")
end
-- close the wrapping div if one was added
if class ~= "" then
table.insert(t_section, "</div>")
end
s = table.concat(t_section)
if s ~= "" then
-- insert the section, prepended by the section header, to the final text
table.insert(text, '<h2>' .. category .. '</h2>\n' .. s)
end
end
end
gallery = table.concat(text)
return gallery
end
-------------------------------------
-- Main
-------------------------------------
local p = {}
function p.main(frame)
data = frame.args.data or 'Module:Sandbox/Chao/data'
gallery = Gallery.new(data, frame)
--mw.log(dump(gallery))
--mw.log("rendered: \n" .. gallery:makeGallery())
return tostring(gallery)
end
-------------------------------------
-- Tests
-------------------------------------
function p.testExpandTemplate_1(frame)
v = {
character = 'Noel Vermillion',
source = 'BlazBlue: Central Fiction',
section = '',
limit = 8,
title = 'title',
header = 'h3',
see_all = 'y',
see_cameos = 'y',
count_all = '7'
}
text = p._expandTemplate_GallerySectionCargo(frame, v)
return text
end
function p.testExpandTemplate_2(frame)
v = {
character = 'Noel Vermillion',
season = 'NEO',
limit = 8,
title = 'title',
header = 'h3',
see_all = 'y',
count_all = '7',
}
text = p._expandTemplate_GallerySectionBBRadio(frame, v)
return text
end
function p.testOrdering()
for i,v in ipairs (ordering) do
section = v
k = sections[v] or ''
mw.log('\n' .. i .. ': ' .. section)
if k == '' then
mw.log('no parameters found')
else
mw.log(k.class)
for k2,v2 in pairs (k) do
mw.log(k2)
end
end
end
end
-------------------------------------
-- Utility Functions
-------------------------------------
-- clear a table
function clear_table(t)
for k in pairs (t) do
t [k] = nil
end
end
-- concat all the strings in table s together with the given delimiter
function concatvalues(s,delimiter)
local t = { }
for k,v in ipairs(s) do
t[#t+1] = tostring(v)
end
return table.concat(t,delimiter)
end
-- concat all the strings in table s together with the given delimiter
-- skips blank entries (where value v = "")
function concatvaluesonly(s,delimiter)
local t = { }
for k,v in ipairs(s) do
if not (v == '') then
t[#t+1] = tostring(v)
end
end
return table.concat(t,delimiter)
end
function cargo_escape(s)
return (string.gsub(s, "[']", {
["'"] = "",
['"'] = "",
}))
end
function quote_escape(s)
return (string.gsub(s, "[\"']", {
["\'"] = "\\'",
['\"'] = '\\"'
}))
end
function html_escape(s)
return (string.gsub(s, "[<>\"'/ ,]", {
["<"] = "%3C",
[">"] = "%3E",
['"'] = "%22",
["'"] = "%27",
["/"] = "%2F",
[" "] = "+",
[","] = "%2C"
}))
end
-- helpful for printing tables
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
return p