﻿<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://scholarlywiki.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Maintenance+script</id>
	<title>HandWiki Stage - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://scholarlywiki.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Maintenance+script"/>
	<link rel="alternate" type="text/html" href="https://scholarlywiki.org/wiki/Special:Contributions/Maintenance_script"/>
	<updated>2026-05-14T00:10:50Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://scholarlywiki.org/index.php?title=Module:PhysicsQC&amp;diff=2161</id>
		<title>Module:PhysicsQC</title>
		<link rel="alternate" type="text/html" href="https://scholarlywiki.org/index.php?title=Module:PhysicsQC&amp;diff=2161"/>
		<updated>2026-05-13T18:44:52Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Fix gallery thumbnail sizing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Module:PhysicsQC&lt;br /&gt;
-- Quantum Collection table-of-contents and gallery renderer.&lt;br /&gt;
--&lt;br /&gt;
-- Restored rules:&lt;br /&gt;
--   * Index main groups: 2 columns.&lt;br /&gt;
--   * Book II / Book III / Book IV: one horizontal row below Index.&lt;br /&gt;
--   * Full contents: ONE column.&lt;br /&gt;
--   * Full contents preserves raw body, including images.&lt;br /&gt;
--   * Counts exclude File/Image/Book/etc.&lt;br /&gt;
--   * Gallery uses compact cards.&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
local function trim(s)&lt;br /&gt;
	if not s then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
	return mw.text.trim(s)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function escapeHtml(s)&lt;br /&gt;
	return mw.text.encode(s or &#039;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getPageContent(pageName)&lt;br /&gt;
	local title = mw.title.new(pageName or &#039;&#039;)&lt;br /&gt;
	if not title then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local text = title:getContent() or &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
	-- Prevent visible &amp;lt;includeonly&amp;gt; / &amp;lt;/includeonly&amp;gt; leaks from data/template pages.&lt;br /&gt;
	text = text:gsub(&#039;&amp;lt;%s*/?%s*includeonly%s*&amp;gt;&#039;, &#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
	return text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getCurrentPageName()&lt;br /&gt;
	local t = mw.title.getCurrentTitle()&lt;br /&gt;
	if not t then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
	return t.prefixedText or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isCurrentBookPage()&lt;br /&gt;
	return getCurrentPageName():match(&#039;^Book:Quantum Collection&#039;) ~= nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stripNamespaceForLabel(page)&lt;br /&gt;
	page = page or &#039;&#039;&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum data analysis/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum methods/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum materials/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum matter/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum atoms/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum basics/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:Quantum &#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Physics:&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;^Book:Quantum Collection/&#039;, &#039;&#039;)&lt;br /&gt;
	page = page:gsub(&#039;_&#039;, &#039; &#039;)&lt;br /&gt;
	return page&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeAnchorId(s)&lt;br /&gt;
	s = s or &#039;&#039;&lt;br /&gt;
	s = mw.text.decode(s, true)&lt;br /&gt;
	s = s:gsub(&#039;&amp;lt;[^&amp;gt;]+&amp;gt;&#039;, &#039;&#039;)&lt;br /&gt;
	s = s:gsub(&#039;&amp;amp;nbsp;&#039;, &#039; &#039;)&lt;br /&gt;
	s = trim(s)&lt;br /&gt;
	s = s:gsub(&#039;%s+&#039;, &#039;_&#039;)&lt;br /&gt;
	return mw.uri.anchorEncode(s)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isSkippedLink(link)&lt;br /&gt;
	if not link or link == &#039;&#039; then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	link = trim(link)&lt;br /&gt;
&lt;br /&gt;
	if link:match(&#039;^#&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^File:&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^Image:&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^Category:&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^Help:&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^Special:&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^Template:&#039;) then return true end&lt;br /&gt;
	if link:match(&#039;^Module:&#039;) then return true end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isSkippedArticleCountLink(link)&lt;br /&gt;
	if isSkippedLink(link) then return true end&lt;br /&gt;
	if link:match(&#039;^Book:&#039;) then return true end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function parseWikiLink(raw)&lt;br /&gt;
	raw = raw or &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
	local target, label = raw:match(&#039;^([^|]+)|(.+)$&#039;)&lt;br /&gt;
&lt;br /&gt;
	if not target then&lt;br /&gt;
		target = raw&lt;br /&gt;
		label = stripNamespaceForLabel(raw)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	target = trim(target)&lt;br /&gt;
	label = trim(label)&lt;br /&gt;
&lt;br /&gt;
	return target, label&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function countArticleLinks(text)&lt;br /&gt;
	local count = 0&lt;br /&gt;
	local seen = {}&lt;br /&gt;
&lt;br /&gt;
	for raw in (text or &#039;&#039;):gmatch(&#039;%[%[([^%]]+)%]%]&#039;) do&lt;br /&gt;
		local target = raw:match(&#039;^([^|#]+)&#039;) or raw&lt;br /&gt;
		target = trim(target)&lt;br /&gt;
&lt;br /&gt;
		if not isSkippedArticleCountLink(target) and not seen[target] then&lt;br /&gt;
			seen[target] = true&lt;br /&gt;
			count = count + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Parse == Index ==&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
local function parseIndex(content)&lt;br /&gt;
	local indexText = content:match(&#039;==%s*Index%s*==%s*(.-)%s*==%s*Full contents%s*==&#039;)&lt;br /&gt;
&lt;br /&gt;
	if not indexText then&lt;br /&gt;
		indexText = content:match(&#039;==%s*Index%s*==%s*(.*)&#039;) or &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local groups = {}&lt;br /&gt;
	local currentGroup = nil&lt;br /&gt;
&lt;br /&gt;
	for line in indexText:gmatch(&#039;[^\r\n]+&#039;) do&lt;br /&gt;
		line = trim(line)&lt;br /&gt;
&lt;br /&gt;
		if line ~= &#039;&#039; then&lt;br /&gt;
			local boldContent = line:match(&amp;quot;^&#039;&#039;&#039;%s*(.-)%s*&#039;&#039;&#039;%s*$&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
			if boldContent then&lt;br /&gt;
				currentGroup = {&lt;br /&gt;
					name = trim(boldContent),&lt;br /&gt;
					links = {}&lt;br /&gt;
				}&lt;br /&gt;
				table.insert(groups, currentGroup)&lt;br /&gt;
			else&lt;br /&gt;
				for raw in line:gmatch(&#039;%[%[([^%]]+)%]%]&#039;) do&lt;br /&gt;
					local target, label = parseWikiLink(raw)&lt;br /&gt;
					target = target:match(&#039;^([^#]+)&#039;) or target&lt;br /&gt;
					target = trim(target)&lt;br /&gt;
&lt;br /&gt;
					if currentGroup and not isSkippedLink(target) then&lt;br /&gt;
						table.insert(currentGroup.links, {&lt;br /&gt;
							target = target,&lt;br /&gt;
							label = label&lt;br /&gt;
						})&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return groups&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isBookGroup(group)&lt;br /&gt;
	if not group or not group.name then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local name = group.name&lt;br /&gt;
&lt;br /&gt;
	if name == &#039;Book II&#039; then return true end&lt;br /&gt;
	if name == &#039;Book III&#039; then return true end&lt;br /&gt;
	if name == &#039;Book IV&#039; then return true end&lt;br /&gt;
&lt;br /&gt;
	if name == &#039;Quantum Book II&#039; then return true end&lt;br /&gt;
	if name == &#039;Quantum Book III&#039; then return true end&lt;br /&gt;
	if name == &#039;Quantum Book IV&#039; then return true end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function displayBookGroupName(name)&lt;br /&gt;
	name = name or &#039;&#039;&lt;br /&gt;
	name = name:gsub(&#039;^Quantum%s+&#039;, &#039;&#039;)&lt;br /&gt;
	return name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderOrdinaryIndexGroup(group, startNumber)&lt;br /&gt;
	local out = {}&lt;br /&gt;
	local n = startNumber or 1&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div style=&amp;quot;break-inside:avoid; margin-bottom:0.8em;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div style=&amp;quot;font-weight:bold; margin-bottom:0.25em;&amp;quot;&amp;gt;&#039; .. escapeHtml(group.name) .. &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	if #group.links &amp;gt; 0 then&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;div style=&amp;quot;margin-top:0; margin-left:1.2em;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
		for _, link in ipairs(group.links) do&lt;br /&gt;
			local anchor = makeAnchorId(link.label)&lt;br /&gt;
&lt;br /&gt;
			if link.target:match(&#039;^Book:&#039;) then&lt;br /&gt;
				table.insert(out, &#039;&amp;lt;div&amp;gt;&#039; .. n .. &#039;. [[&#039; .. link.target .. &#039;|&#039; .. escapeHtml(link.label) .. &#039;]]&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
				n = n + 1&lt;br /&gt;
			else&lt;br /&gt;
				table.insert(out, &#039;&amp;lt;div&amp;gt;&#039; .. n .. &#039;. [[#&#039; .. anchor .. &#039;|&#039; .. escapeHtml(link.label) .. &#039;]]&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
				n = n + 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;), n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBookRow(bookGroups)&lt;br /&gt;
	local out = {}&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div class=&amp;quot;noexcerpt&amp;quot; style=&amp;quot;margin:1em 0 0.8em 0;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div style=&amp;quot;display:flex; gap:2em; align-items:flex-start;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	for _, group in ipairs(bookGroups) do&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;div style=&amp;quot;flex:1; min-width:0;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;div style=&amp;quot;font-weight:bold; margin-bottom:0.25em;&amp;quot;&amp;gt;&#039; .. escapeHtml(displayBookGroupName(group.name)) .. &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
		if #group.links &amp;gt; 0 then&lt;br /&gt;
			table.insert(out, &#039;&amp;lt;ul style=&amp;quot;margin-top:0;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
			for _, link in ipairs(group.links) do&lt;br /&gt;
				if link.target:match(&#039;^Book:&#039;) then&lt;br /&gt;
					table.insert(out, &#039;&amp;lt;li&amp;gt;[[&#039; .. link.target .. &#039;|&#039; .. escapeHtml(link.label) .. &#039;]]&amp;lt;/li&amp;gt;&#039;)&lt;br /&gt;
				else&lt;br /&gt;
					local anchor = makeAnchorId(link.label)&lt;br /&gt;
					table.insert(out, &#039;&amp;lt;li&amp;gt;[[#&#039; .. anchor .. &#039;|&#039; .. escapeHtml(link.label) .. &#039;]]&amp;lt;/li&amp;gt;&#039;)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			table.insert(out, &#039;&amp;lt;/ul&amp;gt;&#039;)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderIndex(groups)&lt;br /&gt;
	if #groups == 0 then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local normalGroups = {}&lt;br /&gt;
	local bookGroups = {}&lt;br /&gt;
&lt;br /&gt;
	for _, group in ipairs(groups) do&lt;br /&gt;
		if isBookGroup(group) then&lt;br /&gt;
			table.insert(bookGroups, group)&lt;br /&gt;
		else&lt;br /&gt;
			table.insert(normalGroups, group)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;= Index =&#039;)&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div class=&amp;quot;noexcerpt&amp;quot; style=&amp;quot;column-count:2; column-gap:2em;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	local indexNumber = 1&lt;br /&gt;
&lt;br /&gt;
	for _, group in ipairs(normalGroups) do&lt;br /&gt;
		local rendered&lt;br /&gt;
		rendered, indexNumber = renderOrdinaryIndexGroup(group, indexNumber)&lt;br /&gt;
		table.insert(out, rendered)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	if #bookGroups &amp;gt; 0 then&lt;br /&gt;
		table.insert(out, renderBookRow(bookGroups))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Parse == Full contents ==&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
local function parseFullContents(content)&lt;br /&gt;
	local full = content:match(&#039;==%s*Full contents%s*==%s*(.*)&#039;) or &#039;&#039;&lt;br /&gt;
	local sections = {}&lt;br /&gt;
	local current = nil&lt;br /&gt;
&lt;br /&gt;
	for line in full:gmatch(&#039;[^\r\n]+&#039;) do&lt;br /&gt;
		local heading = line:match(&#039;^===%s*(.-)%s*===%s*$&#039;)&lt;br /&gt;
&lt;br /&gt;
		if heading then&lt;br /&gt;
			current = {&lt;br /&gt;
				name = trim(heading),&lt;br /&gt;
				lines = {}&lt;br /&gt;
			}&lt;br /&gt;
			table.insert(sections, current)&lt;br /&gt;
		elseif current then&lt;br /&gt;
			table.insert(current.lines, line)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return sections&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function forceOrderedList(body, startNumber)&lt;br /&gt;
	body = body or &#039;&#039;&lt;br /&gt;
	startNumber = startNumber or 1&lt;br /&gt;
&lt;br /&gt;
	-- Extra safety in case includeonly tags survive from copied content.&lt;br /&gt;
	body = body:gsub(&#039;&amp;lt;%s*/?%s*includeonly%s*&amp;gt;&#039;, &#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
	-- If the data page already contains an ordered list, preserve it.&lt;br /&gt;
	if body:match(&#039;&amp;lt;%s*ol[%s&amp;gt;]&#039;) then&lt;br /&gt;
		return body, startNumber&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
	local inList = false&lt;br /&gt;
	local n = startNumber&lt;br /&gt;
&lt;br /&gt;
	for line in body:gmatch(&#039;([^\r\n]*)\r?\n?&#039;) do&lt;br /&gt;
		if line ~= &#039;&#039; then&lt;br /&gt;
			local liContent = line:match(&#039;^%s*&amp;lt;%s*li%s*&amp;gt;(.-)&amp;lt;%s*/%s*li%s*&amp;gt;%s*$&#039;)&lt;br /&gt;
&lt;br /&gt;
			if liContent then&lt;br /&gt;
				if not inList then&lt;br /&gt;
					table.insert(out, &#039;&amp;lt;div style=&amp;quot;margin-top:0; margin-left:1.6em;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
					inList = true&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				table.insert(out, &#039;&amp;lt;div&amp;gt;&#039; .. n .. &#039;. &#039; .. liContent .. &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
				n = n + 1&lt;br /&gt;
			else&lt;br /&gt;
				if inList then&lt;br /&gt;
					table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
					inList = false&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				table.insert(out, line)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if inList then&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;), n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderSectionList(section, collapse, sectionNumber, articleStartNumber)&lt;br /&gt;
	local body, nextArticleNumber = forceOrderedList(table.concat(section.lines, &#039;\n&#039;), articleStartNumber)&lt;br /&gt;
	local count = countArticleLinks(body)&lt;br /&gt;
	local anchor = makeAnchorId(section.name)&lt;br /&gt;
	local collapsedClass = &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
	if collapse then&lt;br /&gt;
		collapsedClass = &#039; mw-collapsed&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local sectionLabel = section.name&lt;br /&gt;
	if sectionNumber then&lt;br /&gt;
		sectionLabel = tostring(sectionNumber) .. &#039;. &#039; .. sectionLabel&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div id=&amp;quot;&#039; .. anchor .. &#039;&amp;quot; class=&amp;quot;mw-collapsible&#039; .. collapsedClass .. &#039;&amp;quot; style=&amp;quot;border:1px solid #e0d890; background:#fffdf0; margin:0.8em 0; padding:0.6em;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div style=&amp;quot;font-weight:bold; margin-bottom:0.4em;&amp;quot;&amp;gt;&#039; .. escapeHtml(sectionLabel) .. &#039; (&#039; .. count .. &#039;) &amp;lt;span style=&amp;quot;font-size:85%; font-weight:normal;&amp;quot;&amp;gt;[[#Index|↑ Back to index]]&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	-- IMPORTANT:&lt;br /&gt;
	-- Full contents is intentionally ONE column and raw.&lt;br /&gt;
	-- This preserves original list formatting and any images.&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div&amp;gt;&#039;)&lt;br /&gt;
	table.insert(out, body)&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;), nextArticleNumber&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderFullContents(sections)&lt;br /&gt;
	if #sections == 0 then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
	local collapse = false&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;= Full contents =&#039;)&lt;br /&gt;
&lt;br /&gt;
	local articleNumber = 1&lt;br /&gt;
&lt;br /&gt;
	for i, section in ipairs(sections) do&lt;br /&gt;
		local rendered&lt;br /&gt;
		rendered, articleNumber = renderSectionList(section, collapse, i, articleNumber)&lt;br /&gt;
		table.insert(out, rendered)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function totalArticleCount(sections)&lt;br /&gt;
	local n = 0&lt;br /&gt;
&lt;br /&gt;
	for _, section in ipairs(sections) do&lt;br /&gt;
		n = n + countArticleLinks(table.concat(section.lines, &#039;\n&#039;))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Public TOC functions&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
function p.tocHeading(frame)&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	local dataPage = args[1] or &#039;Physics:Quantum basics/See also&#039;&lt;br /&gt;
	local content = getPageContent(dataPage)&lt;br /&gt;
	local sections = parseFullContents(content)&lt;br /&gt;
	local n = totalArticleCount(sections)&lt;br /&gt;
&lt;br /&gt;
	return &#039;= Table of contents (&#039; .. n .. &#039; articles) =&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.tocHeadingAndList(frame)&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	local dataPage = args[1] or &#039;Physics:Quantum basics/See also&#039;&lt;br /&gt;
	local content = getPageContent(dataPage)&lt;br /&gt;
&lt;br /&gt;
	local groups = parseIndex(content)&lt;br /&gt;
	local sections = parseFullContents(content)&lt;br /&gt;
	local n = totalArticleCount(sections)&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;= Table of contents (&#039; .. n .. &#039; articles) =&#039;)&lt;br /&gt;
	table.insert(out, renderIndex(groups))&lt;br /&gt;
	table.insert(out, renderFullContents(sections))&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Child-book wrappers&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
function p.qm(frame)&lt;br /&gt;
	frame = frame or {}&lt;br /&gt;
	frame.args = frame.args or {}&lt;br /&gt;
	frame.args[1] = frame.args[1] or &#039;Physics:Quantum basics/See also/Matter&#039;&lt;br /&gt;
	return p.tocHeadingAndList(frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.qt(frame)&lt;br /&gt;
	frame = frame or {}&lt;br /&gt;
	frame.args = frame.args or {}&lt;br /&gt;
	frame.args[1] = frame.args[1] or &#039;Physics:Quantum basics/See also/Methods&#039;&lt;br /&gt;
	return p.tocHeadingAndList(frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.qd(frame)&lt;br /&gt;
	frame = frame or {}&lt;br /&gt;
	frame.args = frame.args or {}&lt;br /&gt;
	frame.args[1] = frame.args[1] or &#039;Physics:Quantum basics/See also/Data Analysis&#039;&lt;br /&gt;
	return p.tocHeadingAndList(frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Gallery helpers&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
local function extractPagesForGallery(content)&lt;br /&gt;
	local pages = {}&lt;br /&gt;
	local seen = {}&lt;br /&gt;
&lt;br /&gt;
	local full = content:match(&#039;==%s*Full contents%s*==%s*(.*)&#039;) or content&lt;br /&gt;
&lt;br /&gt;
	for raw in full:gmatch(&#039;%[%[([^%]]+)%]%]&#039;) do&lt;br /&gt;
		local target = raw:match(&#039;^([^|#]+)&#039;) or raw&lt;br /&gt;
		target = trim(target)&lt;br /&gt;
&lt;br /&gt;
		if not isSkippedLink(target) and not target:match(&#039;^Book:&#039;) and not seen[target] then&lt;br /&gt;
			seen[target] = true&lt;br /&gt;
			table.insert(pages, target)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return pages&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function firstFileInArticle(page)&lt;br /&gt;
	local text = getPageContent(page)&lt;br /&gt;
&lt;br /&gt;
	if text == &#039;&#039; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local file = text:match(&#039;%[%[%s*[Ff]ile%s*:%s*([^%]|%]]+)&#039;)&lt;br /&gt;
&lt;br /&gt;
	if not file then&lt;br /&gt;
		file = text:match(&#039;%[%[%s*[Ii]mage%s*:%s*([^%]|%]]+)&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if file then&lt;br /&gt;
		file = trim(file)&lt;br /&gt;
		file = file:gsub(&#039;|.*$&#039;, &#039;&#039;)&lt;br /&gt;
		file = trim(file)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return file&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function normalizeGalleryWidth(value)&lt;br /&gt;
	local width = tonumber(value) or 150&lt;br /&gt;
&lt;br /&gt;
	if width &amp;lt; 40 then&lt;br /&gt;
		width = 40&lt;br /&gt;
	elseif width &amp;gt; 400 then&lt;br /&gt;
		width = 400&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return math.floor(width)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderGalleryCard(page, file, width)&lt;br /&gt;
	local label = stripNamespaceForLabel(page)&lt;br /&gt;
	local out = {}&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div style=&amp;quot;display:inline-block; vertical-align:top; width:180px; margin:6px; padding:5px; border:1px solid #e0d890; background:#fff8cc; text-align:center;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
	if file then&lt;br /&gt;
		table.insert(out, &#039;[[File:&#039; .. file .. &#039;|&#039; .. width .. &#039;px]]&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		table.insert(out, &#039;&amp;lt;div style=&amp;quot;height:120px; display:flex; align-items:center; justify-content:center; border:1px dashed #c0a850; background:#fffdf0; color:#900; font-size:90%;&amp;quot;&amp;gt;No image found&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div style=&amp;quot;font-size:90%; line-height:1.25; margin-top:4px;&amp;quot;&amp;gt;[[&#039; .. page .. &#039;|&#039; .. escapeHtml(label) .. &#039;]]&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	return table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ============================================================&lt;br /&gt;
-- Public gallery function&lt;br /&gt;
-- ============================================================&lt;br /&gt;
&lt;br /&gt;
function p.gallery(frame)&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	local dataPage = args[1] or &#039;Physics:Quantum basics/See also&#039;&lt;br /&gt;
	local width = normalizeGalleryWidth(args.width or args[2])&lt;br /&gt;
	local content = getPageContent(dataPage)&lt;br /&gt;
&lt;br /&gt;
	local pages = extractPagesForGallery(content)&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
	local imageCount = 0&lt;br /&gt;
	local missingCount = 0&lt;br /&gt;
	local pageCount = #pages&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;div class=&amp;quot;noexcerpt&amp;quot; style=&amp;quot;max-width:100%; clear:both;&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	for _, page in ipairs(pages) do&lt;br /&gt;
		local file = firstFileInArticle(page)&lt;br /&gt;
&lt;br /&gt;
		if file then&lt;br /&gt;
			imageCount = imageCount + 1&lt;br /&gt;
		else&lt;br /&gt;
			missingCount = missingCount + 1&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		table.insert(out, renderGalleryCard(page, file, width))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert(out, &#039;&amp;lt;/div&amp;gt;&#039;)&lt;br /&gt;
&lt;br /&gt;
	local heading = &#039;== Gallery (&#039; .. imageCount .. &#039; images, &#039; .. missingCount .. &#039; missing, &#039; .. pageCount .. &#039; pages) ==&#039;&lt;br /&gt;
&lt;br /&gt;
	return heading .. &#039;\n&#039; .. table.concat(out, &#039;\n&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://scholarlywiki.org/index.php?title=Book:Quantum_Collection/Methods_and_tools/Gallery&amp;diff=2160</id>
		<title>Book:Quantum Collection/Methods and tools/Gallery</title>
		<link rel="alternate" type="text/html" href="https://scholarlywiki.org/index.php?title=Book:Quantum_Collection/Methods_and_tools/Gallery&amp;diff=2160"/>
		<updated>2026-05-13T17:57:11Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Create redirect to Methods Gallery&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Book:Quantum Collection/Methods Gallery]]&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
</feed>