Module:Documentation

--Load loader function local loadLib = require('Module:LoadLib')

--Load Modules loadLib(_G, {	string='String',	table='Table',	{ "LibraryUtil" },	{ 'Arguments', values={ 'getArgs' }} }, {	doAutoLoads=false })

--Begin Exports Doc = {} local nowiki = mw.text.nowiki local lb, rb = nowiki('['), nowiki(']') local sep = ' &bull; ' local titleObj = mw.title.getCurrentTitle local pagename = titleObj.fullText

local notices = { protected='', } local noticePatterns = { protected='', }

function Doc:linksToList(t, ignoreBrackets) local ret = {} local t2 = {}

for i, v in ipairs(t) do		table.push(ret, self:parseLinkEntry(v, ignoreBrackets or t.main)) end ret = table.concat(ret, sep) if t.main then ret = table.concat{ t.prepend and self:parseLinkEntry(t.prepend, true)..' ' or '', self:parseLinkEntry(t.main, true), ' ', 			#t ~= 0 and '(' or , 			ret, 			#t ~= 0 and ')' or , ' ',		}	end return ret end

function Doc:exists(title) return mw.title.new(title).exists end

function Doc:existsWithoutWanted(title) return mw.title.existsWithoutWanted(title) end

function Doc:newTitleWithoutWanted(title) local exists = self:existsWithoutWanted(title) return exists and mw.title.new(title) -- if it doesn't exist we don't want to create a title object, as that would mark it as wanted or { exists=false, fullText=title, talkPageTitle={ exists=false } } end

function Doc:msg(name, ...) checkType(1, name, 'string') assertTrue(self.config[name], 'Message %q does not exist', 2, name) return (tostring(self.config[name]):gsub('%$%d', '%%%s')):format(...) end

function Doc:parseLinkEntry(t2, isMain) t2[1] = string.parseDualArg(t2[1]) local val local title = string.makeTitle(t2[2] or '', t2[3], { disableAbbr=true }) if t2.query then val = string.fullUrl(t2[1], t2.query or {}, title) else val = string.makeLink(t2[1], title) end return isMain and (t2.alt or val) or table.concat{ lb, val, rb } end

function Doc:renderNotices self.noticeStr = {} if self.subpageExists and not self._noNotices then for id, pattern in pairs(noticePatterns) do 			local notice = notices[id] if not self.subpageContent:match(pattern) and self.noticeConditions[id] then table.push(self.noticeStr, notice) end end end self.noticeStr = table.concat(self.noticeStr, '\n')..'\n' return self.noticeStr end

function Doc:renderVariables self.ns = mw.site.namespaces[mw.title.getCurrentTitle.namespace].canonicalName; self.title = self._title or self:msg('default-title') self.type = string.ucfirst(self._type or self:msg('type-module')) -- Since ns is set to the current page's type, if ns is set to module but the type isn't, set ns to type instead if self.ns == self:msg('type-module') and self.type ~= self:msg('type-module') then self.ns = self.type end self.title = table.concat{ self.ns, ':', ((self.title:gsub(table.concat{ '^', self.ns, ':?' }, )):gsub('%/doc$', )) } self.sandbox = table.concat{ self.title, '/', self:msg('subpage-sandbox') } self.testcases = table.concat{ self.title, '/', self:msg('subpage-testcases') } self.doc = table.concat{ self.title, '/', self:msg('subpage-documentation') } self.titleObj = assertTrue(mw.title.new(self.title), 'Bad module title %q', 4, self.title) self.titleExists = self.titleObj.exists -- We don't want these to show up as wanted - as such we have to reconstruct the talk page link manually self.talk = table.concat{ mw.site.namespaces[self.titleObj.namespace].talk.name, ':', self.titleObj.text } self.talkObj = self:newTitleWithoutWanted(self.talk) self.sandboxObj = self:newTitleWithoutWanted(self.sandbox) self.testcasesObj = self:newTitleWithoutWanted(self.testcases) self.docObj = mw.title.new(self.doc) self.subpageExists = self.docObj.exists self.subpageContent = self.subpageExists and self.docObj:getContent or nil return self end

function Doc:generateData self.createDocQuery = { action='edit', redlink=1, minor=1, useeditor="source", preload='Template:__DIR__/__PRELOAD__', editintro='Template:__DIR__/__EDITINTRO__', summary=self:msg('doc-edit-summary', self.doc); }	self.createSandbox = { self.sandbox, string.makeTitle(self:msg('sandbox-create-text'), self:msg('sandbox-create-title'), { disableAbbr=true }), query = { action="edit", redlink=1, minor=1, useeditor="source", preload=self.title, summary=self:msg('sandbox-create-summary', self.doc); }	}	self.links = {} self.links.pageTools = { {			{ self.doc, self:msg('main-view-text'), self:msg('main-view-title'), }, { self.doc, self:msg('main-edit-text'), self:msg('main-edit-title'), query={ action='edit' }}, { self.doc, self:msg('main-curdiff-text'), self:msg('main-curdiff-title'), query={ diff='cur' }}, { self.doc, self:msg('main-hist-text'), self:msg('main-hist-title'), query={ action='history' }}, { self.title, self:msg('main-purge-text'), self:msg('main-purge-title'), query={ action="purge" }}, },		{			{ self.doc, self:msg('main-create-text'), self:msg('main-create-title'), query=self.createDocQuery }, { self.title, self:msg('main-purge-text'), self:msg('main-purge-title'), query={ action="purge" }}, }	}	self.links.sandbox = { main={ self.sandbox, self:msg('sandbox-main-text') }, { self.sandbox, self:msg('sandbox-edit-text'), self:msg('sandbox-edit-title'), query={ action="edit" }}, { self.sandbox, self:msg('sandbox-curdiff-text'), self:msg('sandbox-curdiff-title'), query={ diff="cur" }}, { self.sandbox, self:msg('sandbox-hist-text'), self:msg('sandbox-hist-title'), query={ action="history" }}, { 			alt=string.wrapHtml{ self:msg('sandbox-reset-text'), " ", attrs={ title=self:msg('sandbox-reset-title'), id="re-mirror-sandbox", style={ cursor="pointer"; } }			}, 		},	}	self.links.tools = { { { 'Special:PrefixIndex/', self.title, '/' }, self:msg('tool-subpages-text'), self:msg('tool-subpages-title') }, { { 'Special:WhatLinksHere/', self.title, }, self:msg('tool-whl-text'), self:msg('tool-whl-title') }, }	self.links.talk = { {			main={ self.talk, self:msg('talk-main-text') }, prepend={ self.talk, '+', self:msg('talk-newsec-title'), query={ action="edit", section="new" }}, { self.talk, self:msg('talk-edit-text'), self:msg('talk-edit-title'), query={ action="edit" }}, { self.talk, self:msg('talk-curdiff-text'), self:msg('talk-curdiff-title'), query={ diff="cur" }}, { self.talk, self:msg('talk-hist-text'), self:msg('talk-hist-title'), query={ action="history" }}, },		{			main={ self.talk, self:msg('talk-create-text'), self:msg('talk-create-title'), query={ action='edit', redlink=1, minor=1, useeditor="source", section="new", summary=self:msg('talk-create-summary', self.title), }			},		},	}	self.links.pageLinks = { { alt=self:linksToList(self.links.talk[self.talkObj.exists and 1 or 2]) }, { self.title, self:msg('page-curdiff-text'), self:msg('page-curdiff-title'), query={ diff="cur" }}, { { 'Special:Log/', self.title }, 'Page Logs', 'View the logs for this page', }, }	self.links.testcases = { {			 main={ self.testcases, self:msg('test-main-text'), self:msg('test-main-title') }, { self.testcases, self:msg('test-edit-text'), self:msg('test-edit-title') }, { self.testcases, self:msg('test-curdiff-text'), self:msg('test-curdiff-title') }, self.testcasesObj.talkPageTitle.exists and { self.testcasesObj.talkPageTitle.fullText, self:msg('test-run-text'), self:msg('test-run-title') } or { self.testcasesObj.talkPageTitle.fullText, self:msg('test-results-create-text'), self:msg('test-results-create-title'), query={ action='edit', redlink=1, minor=1, useeditor="source", preload='Template:__DIR__/__PRELOAD__/' .. self:msg('testcases-results-name'), editintro='Template:__DIR__/__EDITINTRO__/' .. self:msg('testcases-results-name'), summary=self:msg('test-results-create-summary', self.title), },				},		},		{ 			self.testcases, self:msg('test-create-text'), self:msg('test-create-title'), query={ action='edit', redlink=1, minor=1, useeditor="source", preload='Template:__DIR__/__PRELOAD__/' .. self:msg('testcases-name'), editintro='Template:__DIR__/__EDITINTRO__/' .. self:msg('testcases-name'), summary=self:msg('test-create-summary', self.title), }		}	}	self.noticeConditions = { protected = self.titleObj.protectionLevels.edit and self.titleObj.protectionLevels.edit[1] == 'sysop', } end

function Doc:buildMainBox local inst = self.html :css{ ['font-family']='Arial !important'; ['clear']='both'; }		:tag('div') :addClass('color1') :css{ ['padding'] = '2px'; ['border-top'] = '1.3px solid black'; ['border-left'] = '1.3px solid black'; ['border-right'] = '1.3px solid black'; ['border-bottom'] = 'none'; ['margin'] = '4px 4px 0 4px'; ['box-shadow'] = '0 0 2px black'; }

if self.type == self:msg('type-module') then inst:tag('span') :css{ ['float'] = 'right'; ['font'] = 'italic small arial'; }			:wikitext('(', string.makeLink('#code', string.makeTitle(self:msg('jump-text'), self:msg('jump-title'), { disableAbbr=true })), ')')		:done end inst:tag('span') :css{ float="left"; }				:wikitext(string.makeImage(self:msg('doc-image'), { link='', size=60 })) :done :tag('span') :attr{ id="header" } :css{ ['font'] = 'bold x-large arial'; ['margin-left'] = '7px'; ['text-align'] = 'center'; }				:wikitext(self:msg('header-text')) :done :tag('span') :css{ ['margin-left']='7px'; }				:wikitext(self:linksToList(self.subpageExists and self.links.pageTools[1] or self.links.pageTools[2])) :done if self.type == self:msg('type-template') then inst:tag('small') :css{ display='block'; }			:tag('i') :tag('b'):wikitext(self:msg('note'), ': '):done :wikitext(self:msg('template-note')) :done :done end

inst = inst:tag('hr') :addClass('page-header__separator') :css{ ['background-color'] = '#daa9ad'; ['border'] = 0; ['height'] = '1px'; ['margin-bottom'] = '3px'; ['margin-top'] = 0; ['width'] = '100%'; }			:done :tag('div') :tag('p') :css{ margin=0; }					:wikitext(						string.wrapTag({ self:msg('page-tools'), ': '}, 'b'), 						(table.concat(table.merge({ self.sandboxObj.exists and self:linksToList(self.links.sandbox) or self:parseLinkEntry(self.createSandbox, true), }, self:linksToList(self.links.tools, true)), sep):gsub(' &bull; 1', ''))					) :done :done :tag('hr') :addClass('page-header__separator') :css{ ['background-color'] = '#daa9ad'; ['border'] = 0; ['height'] = '1px'; ['margin-bottom'] = '3px'; ['margin-top'] = 0; ['width'] = '100%'; }			:done :tag('div') :tag('p') :css{ margin=0; }					:wikitext(						string.wrapTag({ self:msg('page-links'), ': '}, 'b'), 						self:linksToList(self.links.pageLinks, true)					) :done :done :done :tag('div') :css{ ['background-color'] = 'rgba(120,120,120,0.05)'; ['box-shadow'] = '0 0 2px black'; ['border-bottom'] = 'none'; ['border-top'] = 'none'; ['padding'] = '0.5em'; ['margin'] = '0 4px 0 4px'; ['border-right'] = '1.3px solid black'; ['border-left'] = '1.3px solid black'; ['clear'] = 'both'; }			:wikitext(				self.subpageExists and					mw.getCurrentFrame:newChild{ title=self.doc }:preprocess(self.noticeStr..self.subpageContent)				or					table.concat{ 						string.wrapHtml( {								self:msg('no-exist-message'), ' ', },							' ', {								class="error" }						),						string.wrapTag({ '(', string.fullUrl(self.doc, self.createDocQuery, self:msg('main-create-text')), ')', }, 'strong'),					}			) :wikitext(not self.subpageExists and table.concat{ , self:msg('no-exist-cat'),  } or '') :tag('span') :css{ display="none" } :wikitext(, self.doc, ) :done -- To Make sure the contents of the box does not overflow :tag('div') :css{ clear="both"; ['margin-bottom']='5px'; }			:done :done :tag('div') :addClass('color1') :css{ ['padding'] = '2px'; ['border-left'] = '1.3px solid black'; ['border-right'] = '1.3px solid black'; ['border-bottom'] = '1.3px solid black'; ['border-top'] = 'none'; ['margin'] = '0 4px 4px 4px'; ['box-shadow'] = '0 0 2px black'; }			:tag('span') :css{ ['font-size']='20px'; ['margin']='5px'; }				:wikitext('(', string.makeLink({ self:msg('hdtw-name') }, self:msg('hdtw-text')), ')') :done :tag('span') :css{ ['margin-left']=self.titleObj.exists and '13%' or '23%'; }				:wikitext(self.type == self:msg('type-module') and string.wrapTag({ self:msg('other-tools'), ': ' }, 'b') or '') :wikitext(					self.type == self:msg('type-module') and ( self.testcasesObj.exists and self:linksToList(self.links.testcases[1]) or self:parseLinkEntry(self.links.testcases[2], true) ) or ''				) :done :tag('span') :css{ float='right'; ['font-style']='italic'; }				:wikitext('(', string.makeLink('#header', self:msg('back-to-top')), ')') :done :done :allDone return inst end

-- Some subpages shouldn't show documentation, since it's never needed and causes needless red links function Doc:checkSkipDocumentation local skip = false -- Only module documentation is automatically shown, so only do the skip check for modules if self._type == self:msg('type-module') then -- sandboxes / testcases are standardized subpages each module can have, but never need documentation if    self._title:find("/sandbox$") or self._title:find("/testcases$") -- These pages only contain data tables - any info about them should just be included on the parent module or self._title:find("/Aliases$") or self._title:find("/Data$") or self._title:find("^Sandbox/") -- don't add doc pages for sandbox module subpages then skip = true end end if skip then -- if it has a documentation page show it anyways (to allow bypassing the skip) local docTitle = table.concat{ self._type, ':', self._title, '/', self:msg('subpage-documentation') } skip = not mw.title.existsWithoutWanted(docTitle) end return skip end

function Doc:constructor(title, _type, noNotices) checkType(true, 1, title, 'string', true) checkType(true, 2, _type, 'string', true) self.config = require('Module:Documentation/Config') self._title, self._type = title, string.ucfirst(_type or self:msg('type-module') ) self._noNotices = noNotices if self:checkSkipDocumentation then self.ret = '' return self end self:renderVariables self.html = mw.html.create('div') self:generateData self:renderNotices self.ret, _ = tostring(self:buildMainBox) :gsub('__TYPE__', self.type) :gsub('__DIR__', self.type == self:msg('type-module') and self:msg('dir-module') or self:msg('dir-template')) :gsub('__PRELOAD__', self:msg('dir-preload')) :gsub('__EDITINTRO__', self:msg('dir-editintro')) return self end

function Doc:__tostring return self.ret end

function Doc:__concat(a, b)	if a == self then return self.ret..b	else return b..self.ret end end

--Create Class Doc = table.makeClass(nil, Doc, nil, { ignore=true })

function Doc.main(frame) local args = getArgs(frame) return Doc:constructor(table.unpack(table.deepCopy(args, false))) end

function Doc.module(title, noNotices) checkType(1, title, 'string', true) checkType(2, noNotices, 'boolean', true) return Doc(title or titleObj.text, 'Module', noNotices) end

function Doc.template(title, noNotices) checkType(1, title, 'string', true) checkType(2, noNotices, 'boolean', true) return Doc(title or titleObj.text, 'Template', noNotices) end

function Doc._module(frame) local args = getArgs(frame) return Doc:constructor(args[1] or titleObj.text, 'Module', args['nonotices'] or args['nonotice'] or args['nonoti'] or args['nn']) end

function Doc._template(frame) local args = getArgs(frame) return Doc:constructor(args[1] or titleObj.text, 'Template', args['nonotices'] or args['nonotice'] or args['nonoti'] or args['nn']) end

--Finish __TYPE__/Exports return Doc