Module:Sandbox/Aseleste/Indexer

-- Categorizes content and display them. -- Improvements are welcomed.

-- requires

-- forward declarations local index; local process_key_value; local build_keys; local build_values; local as_key_content_function; local as_value_content_function; local as_fold_function; local escape_replacement; local unstrip_and_strip_nowiki_tags;

-- exposed: categorizes content and display them -- 'folder', 'table_*', 'content' index = function(frame) local args = build_args(frame) local folder = as_fold_function(args.folder) local keys, key_order = build_keys(args) local values = build_values(args.content) local result = "" -- ordered first for _, categories in ipairs(key_order) do		for _, category in ipairs(categories) do			local processed = process_key_value(keys, values, category) if processed then result = folder(result, processed) -- remove key and value to mark as processed keys[category] = nil values[category] = nil end end end -- unordered last for category, _ in pairs(keys) do		local processed = process_key_value(keys, values, category) if processed then result = folder(result, processed) end end return frame:getParent:preprocess(result) -- intended to be used with a template end

-- process key and value for a given category process_key_value = function(keys, values, category) local key = keys[category] if key then local value = values[category] if not value then value = function(category) return "" end end local result = key(value(category)) return result end return nil end

-- builds arguments -- reads parent frame build_args = function(frame) local result = {} for key, value in pairs(frame.args) do		result[mw.text.trim(key)] = unstrip_and_strip_nowiki_tags(value) end local pframe = frame:getParent for key, value in pairs(pframe.args) do		result[mw.text.trim(key)] = unstrip_and_strip_nowiki_tags(value) end return result end

-- builds key mapping, key order mapping -- reads 'table_*' build_keys = function(args) local keys = {} local key_order = {} for arg_k, arg_v in pairs(args) do		if arg_k:match("^table_") then local key = arg_k:gsub("table_", "", 1) local order = key:match("^(%d+)_") if order then key = key:gsub(order .. "_", "", 1) local order_list = key_order[order] if order_list then table.insert(order_list, key) else key_order[order] = {key} end end keys[key] = as_key_content_function(arg_v) end end -- sort key order local key_order_list = {} for order, category in pairs(key_order) do		table.insert(key_order_list, {key=tonumber(order), value=category}) end table.sort(key_order_list, function(a, b) return a.key < b.key end) key_order = {} for _, value in ipairs(key_order_list) do		table.insert(key_order, value.value) end return keys, key_order end

-- builds values mapping -- magic words: '__M_INDEX__' -- syntax: '__M_INDEX__ [category1] [category2] ... [categoryN]' build_values = function(text) -- need cleanup local result = {} local context = {} local content = "" for _, line in ipairs(mw.text.split(text, "\n", true)) do -- is \n sufficient if line:match("^__M_INDEX__") then -- finish result for _, category in ipairs(context) do				local sub_result = result[category] if sub_result == nil then result[category] = content else result[category] = sub_result .. content end end content = "" -- change context context = {} for category in line:gmatch("%[([^%]]*)%]") do				table.insert(context, category) end else -- append content content = content .. line .. "\n" end end -- one more round of finishing result for _, category in ipairs(context) do		local sub_result = result[category] if sub_result == nil then result[category] = content else result[category] = sub_result .. content end end -- turn into content function for key, value in pairs(result) do		result[key] = as_value_content_function(value) end return result end

-- converts text to key content function -- magic words: '__M_CONTENT__' as_key_content_function = function(text) local function func(content) local result = text:gsub("__M_CONTENT__", escape_replacement(content)) -- need this variable to only return 1 result return result end return func end

-- converts text to value content function -- magic words: '__M_CATEGORY__' as_value_content_function = function(text) local function func(category) local result = text:gsub("__M_CATEGORY__", escape_replacement(category)) -- need this variable to only return 1 result return result end return func end

-- converts text to folding function -- magic words: '__M_LEFT__', '__M_RIGHT__' as_fold_function = function(text) local function func(left, right) -- some improvements could be made here local result = text:gsub("__M_LEFT__", escape_replacement(left)):gsub("__M_RIGHT__", escape_replacement(right)) -- need this variable to only return 1 result return result end return func end

-- escapes replacement, replace '%' with '%%' -- ah, multiple return results cost us headache, apparently multiple return results expand into multiple arguments escape_replacement = function(text) local result = text:gsub("%%", "%%%%") -- need this variable to only return 1 result return result end

unstrip_and_strip_nowiki_tags = function(text) local result = mw.text.trim(mw.text.unstripNoWiki(text)) -- need this variable to only return 1 result :gsub(" ", "") :gsub(" ", "") :gsub("&lt;", "<") -- needs to be escaped in the source :gsub("&gt;", ">") :gsub("&quot;", '"')	return result end

return {index = index}